master #475
| @@ -18,6 +18,10 @@ class UserWarningsRepositoryABC(ABC): | ||||
|     def get_user_warnings_by_id(self, id: int) -> UserWarnings: | ||||
|         pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     def get_user_warnings_by_server_id(self, server_id: int) -> List[UserWarnings]: | ||||
|         pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     def get_user_warnings_by_user_id(self, user_id: int) -> List[UserWarnings]: | ||||
|         pass | ||||
|   | ||||
| @@ -59,6 +59,17 @@ class UserWarnings(TableABC): | ||||
|             """ | ||||
|         ) | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_select_by_server_id_string(id: int) -> str: | ||||
|         return str( | ||||
|             f""" | ||||
|                 SELECT `UserWarnings`.* FROM `UserWarnings` | ||||
|                 INNER JOIN `Users` | ||||
|                 ON `Users`.`UserId` = `UserWarnings`.`UserId` | ||||
|                 WHERE `Users`.`ServerId` = {id}; | ||||
|             """ | ||||
|         ) | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_select_by_user_id_string(id: int) -> str: | ||||
|         return str( | ||||
|   | ||||
| @@ -56,6 +56,20 @@ class UserWarningsRepositoryService(UserWarningsRepositoryABC): | ||||
|         self._logger.trace(__name__, f"Send SQL command: {UserWarnings.get_select_by_id_string(id)}") | ||||
|         return self._from_result(self._context.select(UserWarnings.get_select_by_id_string(id))[0]) | ||||
|  | ||||
|     def get_user_warnings_by_server_id(self, server_id: int) -> List[UserWarnings]: | ||||
|         self._logger.trace( | ||||
|             __name__, | ||||
|             f"Send SQL command: {UserWarnings.get_select_by_server_id_string(server_id)}", | ||||
|         ) | ||||
|  | ||||
|         return List( | ||||
|             UserWarnings, | ||||
|             [ | ||||
|                 self._from_result(warning) | ||||
|                 for warning in self._context.select(UserWarnings.get_select_by_server_id_string(server_id)) | ||||
|             ], | ||||
|         ) | ||||
|  | ||||
|     def get_user_warnings_by_user_id(self, user_id: int) -> List[UserWarnings]: | ||||
|         self._logger.trace( | ||||
|             __name__, | ||||
|   | ||||
| @@ -29,6 +29,9 @@ type Server implements TableWithHistoryQuery { | ||||
|     activeUserCount: Int | ||||
|     users(filter: UserFilter, page: Page, sort: Sort): [User] | ||||
|  | ||||
|     userWarningCount: Int | ||||
|     userWarnings(filter: UserWarningFilter, page: Page, sort: Sort): [UserWarning] | ||||
|  | ||||
|     achievementCount: Int | ||||
|     achievements(filter: AchievementFilter, page: Page, sort: Sort): [Achievement] | ||||
|  | ||||
|   | ||||
| @@ -86,5 +86,9 @@ class UserMutation(QueryABC): | ||||
|                 continue | ||||
|  | ||||
|             member = self._bot.get_guild(user.server.discord_id).get_member(user.discord_id) | ||||
|             author = self._users.get_user_by_id(int(warning["author"])) | ||||
|             if "author" not in warning: | ||||
|                 author = Route.get_user().users.where(lambda u: u.server.id == user.server.id).single() | ||||
|             else: | ||||
|                 author = self._users.get_user_by_id(int(warning["author"])) | ||||
|  | ||||
|             self._user_warning_service.add_warnings(member, warning["description"], author.discord_id) | ||||
|   | ||||
| @@ -17,6 +17,7 @@ from bot_data.abc.user_joined_voice_channel_repository_abc import ( | ||||
|     UserJoinedVoiceChannelRepositoryABC, | ||||
| ) | ||||
| from bot_data.abc.user_repository_abc import UserRepositoryABC | ||||
| from bot_data.abc.user_warnings_repository_abc import UserWarningsRepositoryABC | ||||
| from bot_data.model.server import Server | ||||
| from bot_data.model.server_config import ServerConfig | ||||
| from bot_data.model.server_history import ServerHistory | ||||
| @@ -28,6 +29,7 @@ from bot_graphql.filter.level_filter import LevelFilter | ||||
| from bot_graphql.filter.scheduled_event_filter import ScheduledEventFilter | ||||
| from bot_graphql.filter.short_role_name_filter import ShortRoleNameFilter | ||||
| from bot_graphql.filter.user_filter import UserFilter | ||||
| from bot_graphql.filter.user_warning_filter import UserWarningFilter | ||||
| from bot_graphql.model.server_statistics import ServerStatistics | ||||
|  | ||||
|  | ||||
| @@ -48,6 +50,7 @@ class ServerQuery(DataQueryWithHistoryABC): | ||||
|         short_role_names: ShortRoleNameRepositoryABC, | ||||
|         scheduled_events: ScheduledEventRepositoryABC, | ||||
|         server_configs: ServerConfigRepositoryABC, | ||||
|         user_warnings: UserWarningsRepositoryABC, | ||||
|     ): | ||||
|         DataQueryWithHistoryABC.__init__(self, "Server", "ServersHistory", ServerHistory, db) | ||||
|  | ||||
| @@ -89,6 +92,11 @@ class ServerQuery(DataQueryWithHistoryABC): | ||||
|             lambda server, *_: self._users.get_users_with_activity_by_server_id(server.id), | ||||
|             UserFilter, | ||||
|         ) | ||||
|         self.add_collection( | ||||
|             "userWarning", | ||||
|             lambda server, *_: user_warnings.get_user_warnings_by_server_id(server.id), | ||||
|             UserWarningFilter, | ||||
|         ) | ||||
|         self.add_collection( | ||||
|             "gameServer", | ||||
|             lambda server, *_: game_servers.get_game_servers_by_server_id(server.id), | ||||
|   | ||||
| @@ -377,6 +377,22 @@ export class Queries { | ||||
|     } | ||||
|   `; | ||||
|  | ||||
|   static liteUsersQuery = ` | ||||
|     query UsersList($serverId: ID, $filter: UserFilter, $page: Page, $sort: Sort) { | ||||
|       servers(filter: {id: $serverId}) { | ||||
|         userCount | ||||
|         users(filter: $filter, page: $page, sort: $sort) { | ||||
|           id | ||||
|           discordId | ||||
|           name | ||||
|  | ||||
|           createdAt | ||||
|           modifiedAt | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   `; | ||||
|  | ||||
|   static userProfile = ` | ||||
|     query UserProfile($serverId: ID, $userId: ID, $page: Page, $sort: Sort) { | ||||
|       userCount | ||||
| @@ -503,6 +519,29 @@ export class Queries { | ||||
|     } | ||||
|   `; | ||||
|  | ||||
|   static userWarningsQuery = ` | ||||
|     query userWarnings($serverId: ID, $page: Page, $sort: Sort) { | ||||
|       servers(filter: {id: $serverId}) { | ||||
|         userWarningCount | ||||
|         userWarnings(page: $page, sort: $sort) { | ||||
|           id | ||||
|           user { | ||||
|             id | ||||
|             name | ||||
|           } | ||||
|           description | ||||
|           author { | ||||
|             id | ||||
|             name | ||||
|           } | ||||
|  | ||||
|           createdAt | ||||
|           modifiedAt | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   `; | ||||
|  | ||||
|   static autoRolesQuery = ` | ||||
|     query AutoRoleQuery($serverId: ID, $filter: AutoRoleFilter, $page: Page, $sort: Sort) { | ||||
|       servers(filter: {id: $serverId}) { | ||||
|   | ||||
| @@ -64,7 +64,9 @@ export class MembersComponent extends ComponentWithTable implements OnInit, OnDe | ||||
|     level: FormControl<Level | null> | ||||
|   }>; | ||||
|  | ||||
|   filter: UserFilter = {}; | ||||
|   filter: UserFilter = { | ||||
|     leftServer: false | ||||
|   }; | ||||
|   page: Page = { | ||||
|     pageSize: undefined, | ||||
|     pageIndex: undefined | ||||
| @@ -145,7 +147,7 @@ export class MembersComponent extends ComponentWithTable implements OnInit, OnDe | ||||
|       id: new FormControl<number | null>(null), | ||||
|       discordId: new FormControl<number | null>(null), | ||||
|       name: [""], | ||||
|       leftServer: new FormControl<boolean | null>(null), | ||||
|       leftServer: new FormControl<boolean | null>(false), | ||||
|       level: new FormControl<Level | null>(null) | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -8,19 +8,55 @@ import { MemberRoles } from "../../../models/auth/auth-user.dto"; | ||||
|  | ||||
| const routes: Routes = [ | ||||
|   { path: "", component: ServerDashboardComponent }, | ||||
|   { path: "members", component: MembersComponent, canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, | ||||
|   { | ||||
|     path: "members", | ||||
|     component: MembersComponent, | ||||
|     canActivate: [AuthGuard], | ||||
|     data: { memberRole: MemberRoles.Moderator } | ||||
|   }, | ||||
|   { path: "members/:memberId", component: ProfileComponent }, | ||||
|   { | ||||
|     path: "user-warnings", | ||||
|     loadChildren: () => import("./user-warning/user-warning.module").then(m => m.UserWarningModule), | ||||
|     canActivate: [AuthGuard], | ||||
|     data: { memberRole: MemberRoles.Moderator } | ||||
|   }, | ||||
|   { | ||||
|     path: "auto-roles", | ||||
|     loadChildren: () => import("./auto-role/auto-role.module").then(m => m.AutoRoleModule), | ||||
|     canActivate: [AuthGuard], | ||||
|     data: { memberRole: MemberRoles.Moderator } | ||||
|   }, | ||||
|   { path: "levels", loadChildren: () => import("./levels/levels.module").then(m => m.LevelsModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, | ||||
|   { path: "achievements", loadChildren: () => import("./achievements/achievements.module").then(m => m.AchievementsModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, | ||||
|   { path: "short-role-names", loadChildren: () => import("./short-role-name/short-role-name.module").then(m => m.ShortRoleNameModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, | ||||
|   { path: "scheduled-events", loadChildren: () => import("./scheduled-events/scheduled-events.module").then(m => m.ScheduledEventsModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, | ||||
|   { path: "config", loadChildren: () => import("./config/config.module").then(m => m.ConfigModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Admin } } | ||||
|   { | ||||
|     path: "levels", | ||||
|     loadChildren: () => import("./levels/levels.module").then(m => m.LevelsModule), | ||||
|     canActivate: [AuthGuard], | ||||
|     data: { memberRole: MemberRoles.Moderator } | ||||
|   }, | ||||
|   { | ||||
|     path: "achievements", | ||||
|     loadChildren: () => import("./achievements/achievements.module").then(m => m.AchievementsModule), | ||||
|     canActivate: [AuthGuard], | ||||
|     data: { memberRole: MemberRoles.Moderator } | ||||
|   }, | ||||
|   { | ||||
|     path: "short-role-names", | ||||
|     loadChildren: () => import("./short-role-name/short-role-name.module").then(m => m.ShortRoleNameModule), | ||||
|     canActivate: [AuthGuard], | ||||
|     data: { memberRole: MemberRoles.Moderator } | ||||
|   }, | ||||
|   { | ||||
|     path: "scheduled-events", | ||||
|     loadChildren: () => import("./scheduled-events/scheduled-events.module").then(m => m.ScheduledEventsModule), | ||||
|     canActivate: [AuthGuard], | ||||
|     data: { memberRole: MemberRoles.Moderator } | ||||
|   }, | ||||
|   { | ||||
|     path: "config", | ||||
|     loadChildren: () => import("./config/config.module").then(m => m.ConfigModule), | ||||
|     canActivate: [AuthGuard], | ||||
|     data: { memberRole: MemberRoles.Admin } | ||||
|   } | ||||
| ]; | ||||
|  | ||||
| @NgModule({ | ||||
|   | ||||
| @@ -0,0 +1,213 @@ | ||||
| <h1> | ||||
|   {{'common.user_warnings' | translate}} | ||||
| </h1> | ||||
| <div class="content-wrapper"> | ||||
|   <div class="content"> | ||||
|     <p-table #dt [value]="userWarnings" [responsive]="true" responsiveLayout="stack" [breakpoint]="'720px'" | ||||
|              dataKey="id" editMode="row" [rowHover]="true" [rows]="10" | ||||
|              [rowsPerPageOptions]="[10,25,50]" [paginator]="true" [loading]="loading" [totalRecords]="totalRecords" | ||||
|              [lazy]="true" (onLazyLoad)="nextPage($event)"> | ||||
|  | ||||
|       <ng-template pTemplate="caption"> | ||||
|         <div class="table-caption"> | ||||
|           <div class="table-caption-table-info"> | ||||
|             <div class="table-caption-text"> | ||||
|               <ng-container *ngIf="!loading">{{userWarnings.length}} {{'common.of' | translate}} | ||||
|                 {{dt.totalRecords}} | ||||
|               </ng-container> | ||||
|               {{'common.user_warnings' | translate}} | ||||
|             </div> | ||||
|  | ||||
|             <app-multi-select-columns [table]="name" [columns]="columns" | ||||
|                                       [(hiddenColumns)]="hiddenColumns"></app-multi-select-columns> | ||||
|           </div> | ||||
|  | ||||
|           <div class="table-caption-btn-wrapper btn-wrapper"> | ||||
|             <button pButton label="{{'common.add' | translate}}" class="icon-btn btn" | ||||
|                     icon="pi pi-plus" (click)="addUserWarning(dt)" | ||||
|                     [disabled]="isEditingNew || !user?.isModerator && !user?.isAdmin"> | ||||
|             </button> | ||||
|             <button pButton label="{{'common.reset_filters' | translate}}" icon="pi pi-undo" | ||||
|                     class="icon-btn btn" (click)="resetFilters()"> | ||||
|             </button> | ||||
|             <app-data-import-and-export name="userWarnings" [(data)]="userWarnings" | ||||
|                                         [callback]="callback" [validator]="validator"></app-data-import-and-export> | ||||
|           </div> | ||||
|         </div> | ||||
|       </ng-template> | ||||
|  | ||||
|       <ng-template pTemplate="header"> | ||||
|         <tr> | ||||
|           <th hideable-th="id" [parent]="this" [sortable]="true"> | ||||
|             <div class="table-header-label"> | ||||
|               <div class="table-header-text">{{'common.id' | translate}}</div> | ||||
|               <p-sortIcon field="id" class="table-header-icon"></p-sortIcon> | ||||
|             </div> | ||||
|           </th> | ||||
|  | ||||
|           <th hideable-th="description" [parent]="this" [sortable]="true"> | ||||
|             <div class="table-header-label"> | ||||
|               <div class="table-header-text">{{'common.description' | translate}}</div> | ||||
|               <p-sortIcon field="name" class="table-header-icon"></p-sortIcon> | ||||
|             </div> | ||||
|           </th> | ||||
|  | ||||
|           <th hideable-th="user" [parent]="this" [sortable]="true"> | ||||
|             <div class="table-header-label"> | ||||
|               <div class="table-header-text">{{'common.member' | translate}}</div> | ||||
|               <p-sortIcon field="attribute" class="table-header-icon"></p-sortIcon> | ||||
|             </div> | ||||
|           </th> | ||||
|  | ||||
|           <th hideable-th="author" [parent]="this" [sortable]="true"> | ||||
|             <div class="table-header-label"> | ||||
|               <div class="table-header-text">{{'common.author' | translate}}</div> | ||||
|               <p-sortIcon field="operator" class="table-header-icon"></p-sortIcon> | ||||
|             </div> | ||||
|           </th> | ||||
|  | ||||
|           <th> | ||||
|             <div class="table-header-label"> | ||||
|               <div class="table-header-text">{{'common.created_at' | translate}}</div> | ||||
|             </div> | ||||
|           </th> | ||||
|  | ||||
|           <th> | ||||
|             <div class="table-header-label"> | ||||
|               <div class="table-header-text">{{'common.modified_at' | translate}}</div> | ||||
|             </div> | ||||
|           </th> | ||||
|  | ||||
|           <th> | ||||
|             <div class="table-header-label"> | ||||
|               <div class="table-header-text">{{'common.actions' | translate}}</div> | ||||
|             </div> | ||||
|           </th> | ||||
|         </tr> | ||||
|  | ||||
|         <tr> | ||||
|           <th hideable-th="id" [parent]="this" class="table-header-small"> | ||||
|             <form [formGroup]="filterForm"> | ||||
|               <input type="text" pInputText formControlName="id" | ||||
|                      placeholder="{{'common.id' | translate}}"> | ||||
|             </form> | ||||
|           </th> | ||||
|           <th hideable-th="description" [parent]="this"> | ||||
|             <form [formGroup]="filterForm"> | ||||
|               <input type="text" pInputText formControlName="description" | ||||
|                      placeholder="{{'common.description' | translate}}"> | ||||
|             </form> | ||||
|           </th> | ||||
|           <th hideable-th="user" [parent]="this"> | ||||
|             <form [formGroup]="filterForm"> | ||||
|               <input type="text" pInputText formControlName="user" | ||||
|                      placeholder="{{'common.member' | translate}}"> | ||||
|             </form> | ||||
|           </th> | ||||
|           <th hideable-th="author" [parent]="this"> | ||||
|             <form [formGroup]="filterForm"> | ||||
|               <input type="text" pInputText formControlName="author" | ||||
|                      placeholder="{{'common.author' | translate}}"> | ||||
|             </form> | ||||
|           </th> | ||||
|           <th class="table-header-small-dropdown"></th> | ||||
|           <th class="table-header-small-dropdown"></th> | ||||
|           <th class="table-header-actions"></th> | ||||
|         </tr> | ||||
|       </ng-template> | ||||
|  | ||||
|       <ng-template pTemplate="body" let-userWarning let-editing="editing" let-ri="rowIndex"> | ||||
|         <tr [pEditableRow]="userWarning"> | ||||
|           <td hideable-td="id" [parent]="this"> | ||||
|             <span class="p-column-title">{{'common.id' | translate}}:</span> | ||||
|             <p-cellEditor> | ||||
|               <ng-template pTemplate="input"> | ||||
|                 {{userWarning.id}} | ||||
|               </ng-template> | ||||
|               <ng-template pTemplate="output"> | ||||
|                 {{userWarning.id}} | ||||
|               </ng-template> | ||||
|             </p-cellEditor> | ||||
|           </td> | ||||
|  | ||||
|           <td hideable-td="description" [parent]="this"> | ||||
|             <span class="p-column-title">{{'common.description' | translate}}:</span> | ||||
|             <p-cellEditor> | ||||
|               <ng-template pTemplate="input"> | ||||
|                 <input *ngIf="isEditingNew; else description" class="table-edit-input" pInputText type="text" | ||||
|                        [(ngModel)]="userWarning.description"> | ||||
|                 <ng-template #description>{{userWarning.description}}</ng-template> | ||||
|               </ng-template> | ||||
|               <ng-template pTemplate="output"> | ||||
|                 {{userWarning.description}} | ||||
|               </ng-template> | ||||
|             </p-cellEditor> | ||||
|           </td> | ||||
|  | ||||
|           <td hideable-td="user" [parent]="this"> | ||||
|             <span class="p-column-title">{{'common.user' | translate}}:</span> | ||||
|             <p-cellEditor> | ||||
|               <ng-template pTemplate="input"> | ||||
|                 <p-dropdown *ngIf="isEditingNew; else userName" optionLabel="name" [options]="users" | ||||
|                             [(ngModel)]="userWarning.user" | ||||
|                             placeholder="{{'common.member' | translate}}"></p-dropdown> | ||||
|                 <ng-template #userName>{{userWarning.user.name}}</ng-template> | ||||
|               </ng-template> | ||||
|               <ng-template pTemplate="output"> | ||||
|                 {{userWarning.user.name}} | ||||
|               </ng-template> | ||||
|             </p-cellEditor> | ||||
|           </td> | ||||
|  | ||||
|           <td hideable-td="author" [parent]="this"> | ||||
|             <span class="p-column-title">{{'common.author' | translate}}:</span> | ||||
|             <p-cellEditor> | ||||
|               <ng-template pTemplate="input"> | ||||
|                 {{'common.you' | translate}} | ||||
|               </ng-template> | ||||
|               <ng-template pTemplate="output"> | ||||
|                 {{userWarning.author.name}} | ||||
|               </ng-template> | ||||
|             </p-cellEditor> | ||||
|           </td> | ||||
|  | ||||
|           <td> | ||||
|             <span class="p-column-title">{{'common.created_at' | translate}}:</span> | ||||
|             {{userWarning.createdAt | date:'dd.MM.yy HH:mm'}} | ||||
|           </td> | ||||
|  | ||||
|           <td> | ||||
|             <span class="p-column-title">{{'common.modified_at' | translate}}:</span> | ||||
|             {{userWarning.modifiedAt | date:'dd.MM.yy HH:mm'}} | ||||
|           </td> | ||||
|           <td> | ||||
|             <div class="btn-wrapper"> | ||||
|               <button *ngIf="!editing" pButton class="btn icon-btn danger-icon-btn" icon="pi pi-trash" | ||||
|                       (click)="deleteUserWarning(userWarning)" | ||||
|                       [disabled]="!user || !user.isModerator && !user.isAdmin"></button> | ||||
|  | ||||
|               <button *ngIf="editing" pButton pSaveEditableRow class="btn icon-btn" | ||||
|                       icon="pi pi-check-circle" (click)="onRowEditSave(userWarning, ri)" | ||||
|                       [disabled]="!user || !user.isModerator && !user.isAdmin"></button> | ||||
|               <button *ngIf="editing" pButton pCancelEditableRow class="btn icon-btn danger-icon-btn" | ||||
|                       icon="pi pi-times-circle" (click)="onRowEditCancel(ri)" | ||||
|                       [disabled]="!user || !user.isModerator && !user.isAdmin"></button> | ||||
|             </div> | ||||
|           </td> | ||||
|         </tr> | ||||
|       </ng-template> | ||||
|  | ||||
|       <ng-template pTemplate="emptymessage"> | ||||
|         <tr></tr> | ||||
|         <tr> | ||||
|           <td colspan="10">{{'common.no_entries_found' | translate}}</td> | ||||
|         </tr> | ||||
|         <tr></tr> | ||||
|       </ng-template> | ||||
|  | ||||
|       <ng-template pTemplate="paginatorleft"> | ||||
|       </ng-template> | ||||
|     </p-table> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| @@ -0,0 +1,23 @@ | ||||
| import { ComponentFixture, TestBed } from "@angular/core/testing"; | ||||
|  | ||||
| import { UserWarningComponent } from "./user-warning.component"; | ||||
|  | ||||
| describe("AchievementComponent", () => { | ||||
|   let component: UserWarningComponent; | ||||
|   let fixture: ComponentFixture<UserWarningComponent>; | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     await TestBed.configureTestingModule({ | ||||
|       declarations: [UserWarningComponent] | ||||
|     }) | ||||
|       .compileComponents(); | ||||
|  | ||||
|     fixture = TestBed.createComponent(UserWarningComponent); | ||||
|     component = fixture.componentInstance; | ||||
|     fixture.detectChanges(); | ||||
|   }); | ||||
|  | ||||
|   it("should create", () => { | ||||
|     expect(component).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
| @@ -0,0 +1,240 @@ | ||||
| import { Component, OnDestroy, OnInit } from "@angular/core"; | ||||
| import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; | ||||
| import { Page } from "../../../../../../models/graphql/filter/page.model"; | ||||
| import { Sort, SortDirection } from "../../../../../../models/graphql/filter/sort.model"; | ||||
| import { Subject, throwError } from "rxjs"; | ||||
| import { Server } from "../../../../../../models/data/server.model"; | ||||
| import { UserDTO } from "../../../../../../models/auth/auth-user.dto"; | ||||
| import { Queries } from "../../../../../../models/graphql/queries.model"; | ||||
| import { AuthService } from "../../../../../../services/auth/auth.service"; | ||||
| import { SpinnerService } from "../../../../../../services/spinner/spinner.service"; | ||||
| import { ToastService } from "../../../../../../services/toast/toast.service"; | ||||
| import { ConfirmationDialogService } from "../../../../../../services/confirmation-dialog/confirmation-dialog.service"; | ||||
| import { TranslateService } from "@ngx-translate/core"; | ||||
| import { DataService } from "../../../../../../services/data/data.service"; | ||||
| import { SidebarService } from "../../../../../../services/sidebar/sidebar.service"; | ||||
| import { ActivatedRoute } from "@angular/router"; | ||||
| import { Query, UserListQuery, UserWarningQuery } from "../../../../../../models/graphql/query.model"; | ||||
| import { catchError, debounceTime, takeUntil } from "rxjs/operators"; | ||||
| import { LazyLoadEvent } from "primeng/api"; | ||||
| import { Table } from "primeng/table"; | ||||
| import { User } from "../../../../../../models/data/user.model"; | ||||
| import { UpdateUserMutationResult } from "../../../../../../models/graphql/result.model"; | ||||
| import { Mutations } from "../../../../../../models/graphql/mutations.model"; | ||||
| import { ComponentWithTable } from "../../../../../../base/component-with-table"; | ||||
| import { UserWarning, UserWarningFilter } from "../../../../../../models/data/user_warning.model"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: "app-user-warning", | ||||
|   templateUrl: "./user-warning.component.html", | ||||
|   styleUrls: ["./user-warning.component.scss"] | ||||
| }) | ||||
| export class UserWarningComponent extends ComponentWithTable implements OnInit, OnDestroy { | ||||
|   public userWarnings: UserWarning[] = []; | ||||
|   public users: User[] = []; | ||||
|   public loading = true; | ||||
|  | ||||
|   public filterForm!: FormGroup<{ | ||||
|     id: FormControl<number | null>, | ||||
|     description: FormControl<string | null>, | ||||
|     user: FormControl<string | null>, | ||||
|     author: FormControl<string | null>, | ||||
|   }>; | ||||
|  | ||||
|   public filter: UserWarningFilter = {}; | ||||
|   public page: Page = { | ||||
|     pageSize: undefined, | ||||
|     pageIndex: undefined | ||||
|   }; | ||||
|   public sort: Sort = { | ||||
|     sortColumn: undefined, | ||||
|     sortDirection: undefined | ||||
|   }; | ||||
|  | ||||
|   public totalRecords: number = 0; | ||||
|  | ||||
|   public clonedUserWarning: { [s: string]: UserWarning; } = {}; | ||||
|  | ||||
|   private unsubscriber = new Subject<void>(); | ||||
|   private server: Server = {}; | ||||
|   public user: UserDTO | null = null; | ||||
|  | ||||
|   query: string = Queries.userWarningsQuery; | ||||
|  | ||||
|   public constructor( | ||||
|     private authService: AuthService, | ||||
|     private spinner: SpinnerService, | ||||
|     private toastService: ToastService, | ||||
|     private confirmDialog: ConfirmationDialogService, | ||||
|     private fb: FormBuilder, | ||||
|     private translate: TranslateService, | ||||
|     private data: DataService, | ||||
|     private sidebar: SidebarService, | ||||
|     private route: ActivatedRoute) { | ||||
|     super("UserWarning", ["id", "name"], | ||||
|       (oldElement: UserWarning, newElement: UserWarning) => { | ||||
|         return oldElement.id === newElement.id; | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   public ngOnInit(): void { | ||||
|     this.loading = true; | ||||
|     this.setFilterForm(); | ||||
|     this.data.getServerFromRoute(this.route).then(async server => { | ||||
|       this.server = server; | ||||
|       let authUser = await this.authService.getLoggedInUser(); | ||||
|       this.user = authUser?.users?.find(u => u.server == this.server.id) ?? null; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public ngOnDestroy(): void { | ||||
|     this.unsubscriber.next(); | ||||
|     this.unsubscriber.complete(); | ||||
|   } | ||||
|  | ||||
|   public loadNextPage(): void { | ||||
|     this.data.query<UserWarningQuery>(Queries.userWarningsQuery, { | ||||
|         serverId: this.server.id, filter: this.filter, page: this.page, sort: this.sort | ||||
|       }, | ||||
|       (data: Query) => { | ||||
|         return data.servers[0]; | ||||
|       } | ||||
|     ).subscribe(data => { | ||||
|       this.totalRecords = data.userWarningCount; | ||||
|       this.userWarnings = data.userWarnings; | ||||
|  | ||||
|       this.data.query<UserListQuery>(Queries.liteUsersQuery, { | ||||
|           serverId: this.server.id, filter: { | ||||
|             leftServer: false | ||||
|           } | ||||
|         }, | ||||
|         (x: { servers: Server[] }) => { | ||||
|           return x.servers[0]; | ||||
|         } | ||||
|       ).subscribe(data => { | ||||
|         this.users = data.users; | ||||
|  | ||||
|         this.spinner.hideSpinner(); | ||||
|         this.loading = false; | ||||
|       }); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public setFilterForm(): void { | ||||
|     this.filterForm = this.fb.group({ | ||||
|       id: new FormControl<number | null>(null), | ||||
|       description: new FormControl<string | null>(null), | ||||
|       user: new FormControl<string | null>(null), | ||||
|       author: new FormControl<string | null>(null) | ||||
|     }); | ||||
|  | ||||
|     this.filterForm.valueChanges.pipe( | ||||
|       takeUntil(this.unsubscriber), | ||||
|       debounceTime(600) | ||||
|     ).subscribe(changes => { | ||||
|       if (changes.id) { | ||||
|         this.filter.id = changes.id; | ||||
|       } else { | ||||
|         this.filter.id = undefined; | ||||
|       } | ||||
|  | ||||
|       if (this.page.pageSize) | ||||
|         this.page.pageSize = 10; | ||||
|  | ||||
|       if (this.page.pageIndex) | ||||
|         this.page.pageIndex = 0; | ||||
|  | ||||
|       this.loadNextPage(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public newUserWarningTemplate: UserWarning = { | ||||
|     createdAt: "", | ||||
|     modifiedAt: "" | ||||
|   }; | ||||
|  | ||||
|   public nextPage(event: LazyLoadEvent): void { | ||||
|     this.page.pageSize = event.rows ?? 0; | ||||
|     if (event.first != null && event.rows != null) | ||||
|       this.page.pageIndex = event.first / event.rows; | ||||
|     this.sort.sortColumn = event.sortField ?? undefined; | ||||
|     this.sort.sortDirection = event.sortOrder === 1 ? SortDirection.ASC : event.sortOrder === -1 ? SortDirection.DESC : SortDirection.ASC; | ||||
|  | ||||
|     this.loadNextPage(); | ||||
|   } | ||||
|  | ||||
|   public resetFilters(): void { | ||||
|     this.filterForm.reset(); | ||||
|   } | ||||
|  | ||||
|   public onRowEditInit(table: Table, user: User, index: number): void { | ||||
|     this.clonedUserWarning[index] = { ...user }; | ||||
|   } | ||||
|  | ||||
|   public override onRowEditSave(newUserWarning: UserWarning, index: number): void { | ||||
|     if (this.isEditingNew && JSON.stringify(newUserWarning) === JSON.stringify(this.newUserWarningTemplate)) { | ||||
|       this.isEditingNew = false; | ||||
|       this.userWarnings.splice(index, 1); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (!newUserWarning.id && !this.isEditingNew) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     this.updateUserByWarning(newUserWarning.user?.id ?? 0, this.userWarnings.filter(uw => uw.user?.id == newUserWarning.user?.id)); | ||||
|   } | ||||
|  | ||||
|   public onRowEditCancel(index: number): void { | ||||
|     if (this.isEditingNew) { | ||||
|       this.userWarnings.splice(index, 1); | ||||
|       delete this.clonedUserWarning[index]; | ||||
|       this.isEditingNew = false; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     this.userWarnings[index] = this.clonedUserWarning[index]; | ||||
|     delete this.clonedUserWarning[index]; | ||||
|   } | ||||
|  | ||||
|   public addUserWarning(table: Table): void { | ||||
|     const newUserWarning = JSON.parse(JSON.stringify(this.newUserWarningTemplate)); | ||||
|  | ||||
|     this.userWarnings = [newUserWarning, ...this.userWarnings]; | ||||
|  | ||||
|     table.initRowEdit(newUserWarning); | ||||
|  | ||||
|     const index = this.userWarnings.findIndex(l => l.id == newUserWarning.id); | ||||
|     this.onRowEditInit(table, newUserWarning, index); | ||||
|  | ||||
|     this.isEditingNew = true; | ||||
|   } | ||||
|  | ||||
|   public deleteUserWarning(userWarning: UserWarning): void { | ||||
|     this.userWarnings.splice(this.userWarnings.findIndex(uw => uw.id == userWarning.id), 1); | ||||
|     this.updateUserByWarning(userWarning.user?.id ?? 0, this.userWarnings.filter(uw => uw.user?.id == userWarning.user?.id)); | ||||
|   } | ||||
|  | ||||
|   public updateUserByWarning(userId: number, userWarnings: UserWarning[]) { | ||||
|     this.spinner.showSpinner(); | ||||
|     this.data.mutation<UpdateUserMutationResult>(Mutations.updateUser, { | ||||
|         id: userId, | ||||
|         userWarnings: userWarnings?.map(userWarning => { | ||||
|           return { | ||||
|             id: userWarning.id, | ||||
|             user: userWarning.user?.id ?? undefined, | ||||
|             description: userWarning.description, | ||||
|             author: userWarning.author?.id ?? undefined | ||||
|           }; | ||||
|         }) | ||||
|       } | ||||
|     ).pipe(catchError(err => { | ||||
|       this.spinner.hideSpinner(); | ||||
|       return throwError(err); | ||||
|     })).subscribe(_ => { | ||||
|       this.spinner.hideSpinner(); | ||||
|       this.toastService.success(this.translate.instant("view.server.user_warning.message.user_warning_updated"), this.translate.instant("view.server.user_warning.message.user_warning_updated")); | ||||
|       this.loadNextPage(); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,16 @@ | ||||
| import { NgModule } from "@angular/core"; | ||||
| import { RouterModule, Routes } from "@angular/router"; | ||||
| import { UserWarningComponent } from "./components/user-warning/user-warning.component"; | ||||
|  | ||||
| const routes: Routes = [ | ||||
|  | ||||
|   { path: "", component: UserWarningComponent } | ||||
| ]; | ||||
|  | ||||
| @NgModule({ | ||||
|   imports: [RouterModule.forChild(routes)], | ||||
|   exports: [RouterModule] | ||||
| }) | ||||
| export class UserWarningRoutingModule { | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,19 @@ | ||||
| import { NgModule } from "@angular/core"; | ||||
| import { CommonModule } from "@angular/common"; | ||||
| import { UserWarningRoutingModule } from "./user-warning-routing.module"; | ||||
| import { SharedModule } from "../../../shared/shared.module"; | ||||
| import { UserWarningComponent } from "./components/user-warning/user-warning.component"; | ||||
|  | ||||
|  | ||||
| @NgModule({ | ||||
|   declarations: [ | ||||
|     UserWarningComponent | ||||
|   ], | ||||
|   imports: [ | ||||
|     CommonModule, | ||||
|     UserWarningRoutingModule, | ||||
|     SharedModule | ||||
|   ] | ||||
| }) | ||||
| export class UserWarningModule { | ||||
| } | ||||
| @@ -27,6 +27,7 @@ export class SidebarService { | ||||
|   serverDashboard: MenuItem = {}; | ||||
|   serverProfile: MenuItem = {}; | ||||
|   serverMembers: MenuItem = {}; | ||||
|   serverUserWarnings: MenuItem = {}; | ||||
|   serverAutoRoles: MenuItem = {}; | ||||
|   serverLevels: MenuItem = {}; | ||||
|   serverAchievements: MenuItem = {}; | ||||
| @@ -89,6 +90,12 @@ export class SidebarService { | ||||
|       visible: true, | ||||
|       routerLink: `server/${this.server?.id}/members` | ||||
|     }; | ||||
|     this.serverUserWarnings = { | ||||
|       label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.user_warnings") : "", | ||||
|       icon: "pi pi-exclamation-triangle", | ||||
|       visible: true, | ||||
|       routerLink: `server/${this.server?.id}/user-warnings` | ||||
|     }; | ||||
|  | ||||
|     this.serverAutoRoles = { | ||||
|       label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.auto_roles") : "", | ||||
| @@ -137,7 +144,7 @@ export class SidebarService { | ||||
|       icon: "pi pi-server", | ||||
|       visible: false, | ||||
|       expanded: true, | ||||
|       items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles, this.serverLevels, this.serverAchievements, this.serverScheduledEvents, this.serverShortRoleNames, this.serverConfig] | ||||
|       items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverUserWarnings, this.serverAutoRoles, this.serverLevels, this.serverAchievements, this.serverScheduledEvents, this.serverShortRoleNames, this.serverConfig] | ||||
|     }; | ||||
|     this.adminConfig = { | ||||
|       label: this.isSidebarOpen ? this.translateService.instant("sidebar.config") : "", | ||||
| @@ -194,6 +201,7 @@ export class SidebarService { | ||||
|       if (this.server) { | ||||
|         this.serverMenu.visible = true; | ||||
|         this.serverMembers.visible = isTechnicianAndFullAccessActive || user?.isModerator; | ||||
|         this.serverUserWarnings.visible = isTechnicianAndFullAccessActive || user?.isModerator; | ||||
|         this.serverAutoRoles.visible = isTechnicianAndFullAccessActive || this.hasFeature("AutoRoleModule") && user?.isModerator; | ||||
|         this.serverLevels.visible = isTechnicianAndFullAccessActive || this.hasFeature("LevelModule") && user?.isModerator; | ||||
|         this.serverAchievements.visible = isTechnicianAndFullAccessActive || this.hasFeature("AchievementsModule") && user?.isModerator; | ||||
|   | ||||
| @@ -190,6 +190,7 @@ | ||||
|     "left_server": "Aktiv", | ||||
|     "level": "Level", | ||||
|     "location": "Ort", | ||||
|     "member": "Mitglied", | ||||
|     "message_id": "Nachricht Id", | ||||
|     "min_xp": "Min. XP", | ||||
|     "modified_at": "Bearbeitet am", | ||||
| @@ -218,7 +219,8 @@ | ||||
|     "user_warnings": "Verwarnungen", | ||||
|     "users": "Benutzer", | ||||
|     "value": "Wert", | ||||
|     "xp": "XP" | ||||
|     "xp": "XP", | ||||
|     "you": "Du" | ||||
|   }, | ||||
|   "dialog": { | ||||
|     "abort": "Abbrechen", | ||||
| @@ -348,7 +350,8 @@ | ||||
|       "members": "Mitglieder", | ||||
|       "profile": "Dein Profil", | ||||
|       "scheduled_events": "Geplante Events", | ||||
|       "short_role_names": "Rollen Kürzel" | ||||
|       "short_role_names": "Rollen Kürzel", | ||||
|       "user_warnings": "Verwarnungen" | ||||
|     }, | ||||
|     "server_empty": "Kein Server ausgewählt", | ||||
|     "settings": "Einstellungen", | ||||
| @@ -622,6 +625,11 @@ | ||||
|           "short_role_names_update_failed_d": "Die Bearbeitung des Levels ist fehlgeschlagen!" | ||||
|         }, | ||||
|         "short_role_names": "Rollen Kürzel" | ||||
|       }, | ||||
|       "user_warning": { | ||||
|         "message": { | ||||
|           "user_warning_updated": "Verwarnungen wurde bearbeitet" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "user_settings": { | ||||
|   | ||||
| @@ -190,6 +190,7 @@ | ||||
|     "left_server": "Active", | ||||
|     "level": "Level", | ||||
|     "location": "Location", | ||||
|     "member": "Member", | ||||
|     "message_id": "Message Id", | ||||
|     "min_xp": "Min. XP", | ||||
|     "modified_at": "Modified at", | ||||
| @@ -218,7 +219,8 @@ | ||||
|     "user_warnings": "User warnings", | ||||
|     "users": "User", | ||||
|     "value": "Value", | ||||
|     "xp": "XP" | ||||
|     "xp": "XP", | ||||
|     "you": "You" | ||||
|   }, | ||||
|   "dialog": { | ||||
|     "abort": "Abort", | ||||
| @@ -348,7 +350,8 @@ | ||||
|       "members": "Members", | ||||
|       "profile": "Your profile", | ||||
|       "scheduled_events": "Scheduled events", | ||||
|       "short_role_names": "Short role names" | ||||
|       "short_role_names": "Short role names", | ||||
|       "user_warnings": "User warnings" | ||||
|     }, | ||||
|     "server_empty": "No server selected", | ||||
|     "settings": "Settings", | ||||
| @@ -622,6 +625,11 @@ | ||||
|           "short_role_names_update_failed_d": "Short role name editing failed!" | ||||
|         }, | ||||
|         "short_role_names": "Level" | ||||
|       }, | ||||
|       "user_warning": { | ||||
|         "message": { | ||||
|           "user_warning_updated": "Updated user warnings" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "user_settings": { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user