staging into master #426
| @@ -41,6 +41,9 @@ class LevelMutation(QueryABC): | ||||
|             int(input["permissions"]), | ||||
|             server, | ||||
|         ) | ||||
|         levels = self._levels.get_levels_by_server_id(server.id) | ||||
|         if levels.where(lambda x: x.name == level.name).count() > 0: | ||||
|             raise ValueError(f"Level with name {level.name} already exists") | ||||
|         self._levels.add_level(level) | ||||
|         self._db.save_changes() | ||||
|  | ||||
|   | ||||
| @@ -1,58 +1,65 @@ | ||||
| import { Level } from "../models/data/level.model"; | ||||
|  | ||||
| export interface Column { | ||||
|     key: string; | ||||
|     name: string; | ||||
|   key: string; | ||||
|   name: string; | ||||
| } | ||||
|  | ||||
| export class ComponentWithTable { | ||||
|     private _hiddenColumns: Column[] = []; | ||||
|     set hiddenColumns(value: Column[]) { | ||||
|         this._hiddenColumns = value; | ||||
|         localStorage.setItem("hiddenColumns", JSON.stringify(value)); | ||||
|   private _hiddenColumns: Column[] = []; | ||||
|   set hiddenColumns(value: Column[]) { | ||||
|     this._hiddenColumns = value; | ||||
|     localStorage.setItem("hiddenColumns", JSON.stringify(value)); | ||||
|   } | ||||
|  | ||||
|   get hiddenColumns(): Column[] { | ||||
|     return this._hiddenColumns; | ||||
|   } | ||||
|  | ||||
|   public name: string = ""; | ||||
|   public columns: Column[] = []; | ||||
|   public isEditingNew: boolean = false; | ||||
|  | ||||
|   public callback = (data: any[], isNew: boolean) => { | ||||
|     this.save(data, isNew); | ||||
|   }; | ||||
|   public validator: (oldElement: any, newElement: any) => boolean = (oldElement: any, newElement: any) => { | ||||
|     return true; | ||||
|   }; | ||||
|  | ||||
|   constructor( | ||||
|     name: string, | ||||
|     columns: string[], | ||||
|     validator?: (oldElement: any, newElement: any) => boolean | ||||
|   ) { | ||||
|     this.name = name; | ||||
|     this.columns = columns.map(column => { | ||||
|       return { key: this.getKey(column), name: column }; | ||||
|     }); | ||||
|     let hiddenColumns = localStorage.getItem("hiddenColumns"); | ||||
|     if (!hiddenColumns) { | ||||
|       localStorage.setItem("hiddenColumns", JSON.stringify([{}])); | ||||
|       hiddenColumns = localStorage.getItem("hiddenColumns") ?? JSON.stringify([{}]); | ||||
|     } | ||||
|  | ||||
|     get hiddenColumns(): Column[] { | ||||
|         return this._hiddenColumns; | ||||
|     this._hiddenColumns = JSON.parse(hiddenColumns); | ||||
|     if (validator) { | ||||
|       this.validator = validator; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|     public name: string = ""; | ||||
|     public columns: Column[] = []; | ||||
|   private getKey(column: string): string { | ||||
|     return `${this.name}_${column}`; | ||||
|   } | ||||
|  | ||||
|     constructor( | ||||
|         name: string, | ||||
|         columns: string[] | ||||
|     ) { | ||||
|         this.name = name; | ||||
|         this.columns = columns.map(column => { | ||||
|             return { key: this.getKey(column), name: column }; | ||||
|         }); | ||||
|         let hiddenColumns = localStorage.getItem("hiddenColumns"); | ||||
|         if (!hiddenColumns) { | ||||
|             localStorage.setItem("hiddenColumns", JSON.stringify([{}])); | ||||
|             hiddenColumns = localStorage.getItem("hiddenColumns") ?? JSON.stringify([{}]); | ||||
|         } | ||||
|         this._hiddenColumns = JSON.parse(hiddenColumns); | ||||
|     } | ||||
|  | ||||
|     private getKey(column: string): string { | ||||
|         return `${this.name}_${column}`; | ||||
|     } | ||||
|  | ||||
|     public isColumnVisible(column: string): boolean { | ||||
|         return !this._hiddenColumns.map(column => column.key).includes(this.getKey(column)); | ||||
|     } | ||||
|  | ||||
|     public callback = (data: any[]) => { | ||||
|         this.save(data); | ||||
|     }; | ||||
|  | ||||
|     public onRowEditSave(newLevel: any, index: number) { | ||||
|     } | ||||
|  | ||||
|     public save(data: any[]) { | ||||
|         for (let i = 0; i < data.length; i++) { | ||||
|             this.onRowEditSave(data[i], i); | ||||
|         } | ||||
|   public isColumnVisible(column: string): boolean { | ||||
|     return !this._hiddenColumns.map(column => column.key).includes(this.getKey(column)); | ||||
|   } | ||||
|  | ||||
|   public onRowEditSave(newLevel: any, index: number) { | ||||
|   } | ||||
|  | ||||
|   public save(data: any[], isNew: boolean) { | ||||
|     for (let i = 0; i < data.length; i++) { | ||||
|       this.isEditingNew = isNew; | ||||
|       this.onRowEditSave(data[i], i); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -31,7 +31,6 @@ export class AuthUserComponent extends ComponentWithTable implements OnInit, OnD | ||||
|   activityValues: number[] = [0, 100]; | ||||
|  | ||||
|   clonedUsers: { [s: string]: AuthUserDTO; } = {}; | ||||
|   isEditingNew: boolean = false; | ||||
|  | ||||
|   authRoles = [ | ||||
|     { label: AuthRoles[AuthRoles.Normal].toString(), value: AuthRoles.Normal }, | ||||
|   | ||||
| @@ -1,66 +1,86 @@ | ||||
| import { Component, EventEmitter, Input, Output, ViewChild } from "@angular/core"; | ||||
| import { ToastService } from "../../../../services/toast/toast.service"; | ||||
| import { TranslateService } from "@ngx-translate/core"; | ||||
| import { elementAt } from "rxjs"; | ||||
|  | ||||
| interface UploadEvent { | ||||
|     originalEvent: Event; | ||||
|     files: File[]; | ||||
|   originalEvent: Event; | ||||
|   files: File[]; | ||||
| } | ||||
|  | ||||
| @Component({ | ||||
|     selector: "app-data-import-and-export", | ||||
|     templateUrl: "./data-import-and-export.component.html", | ||||
|     styleUrls: ["./data-import-and-export.component.scss"] | ||||
|   selector: "app-data-import-and-export", | ||||
|   templateUrl: "./data-import-and-export.component.html", | ||||
|   styleUrls: ["./data-import-and-export.component.scss"] | ||||
| }) | ||||
| export class DataImportAndExportComponent { | ||||
|  | ||||
|     @ViewChild("upload") upload: any; | ||||
|   @ViewChild("upload") upload: any; | ||||
|  | ||||
|     private _data: any[] = []; | ||||
|   private _data: any[] = []; | ||||
|  | ||||
|     @Input() name: string = ""; | ||||
|   @Input() name: string = ""; | ||||
|  | ||||
|     @Input() | ||||
|     set data(data: any[]) { | ||||
|         this._data = data; | ||||
|         this.dataChange.emit(data); | ||||
|     } | ||||
|   @Input() | ||||
|   set data(data: any[]) { | ||||
|     this._data = data; | ||||
|     this.dataChange.emit(data); | ||||
|   } | ||||
|  | ||||
|     get data(): any[] { | ||||
|         return this._data; | ||||
|     } | ||||
|   get data(): any[] { | ||||
|     return this._data; | ||||
|   } | ||||
|  | ||||
|     @Output() dataChange: EventEmitter<any[]> = new EventEmitter<any[]>(); | ||||
|     @Input() callback!: (data: any[]) => void; | ||||
|   @Output() dataChange: EventEmitter<any[]> = new EventEmitter<any[]>(); | ||||
|   @Input() callback!: (data: any[], isNew: boolean) => void; | ||||
|   @Input() validator: (oldElement: any, newElement: any) => boolean = (oldElement: any, newElement: any) => { | ||||
|     return true; | ||||
|   }; | ||||
|  | ||||
|     public constructor( | ||||
|         private toastService: ToastService, | ||||
|         private translate: TranslateService | ||||
|     ) { | ||||
|   public constructor( | ||||
|     private toastService: ToastService, | ||||
|     private translate: TranslateService | ||||
|   ) { | ||||
|  | ||||
|     } | ||||
|   } | ||||
|  | ||||
|     public export() { | ||||
|         const json = JSON.stringify(this.data); | ||||
|         const element = document.createElement("a"); | ||||
|         element.setAttribute("href", "data:application/json;charset=UTF-8," + encodeURIComponent(json)); | ||||
|         element.setAttribute("download", `${this.name}.json`); | ||||
|         element.style.display = "none"; | ||||
|         document.body.appendChild(element); | ||||
|         element.click(); // simulate click | ||||
|         document.body.removeChild(element); | ||||
|     } | ||||
|   public export() { | ||||
|     const json = JSON.stringify(this.data); | ||||
|     const element = document.createElement("a"); | ||||
|     element.setAttribute("href", "data:application/json;charset=UTF-8," + encodeURIComponent(json)); | ||||
|     element.setAttribute("download", `${this.name}.json`); | ||||
|     element.style.display = "none"; | ||||
|     document.body.appendChild(element); | ||||
|     element.click(); // simulate click | ||||
|     document.body.removeChild(element); | ||||
|   } | ||||
|  | ||||
|     public import(event: { files: File[] }) { | ||||
|         const file = event.files[0]; | ||||
|         const fileReader = new FileReader(); | ||||
|         fileReader.onload = () => { | ||||
|             if (!fileReader.result) return; | ||||
|             this.data = JSON.parse(fileReader.result.toString()); | ||||
|             this.upload.clear(); | ||||
|             this.callback(this.data); | ||||
|             this.toastService.success(this.translate.instant("common.file.uploaded"), this.translate.instant("common.file.uploaded")); | ||||
|         }; | ||||
|         fileReader.readAsText(file, "UTF-8"); | ||||
|     } | ||||
|   public import(event: { files: File[] }) { | ||||
|     const file = event.files[0]; | ||||
|     const fileReader = new FileReader(); | ||||
|     fileReader.onload = () => { | ||||
|       if (!fileReader.result) return; | ||||
|       const newData: any[] = JSON.parse(fileReader.result.toString()); | ||||
|       this.upload.clear(); | ||||
|       newData.forEach(element => { | ||||
|         element.id = 0; | ||||
|       }); | ||||
|       this.data.forEach(element => { | ||||
|         const existingElement = newData.find(x => this.validator(x, element)); | ||||
|         if (existingElement) { | ||||
|           const index = this.data.indexOf(element); | ||||
|           const oldId = element.id; | ||||
|           element = existingElement; | ||||
|           element.id = oldId; | ||||
|           this.data[index] = element; | ||||
|           newData.splice(newData.indexOf(existingElement), 1); | ||||
|         } | ||||
|       }); | ||||
|       this.callback(this.data, false); | ||||
|       this.callback(newData, true); | ||||
|       this.data.push(...newData); | ||||
|       this.toastService.success(this.translate.instant("common.file.uploaded"), this.translate.instant("common.file.uploaded")); | ||||
|     }; | ||||
|     fileReader.readAsText(file, "UTF-8"); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ | ||||
|                     class="icon-btn btn" (click)="resetFilters()"> | ||||
|             </button> | ||||
|             <app-data-import-and-export name="achievements" [(data)]="achievements" | ||||
|                                         [callback]="callback"></app-data-import-and-export> | ||||
|                                         [callback]="callback" [validator]="validator"></app-data-import-and-export> | ||||
|           </div> | ||||
|         </div> | ||||
|       </ng-template> | ||||
|   | ||||
| @@ -15,7 +15,13 @@ 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 { AchievementListQuery, AchievementTypeQuery, GameServerListQuery, LevelListQuery, Query } from "../../../../../../models/graphql/query.model"; | ||||
| import { | ||||
|   AchievementListQuery, | ||||
|   AchievementTypeQuery, | ||||
|   GameServerListQuery, | ||||
|   LevelListQuery, | ||||
|   Query | ||||
| } from "../../../../../../models/graphql/query.model"; | ||||
| import { catchError, debounceTime, takeUntil } from "rxjs/operators"; | ||||
| import { LazyLoadEvent, MenuItem } from "primeng/api"; | ||||
| import { Table } from "primeng/table"; | ||||
| @@ -33,8 +39,6 @@ export class AchievementComponent extends ComponentWithTable implements OnInit, | ||||
|   public achievements: Achievement[] = []; | ||||
|   public loading = true; | ||||
|  | ||||
|   public isEditingNew: boolean = false; | ||||
|  | ||||
|   public filterForm!: FormGroup<{ | ||||
|     id: FormControl<number | null>, | ||||
|     name: FormControl<string | null>, | ||||
| @@ -81,7 +85,10 @@ export class AchievementComponent extends ComponentWithTable implements OnInit, | ||||
|     private data: DataService, | ||||
|     private sidebar: SidebarService, | ||||
|     private route: ActivatedRoute) { | ||||
|     super("achievement", ["id", "name", "description", "attribute", "operator", "value"]); | ||||
|     super("achievement", ["id", "name", "description", "attribute", "operator", "value"], | ||||
|       (oldElement: Achievement, newElement: Achievement) => { | ||||
|         return oldElement.name === newElement.name; | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   public ngOnInit(): void { | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
|                     class="icon-btn btn" (click)="resetFilters()"> | ||||
|             </button> | ||||
|             <app-data-import-and-export name="achievements" [(data)]="rules" | ||||
|                                         [callback]="callback"></app-data-import-and-export> | ||||
|                                         [callback]="callback" [validator]="validator"></app-data-import-and-export> | ||||
|           </div> | ||||
|         </div> | ||||
|       </ng-template> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { Component, OnDestroy, OnInit } from "@angular/core"; | ||||
| import { DataService } from "../../../../../../services/data/data.service"; | ||||
| import { ActivatedRoute, Router } from "@angular/router"; | ||||
| import { AutoRoleRule, AutoRoleRuleFilter } from "../../../../../../models/data/auto_role.model"; | ||||
| import { AutoRole, AutoRoleRule, AutoRoleRuleFilter } from "../../../../../../models/data/auto_role.model"; | ||||
| import { Guild } from "../../../../../../models/data/discord.model"; | ||||
| import { LazyLoadEvent, MenuItem } from "primeng/api"; | ||||
| import { User } from "../../../../../../models/data/user.model"; | ||||
| @@ -40,7 +40,6 @@ export class AutoRolesRulesComponent extends ComponentWithTable implements OnIni | ||||
|   autoRoleId!: number; | ||||
|  | ||||
|   clonedUsers: { [s: string]: User; } = {}; | ||||
|   isEditingNew: boolean = false; | ||||
|  | ||||
|   newAutoRoleTemplate: AutoRoleRule = { | ||||
|     createdAt: "", | ||||
| @@ -81,7 +80,10 @@ export class AutoRolesRulesComponent extends ComponentWithTable implements OnIni | ||||
|     private route: ActivatedRoute, | ||||
|     private router: Router | ||||
|   ) { | ||||
|     super("auto-role-rules", ["id", "role", "emoji"]); | ||||
|     super("auto-role-rules", ["id", "role", "emoji"], (oldElement: AutoRoleRule, newElement: AutoRoleRule) => { | ||||
|       return oldElement.autoRole?.id === newElement.autoRole?.id && | ||||
|         oldElement.roleId === newElement.roleId; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public getEmojiUrl(name: string): string { | ||||
|   | ||||
| @@ -28,7 +28,7 @@ | ||||
|                     class="icon-btn btn" (click)="resetFilters()"> | ||||
|             </button> | ||||
|             <app-data-import-and-export name="achievements" [(data)]="auto_roles" | ||||
|                                         [callback]="callback"></app-data-import-and-export> | ||||
|                                         [callback]="callback" [validator]="validator"></app-data-import-and-export> | ||||
|           </div> | ||||
|         </div> | ||||
|       </ng-template> | ||||
|   | ||||
| @@ -36,7 +36,6 @@ export class AutoRolesComponent extends ComponentWithTable implements OnInit, On | ||||
|   loading = true; | ||||
|  | ||||
|   clonedUsers: { [s: string]: User; } = {}; | ||||
|   isEditingNew: boolean = false; | ||||
|  | ||||
|   newAutoRoleTemplate: AutoRole = { | ||||
|     createdAt: "", | ||||
| @@ -77,7 +76,10 @@ export class AutoRolesComponent extends ComponentWithTable implements OnInit, On | ||||
|     private sidebar: SidebarService, | ||||
|     private route: ActivatedRoute | ||||
|   ) { | ||||
|     super("auto-role", ["id", "channel_id", "channel_name", "message_id", "rule_count"]); | ||||
|     super("auto-role", ["id", "channel_id", "channel_name", "message_id", "rule_count"], (oldElement: AutoRole, newElement: AutoRole) => { | ||||
|       return oldElement.channelId === newElement.channelId && | ||||
|         oldElement.messageId === newElement.messageId; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public ngOnInit(): void { | ||||
|   | ||||
| @@ -31,7 +31,7 @@ | ||||
|                                 class="icon-btn btn" (click)="resetFilters()"> | ||||
|                         </button> | ||||
|                         <app-data-import-and-export name="levels" [(data)]="levels" | ||||
|                                                     [callback]="callback"></app-data-import-and-export> | ||||
|                                                     [callback]="callback" [validator]="validator"></app-data-import-and-export> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </ng-template> | ||||
|   | ||||
| @@ -19,247 +19,247 @@ import { Table } from "primeng/table"; | ||||
| import { User } from "../../../../../../models/data/user.model"; | ||||
| import { LevelMutationResult, UpdateUserMutationResult } from "../../../../../../models/graphql/result.model"; | ||||
| import { Mutations } from "../../../../../../models/graphql/mutations.model"; | ||||
| import { Subject, throwError } from "rxjs"; | ||||
| import { forkJoin, Subject, throwError } from "rxjs"; | ||||
| import { Server } from "../../../../../../models/data/server.model"; | ||||
| import { UserDTO } from "../../../../../../models/auth/auth-user.dto"; | ||||
| import { ComponentWithTable } from "../../../../../../base/component-with-table"; | ||||
|  | ||||
| @Component({ | ||||
|     selector: "app-levels", | ||||
|     templateUrl: "./levels.component.html", | ||||
|     styleUrls: ["./levels.component.scss"] | ||||
|   selector: "app-levels", | ||||
|   templateUrl: "./levels.component.html", | ||||
|   styleUrls: ["./levels.component.scss"] | ||||
| }) | ||||
| export class LevelsComponent extends ComponentWithTable implements OnInit, OnDestroy { | ||||
|     public levels: Level[] = []; | ||||
|     public loading = true; | ||||
|   public levels: Level[] = []; | ||||
|   public loading = true; | ||||
|  | ||||
|     public isEditingNew: boolean = false; | ||||
|   public filterForm!: FormGroup<{ | ||||
|     id: FormControl<number | null>, | ||||
|     name: FormControl<string | null>, | ||||
|     color: FormControl<string | null>, | ||||
|     min_xp: FormControl<number | null>, | ||||
|     permissions: FormControl<string | null>, | ||||
|   }>; | ||||
|  | ||||
|     public filterForm!: FormGroup<{ | ||||
|         id: FormControl<number | null>, | ||||
|         name: FormControl<string | null>, | ||||
|         color: FormControl<string | null>, | ||||
|         min_xp: FormControl<number | null>, | ||||
|         permissions: FormControl<string | null>, | ||||
|     }>; | ||||
|   public filter: LevelFilter = {}; | ||||
|   public page: Page = { | ||||
|     pageSize: undefined, | ||||
|     pageIndex: undefined | ||||
|   }; | ||||
|   public sort: Sort = { | ||||
|     sortColumn: undefined, | ||||
|     sortDirection: undefined | ||||
|   }; | ||||
|  | ||||
|     public filter: LevelFilter = {}; | ||||
|     public page: Page = { | ||||
|         pageSize: undefined, | ||||
|         pageIndex: undefined | ||||
|     }; | ||||
|     public sort: Sort = { | ||||
|         sortColumn: undefined, | ||||
|         sortDirection: undefined | ||||
|     }; | ||||
|   public totalRecords: number = 0; | ||||
|  | ||||
|     public totalRecords: number = 0; | ||||
|   public clonedLevels: { [s: string]: Level; } = {}; | ||||
|  | ||||
|     public clonedLevels: { [s: string]: Level; } = {}; | ||||
|   private unsubscriber = new Subject<void>(); | ||||
|   private server: Server = {}; | ||||
|   public user: UserDTO | null = null; | ||||
|  | ||||
|     private unsubscriber = new Subject<void>(); | ||||
|     private server: Server = {}; | ||||
|     public user: UserDTO | null = null; | ||||
|   query: string = Queries.levelWithHistoryQuery; | ||||
|  | ||||
|     query: string = Queries.levelWithHistoryQuery; | ||||
|   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("level", ["id", "name", "color", "min_xp", "permissions"], (oldElement: Level, newElement: Level) => { | ||||
|       return oldElement.name === newElement.name; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|     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("level", ["id", "name", "color", "min_xp", "permissions"]); | ||||
|   public ngOnInit(): void { | ||||
|     this.setFilterForm(); | ||||
|     this.data.getServerFromRoute(this.route).then(async server => { | ||||
|       this.server = server; | ||||
|       this.loadNextPage(); | ||||
|       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.loading = true; | ||||
|     this.data.query<LevelListQuery>(Queries.levelQuery, { | ||||
|         serverId: this.server.id, filter: this.filter, page: this.page, sort: this.sort | ||||
|       }, | ||||
|       (data: Query) => { | ||||
|         return data.servers[0]; | ||||
|       } | ||||
|     ).subscribe(data => { | ||||
|       this.totalRecords = data.levelCount; | ||||
|       this.levels = data.levels; | ||||
|       this.spinner.hideSpinner(); | ||||
|       this.loading = false; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public setFilterForm(): void { | ||||
|     this.filterForm = this.fb.group({ | ||||
|       id: new FormControl<number | null>(null), | ||||
|       name: new FormControl<string | null>(null), | ||||
|       color: new FormControl<string | null>(null), | ||||
|       min_xp: new FormControl<number | null>(null), | ||||
|       permissions: 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 (changes.name) { | ||||
|         this.filter.name = changes.name; | ||||
|       } else { | ||||
|         this.filter.name = undefined; | ||||
|       } | ||||
|  | ||||
|       if (this.page.pageSize) | ||||
|         this.page.pageSize = 10; | ||||
|  | ||||
|       if (this.page.pageIndex) | ||||
|         this.page.pageIndex = 0; | ||||
|  | ||||
|       this.loadNextPage(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public newLevelTemplate: Level = { | ||||
|     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.clonedLevels[index] = { ...user }; | ||||
|   } | ||||
|  | ||||
|   public override onRowEditSave(newLevel: Level, index: number): void { | ||||
|     if (this.isEditingNew && JSON.stringify(newLevel) === JSON.stringify(this.newLevelTemplate)) { | ||||
|       this.isEditingNew = false; | ||||
|       this.levels.splice(index, 1); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     public ngOnInit(): void { | ||||
|         this.setFilterForm(); | ||||
|         this.data.getServerFromRoute(this.route).then(async server => { | ||||
|             this.server = server; | ||||
|             this.loadNextPage(); | ||||
|             let authUser = await this.authService.getLoggedInUser(); | ||||
|             this.user = authUser?.users?.find(u => u.server == this.server.id) ?? null; | ||||
|         }); | ||||
|     if (!newLevel.id && !this.isEditingNew || !newLevel.minXp && !newLevel?.name && !newLevel?.permissions) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     public ngOnDestroy(): void { | ||||
|         this.unsubscriber.next(); | ||||
|         this.unsubscriber.complete(); | ||||
|     } | ||||
|  | ||||
|     public loadNextPage(): void { | ||||
|         this.loading = true; | ||||
|         this.data.query<LevelListQuery>(Queries.levelQuery, { | ||||
|                 serverId: this.server.id, filter: this.filter, page: this.page, sort: this.sort | ||||
|             }, | ||||
|             (data: Query) => { | ||||
|                 return data.servers[0]; | ||||
|             } | ||||
|         ).subscribe(data => { | ||||
|             this.totalRecords = data.levelCount; | ||||
|             this.levels = data.levels; | ||||
|             this.spinner.hideSpinner(); | ||||
|             this.loading = false; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public setFilterForm(): void { | ||||
|         this.filterForm = this.fb.group({ | ||||
|             id: new FormControl<number | null>(null), | ||||
|             name: new FormControl<string | null>(null), | ||||
|             color: new FormControl<string | null>(null), | ||||
|             min_xp: new FormControl<number | null>(null), | ||||
|             permissions: 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 (changes.name) { | ||||
|                 this.filter.name = changes.name; | ||||
|             } else { | ||||
|                 this.filter.name = undefined; | ||||
|             } | ||||
|  | ||||
|             if (this.page.pageSize) | ||||
|                 this.page.pageSize = 10; | ||||
|  | ||||
|             if (this.page.pageIndex) | ||||
|                 this.page.pageIndex = 0; | ||||
|  | ||||
|             this.loadNextPage(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public newLevelTemplate: Level = { | ||||
|         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; | ||||
|  | ||||
|     if (this.isEditingNew) { | ||||
|       this.spinner.showSpinner(); | ||||
|       this.data.mutation<LevelMutationResult>(Mutations.createLevel, { | ||||
|           name: newLevel.name, | ||||
|           color: newLevel.color, | ||||
|           minXp: newLevel.minXp, | ||||
|           permissions: newLevel.permissions, | ||||
|           serverId: this.server.id | ||||
|         } | ||||
|       ).pipe(catchError(err => { | ||||
|         this.isEditingNew = false; | ||||
|         this.spinner.hideSpinner(); | ||||
|         return throwError(err); | ||||
|       })).subscribe(result => { | ||||
|         this.isEditingNew = false; | ||||
|         this.spinner.hideSpinner(); | ||||
|         this.toastService.success(this.translate.instant("view.server.levels.message.level_create"), this.translate.instant("view.server.levels.message.level_create_d", { name: result.level.createLevel?.name })); | ||||
|         this.loadNextPage(); | ||||
|       }); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     public resetFilters(): void { | ||||
|         this.filterForm.reset(); | ||||
|     this.spinner.showSpinner(); | ||||
|     this.data.mutation<UpdateUserMutationResult>(Mutations.updateLevel, { | ||||
|         id: newLevel.id, | ||||
|         name: newLevel.name, | ||||
|         color: newLevel.color, | ||||
|         minXp: newLevel.minXp, | ||||
|         permissions: newLevel.permissions | ||||
|       } | ||||
|     ).pipe(catchError(err => { | ||||
|       this.spinner.hideSpinner(); | ||||
|       return throwError(err); | ||||
|     })).subscribe(_ => { | ||||
|       this.spinner.hideSpinner(); | ||||
|       this.toastService.success(this.translate.instant("view.server.levels.message.level_update"), this.translate.instant("view.server.levels.message.level_update_d", { name: newLevel.name })); | ||||
|       this.loadNextPage(); | ||||
|     }); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   public onRowEditCancel(index: number): void { | ||||
|     if (this.isEditingNew) { | ||||
|       this.levels.splice(index, 1); | ||||
|       delete this.clonedLevels[index]; | ||||
|       this.isEditingNew = false; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     public onRowEditInit(table: Table, user: User, index: number): void { | ||||
|         this.clonedLevels[index] = { ...user }; | ||||
|     } | ||||
|  | ||||
|     public override onRowEditSave(newLevel: Level, index: number): void { | ||||
|         if (this.isEditingNew && JSON.stringify(newLevel) === JSON.stringify(this.newLevelTemplate)) { | ||||
|             this.isEditingNew = false; | ||||
|             this.levels.splice(index, 1); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!newLevel.id && !this.isEditingNew || !newLevel.minXp && !newLevel?.name && !newLevel?.permissions) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (this.isEditingNew) { | ||||
|             this.spinner.showSpinner(); | ||||
|             this.data.mutation<LevelMutationResult>(Mutations.createLevel, { | ||||
|                     name: newLevel.name, | ||||
|                     color: newLevel.color, | ||||
|                     minXp: newLevel.minXp, | ||||
|                     permissions: newLevel.permissions, | ||||
|                     serverId: this.server.id | ||||
|                 } | ||||
|             ).pipe(catchError(err => { | ||||
|                 this.isEditingNew = false; | ||||
|                 this.spinner.hideSpinner(); | ||||
|                 return throwError(err); | ||||
|             })).subscribe(result => { | ||||
|                 this.isEditingNew = false; | ||||
|                 this.spinner.hideSpinner(); | ||||
|                 this.toastService.success(this.translate.instant("view.server.levels.message.level_create"), this.translate.instant("view.server.levels.message.level_create_d", { name: result.level.createLevel?.name })); | ||||
|                 this.loadNextPage(); | ||||
|             }); | ||||
|             return; | ||||
|         } | ||||
|     this.levels[index] = this.clonedLevels[index]; | ||||
|     delete this.clonedLevels[index]; | ||||
|   } | ||||
|  | ||||
|   public deleteLevel(level: Level): void { | ||||
|     this.confirmDialog.confirmDialog( | ||||
|       this.translate.instant("view.server.levels.message.level_delete"), this.translate.instant("view.server.levels.message.level_delete_q", { name: level.name }), | ||||
|       () => { | ||||
|         this.spinner.showSpinner(); | ||||
|         this.data.mutation<UpdateUserMutationResult>(Mutations.updateLevel, { | ||||
|                 id: newLevel.id, | ||||
|                 name: newLevel.name, | ||||
|                 color: newLevel.color, | ||||
|                 minXp: newLevel.minXp, | ||||
|                 permissions: newLevel.permissions | ||||
|             } | ||||
|         this.data.mutation<LevelMutationResult>(Mutations.deleteLevel, { | ||||
|             id: level.id | ||||
|           } | ||||
|         ).pipe(catchError(err => { | ||||
|             this.spinner.hideSpinner(); | ||||
|             return throwError(err); | ||||
|         })).subscribe(_ => { | ||||
|             this.spinner.hideSpinner(); | ||||
|             this.toastService.success(this.translate.instant("view.server.levels.message.level_update"), this.translate.instant("view.server.levels.message.level_update_d", { name: newLevel.name })); | ||||
|             this.loadNextPage(); | ||||
|           this.spinner.hideSpinner(); | ||||
|           return throwError(err); | ||||
|         })).subscribe(l => { | ||||
|           this.spinner.hideSpinner(); | ||||
|           this.toastService.success(this.translate.instant("view.server.levels.message.level_deleted"), this.translate.instant("view.server.levels.message.level_deleted_d", { name: level.name })); | ||||
|           this.loadNextPage(); | ||||
|         }); | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|     } | ||||
|   public addLevel(table: Table): void { | ||||
|     const newLevel = JSON.parse(JSON.stringify(this.newLevelTemplate)); | ||||
|  | ||||
|     public onRowEditCancel(index: number): void { | ||||
|         if (this.isEditingNew) { | ||||
|             this.levels.splice(index, 1); | ||||
|             delete this.clonedLevels[index]; | ||||
|             this.isEditingNew = false; | ||||
|             return; | ||||
|         } | ||||
|     this.levels = [newLevel, ...this.levels]; | ||||
|  | ||||
|         this.levels[index] = this.clonedLevels[index]; | ||||
|         delete this.clonedLevels[index]; | ||||
|     } | ||||
|     table.initRowEdit(newLevel); | ||||
|  | ||||
|     public deleteLevel(level: Level): void { | ||||
|         this.confirmDialog.confirmDialog( | ||||
|             this.translate.instant("view.server.levels.message.level_delete"), this.translate.instant("view.server.levels.message.level_delete_q", { name: level.name }), | ||||
|             () => { | ||||
|                 this.spinner.showSpinner(); | ||||
|                 this.data.mutation<LevelMutationResult>(Mutations.deleteLevel, { | ||||
|                         id: level.id | ||||
|                     } | ||||
|                 ).pipe(catchError(err => { | ||||
|                     this.spinner.hideSpinner(); | ||||
|                     return throwError(err); | ||||
|                 })).subscribe(l => { | ||||
|                     this.spinner.hideSpinner(); | ||||
|                     this.toastService.success(this.translate.instant("view.server.levels.message.level_deleted"), this.translate.instant("view.server.levels.message.level_deleted_d", { name: level.name })); | ||||
|                     this.loadNextPage(); | ||||
|                 }); | ||||
|             }); | ||||
|     } | ||||
|     const index = this.levels.findIndex(l => l.id == newLevel.id); | ||||
|     this.onRowEditInit(table, newLevel, index); | ||||
|  | ||||
|     public addLevel(table: Table): void { | ||||
|         const newLevel = JSON.parse(JSON.stringify(this.newLevelTemplate)); | ||||
|  | ||||
|         this.levels = [newLevel, ...this.levels]; | ||||
|  | ||||
|         table.initRowEdit(newLevel); | ||||
|  | ||||
|         const index = this.levels.findIndex(l => l.id == newLevel.id); | ||||
|         this.onRowEditInit(table, newLevel, index); | ||||
|  | ||||
|         this.isEditingNew = true; | ||||
|     } | ||||
|     this.isEditingNew = true; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -34,7 +34,6 @@ export class MembersComponent extends ComponentWithTable implements OnInit, OnDe | ||||
|   loading = true; | ||||
|  | ||||
|   clonedUsers: { [s: string]: User; } = {}; | ||||
|   isEditingNew: boolean = false; | ||||
|  | ||||
|   newUserTemplate: User = { | ||||
|     discordId: 0, | ||||
|   | ||||
| @@ -28,7 +28,7 @@ | ||||
|                     class="icon-btn btn" (click)="resetFilters()"> | ||||
|             </button> | ||||
|             <app-data-import-and-export name="achievements" [(data)]="shortRoleNames" | ||||
|                                         [callback]="callback"></app-data-import-and-export> | ||||
|                                         [callback]="callback" [validator]="validator"></app-data-import-and-export> | ||||
|           </div> | ||||
|         </div> | ||||
|       </ng-template> | ||||
|   | ||||
| @@ -33,8 +33,6 @@ export class ShortRoleNamesComponent extends ComponentWithTable implements OnIni | ||||
|   public shortRoleNames: ShortRoleName[] = []; | ||||
|   public loading = true; | ||||
|  | ||||
|   public isEditingNew: boolean = false; | ||||
|  | ||||
|   public filterForm!: FormGroup<{ | ||||
|     id: FormControl<number | null>, | ||||
|     shortName: FormControl<string | null>, | ||||
| @@ -75,7 +73,10 @@ export class ShortRoleNamesComponent extends ComponentWithTable implements OnIni | ||||
|     private sidebar: SidebarService, | ||||
|     private route: ActivatedRoute | ||||
|   ) { | ||||
|     super("short-role-names", ["id", "name", "role", "position"]); | ||||
|     super("short-role-names", ["id", "name", "role", "position"], (oldElement: ShortRoleName, newElement: ShortRoleName) => { | ||||
|       return oldElement.shortName === newElement.shortName && | ||||
|         oldElement.roleId === newElement.roleId; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public ngOnInit(): void { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user