From 84fedfaa0ba097dee881af62d597ebc622a75ee0 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 1 Nov 2023 22:37:33 +0100 Subject: [PATCH] Improved data validation for import --- .../bot_graphql/mutations/level_mutation.py | 3 + web/src/app/base/component-with-table.ts | 103 +++-- .../auth-user/auth-user.component.ts | 1 - .../data-import-and-export.component.ts | 110 +++-- .../achievement/achievement.component.html | 2 +- .../achievement/achievement.component.ts | 15 +- .../auto-roles-rules.component.html | 2 +- .../auto-roles-rules.component.ts | 8 +- .../auto-roles/auto-roles.component.html | 2 +- .../auto-roles/auto-roles.component.ts | 6 +- .../components/levels/levels.component.html | 2 +- .../components/levels/levels.component.ts | 418 +++++++++--------- .../view/server/members/members.component.ts | 1 - .../short-role-names.component.html | 2 +- .../short-role-names.component.ts | 7 +- 15 files changed, 361 insertions(+), 321 deletions(-) diff --git a/bot/src/bot_graphql/mutations/level_mutation.py b/bot/src/bot_graphql/mutations/level_mutation.py index 215538b7..e0959c55 100644 --- a/bot/src/bot_graphql/mutations/level_mutation.py +++ b/bot/src/bot_graphql/mutations/level_mutation.py @@ -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() diff --git a/web/src/app/base/component-with-table.ts b/web/src/app/base/component-with-table.ts index d38e09c9..acafbaa4 100644 --- a/web/src/app/base/component-with-table.ts +++ b/web/src/app/base/component-with-table.ts @@ -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); } + } } diff --git a/web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts b/web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts index cb69d8c2..faec3c9a 100644 --- a/web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts +++ b/web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts @@ -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 }, diff --git a/web/src/app/modules/shared/components/data-import-and-export/data-import-and-export.component.ts b/web/src/app/modules/shared/components/data-import-and-export/data-import-and-export.component.ts index 1a4ef75c..8bab8927 100644 --- a/web/src/app/modules/shared/components/data-import-and-export/data-import-and-export.component.ts +++ b/web/src/app/modules/shared/components/data-import-and-export/data-import-and-export.component.ts @@ -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 = new EventEmitter(); - @Input() callback!: (data: any[]) => void; + @Output() dataChange: EventEmitter = new EventEmitter(); + @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"); + } } diff --git a/web/src/app/modules/view/server/achievements/components/achievement/achievement.component.html b/web/src/app/modules/view/server/achievements/components/achievement/achievement.component.html index 823db3e1..45e43f36 100644 --- a/web/src/app/modules/view/server/achievements/components/achievement/achievement.component.html +++ b/web/src/app/modules/view/server/achievements/components/achievement/achievement.component.html @@ -28,7 +28,7 @@ class="icon-btn btn" (click)="resetFilters()"> + [callback]="callback" [validator]="validator"> diff --git a/web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts b/web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts index 18f03c2b..e2b6e508 100644 --- a/web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts +++ b/web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts @@ -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, name: FormControl, @@ -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 { diff --git a/web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.html b/web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.html index 638c8ad3..c9abacbc 100644 --- a/web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.html +++ b/web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.html @@ -27,7 +27,7 @@ class="icon-btn btn" (click)="resetFilters()"> + [callback]="callback" [validator]="validator"> diff --git a/web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts b/web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts index a8fda8d6..d9bc9061 100644 --- a/web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts +++ b/web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts @@ -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 { diff --git a/web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.html b/web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.html index 7784981d..66915c46 100644 --- a/web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.html +++ b/web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.html @@ -28,7 +28,7 @@ class="icon-btn btn" (click)="resetFilters()"> + [callback]="callback" [validator]="validator"> diff --git a/web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.ts b/web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.ts index c5b26460..2a5fd6b0 100644 --- a/web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.ts +++ b/web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.ts @@ -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 { diff --git a/web/src/app/modules/view/server/levels/components/levels/levels.component.html b/web/src/app/modules/view/server/levels/components/levels/levels.component.html index 329fcd3b..0af4809d 100644 --- a/web/src/app/modules/view/server/levels/components/levels/levels.component.html +++ b/web/src/app/modules/view/server/levels/components/levels/levels.component.html @@ -31,7 +31,7 @@ class="icon-btn btn" (click)="resetFilters()"> + [callback]="callback" [validator]="validator"> diff --git a/web/src/app/modules/view/server/levels/components/levels/levels.component.ts b/web/src/app/modules/view/server/levels/components/levels/levels.component.ts index 80f3660a..85558bc0 100644 --- a/web/src/app/modules/view/server/levels/components/levels/levels.component.ts +++ b/web/src/app/modules/view/server/levels/components/levels/levels.component.ts @@ -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, + name: FormControl, + color: FormControl, + min_xp: FormControl, + permissions: FormControl, + }>; - public filterForm!: FormGroup<{ - id: FormControl, - name: FormControl, - color: FormControl, - min_xp: FormControl, - permissions: FormControl, - }>; + 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(); + private server: Server = {}; + public user: UserDTO | null = null; - private unsubscriber = new Subject(); - 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(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(null), + name: new FormControl(null), + color: new FormControl(null), + min_xp: new FormControl(null), + permissions: new FormControl(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(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(null), - name: new FormControl(null), - color: new FormControl(null), - min_xp: new FormControl(null), - permissions: new FormControl(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(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(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(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(Mutations.updateLevel, { - id: newLevel.id, - name: newLevel.name, - color: newLevel.color, - minXp: newLevel.minXp, - permissions: newLevel.permissions - } + this.data.mutation(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(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; + } } diff --git a/web/src/app/modules/view/server/members/members.component.ts b/web/src/app/modules/view/server/members/members.component.ts index 980de0ca..32855b6e 100644 --- a/web/src/app/modules/view/server/members/members.component.ts +++ b/web/src/app/modules/view/server/members/members.component.ts @@ -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, diff --git a/web/src/app/modules/view/server/short-role-name/components/short-role-names/short-role-names.component.html b/web/src/app/modules/view/server/short-role-name/components/short-role-names/short-role-names.component.html index bb312ecf..684e2e0e 100644 --- a/web/src/app/modules/view/server/short-role-name/components/short-role-names/short-role-names.component.html +++ b/web/src/app/modules/view/server/short-role-name/components/short-role-names/short-role-names.component.html @@ -28,7 +28,7 @@ class="icon-btn btn" (click)="resetFilters()"> + [callback]="callback" [validator]="validator"> diff --git a/web/src/app/modules/view/server/short-role-name/components/short-role-names/short-role-names.component.ts b/web/src/app/modules/view/server/short-role-name/components/short-role-names/short-role-names.component.ts index 49665c6a..8202fd5f 100644 --- a/web/src/app/modules/view/server/short-role-name/components/short-role-names/short-role-names.component.ts +++ b/web/src/app/modules/view/server/short-role-name/components/short-role-names/short-role-names.component.ts @@ -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, shortName: FormControl, @@ -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 {