From 97e7f2f01e242790ee5074d77e7645d2698428b8 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 14 Aug 2023 18:06:32 +0200 Subject: [PATCH] Added server config to frontend #127 --- .../mutations/server_config_mutation.py | 6 + .../app/models/config/server-config.model.ts | 21 +++ .../models/config/technician-config.model.ts | 4 +- .../src/app/models/graphql/mutations.model.ts | 68 +++++++++- .../src/app/models/graphql/queries.model.ts | 67 +++++++--- kdb-web/src/app/models/graphql/query.model.ts | 5 + .../src/app/models/graphql/result.model.ts | 7 + .../settings/settings.component.html | 96 +------------- .../components/settings/settings.component.ts | 114 +--------------- .../config-list/config-list.component.html | 45 +++++++ .../config-list/config-list.component.scss | 0 .../config-list/config-list.component.spec.ts | 23 ++++ .../config-list/config-list.component.ts | 72 ++++++++++ .../src/app/modules/shared/shared.module.ts | 61 ++++----- .../achievement/achievement.component.ts | 3 - .../auto-roles-rules.component.ts | 3 - .../auto-roles/auto-roles.component.ts | 2 - .../components/config/config.component.html | 123 ++++++++++++++++++ .../components/config/config.component.scss | 0 .../config/config.component.spec.ts | 23 ++++ .../components/config/config.component.ts | 116 +++++++++++++++++ .../server/config/config-routing.module.ts | 13 ++ .../view/server/config/config.module.ts | 19 +++ .../components/levels/levels.component.ts | 3 - .../view/server/members/members.component.ts | 1 - .../view/server/server-routing.module.ts | 3 +- kdb-web/src/app/services/data/data.service.ts | 10 +- .../app/services/sidebar/sidebar.service.ts | 13 +- kdb-web/src/assets/i18n/de.json | 26 +++- kdb-web/src/assets/i18n/en.json | 28 +++- 30 files changed, 699 insertions(+), 276 deletions(-) create mode 100644 kdb-web/src/app/models/config/server-config.model.ts create mode 100644 kdb-web/src/app/modules/shared/components/config-list/config-list.component.html create mode 100644 kdb-web/src/app/modules/shared/components/config-list/config-list.component.scss create mode 100644 kdb-web/src/app/modules/shared/components/config-list/config-list.component.spec.ts create mode 100644 kdb-web/src/app/modules/shared/components/config-list/config-list.component.ts create mode 100644 kdb-web/src/app/modules/view/server/config/components/config/config.component.html create mode 100644 kdb-web/src/app/modules/view/server/config/components/config/config.component.scss create mode 100644 kdb-web/src/app/modules/view/server/config/components/config/config.component.spec.ts create mode 100644 kdb-web/src/app/modules/view/server/config/components/config/config.component.ts create mode 100644 kdb-web/src/app/modules/view/server/config/config-routing.module.ts create mode 100644 kdb-web/src/app/modules/view/server/config/config.module.ts diff --git a/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py b/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py index 22669a7c..2480be82 100644 --- a/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py +++ b/kdb-bot/src/bot_graphql/mutations/server_config_mutation.py @@ -150,6 +150,12 @@ class ServerConfigMutation(QueryABC): self._server_configs.delete_server_team_role_id_config(role_id) + for role_id in new_config.team_role_ids: + guild = self._bot.get_guild(new_config.server.discord_id) + role = guild.get_role(int(role_id.role_id)) + if role is None: + raise ValueError(f"Invalid roleId") + for role_id in new_config.team_role_ids: if role_id.role_id in old_config.team_role_ids.select(lambda x: str(x.role_id)): continue diff --git a/kdb-web/src/app/models/config/server-config.model.ts b/kdb-web/src/app/models/config/server-config.model.ts new file mode 100644 index 00000000..b55c4136 --- /dev/null +++ b/kdb-web/src/app/models/config/server-config.model.ts @@ -0,0 +1,21 @@ +import { DataWithHistory } from "../data/data.model"; + +export interface ServerConfig extends DataWithHistory { + id?: number; + messageDeleteTimer?: number; + notificationChatId?: string; + maxVoiceStateHours?: number; + xpPerMessage?: number; + xpPerReaction?: number; + maxMessageXpPerHour?: number; + xpPerOntimeHour?: number; + xpPerEventParticipation?: number; + xpPerAchievement?: number; + afkCommandChannelId?: string; + helpVoiceChannelId?: string; + teamChannelId?: string; + loginMessageChannelId?: string; + afkChannelIds: string[]; + moderatorRoleIds: string[]; + adminRoleIds: string[]; +} diff --git a/kdb-web/src/app/models/config/technician-config.model.ts b/kdb-web/src/app/models/config/technician-config.model.ts index 02ade32c..6e1430f0 100644 --- a/kdb-web/src/app/models/config/technician-config.model.ts +++ b/kdb-web/src/app/models/config/technician-config.model.ts @@ -6,6 +6,6 @@ export interface TechnicianConfig extends DataWithHistory { waitForRestart?: number; waitForShutdown?: number; cacheMaxMessages?: number; - pingURLs?: string[]; - technicianIds?: string[]; + pingURLs: string[]; + technicianIds: string[]; } diff --git a/kdb-web/src/app/models/graphql/mutations.model.ts b/kdb-web/src/app/models/graphql/mutations.model.ts index 2aa447cd..7905eff4 100644 --- a/kdb-web/src/app/models/graphql/mutations.model.ts +++ b/kdb-web/src/app/models/graphql/mutations.model.ts @@ -166,8 +166,6 @@ export class Mutations { } `; - - static updateTechnicianConfig = ` mutation updateTechnicianConfig($id: ID, $helpCommandReferenceUrl: String, $waitForRestart: Int, $waitForShutdown: Int, $cacheMaxMessages: Int, $pingURLs: [String], $technicianIds: [String]) { technicianConfig { @@ -191,4 +189,70 @@ export class Mutations { } } `; + + static updateServerConfig = ` + mutation updateServerConfig( + $id: ID, + $messageDeleteTimer: Int, + $notificationChatId: String, + $maxVoiceStateHours: Int, + $xpPerMessage: Int, + $xpPerReaction: Int, + $maxMessageXpPerHour: Int, + $xpPerOntimeHour: Int, + $xpPerEventParticipation: Int, + $xpPerAchievement: Int, + $afkCommandChannelId: String, + $helpVoiceChannelId: String, + $teamChannelId: String, + $loginMessageChannelId: String, + $afkChannelIds: [String], + $moderatorRoleIds: [String], + $adminRoleIds: [String] + ) { + serverConfig { + updateServerConfig(input: { + id: $id, + messageDeleteTimer: $messageDeleteTimer + notificationChatId: $notificationChatId + maxVoiceStateHours: $maxVoiceStateHours + xpPerMessage: $xpPerMessage + xpPerReaction: $xpPerReaction + maxMessageXpPerHour: $maxMessageXpPerHour + xpPerOntimeHour: $xpPerOntimeHour + xpPerEventParticipation: $xpPerEventParticipation + xpPerAchievement: $xpPerAchievement + afkCommandChannelId: $afkCommandChannelId + helpVoiceChannelId: $helpVoiceChannelId + teamChannelId: $teamChannelId + loginMessageChannelId: $loginMessageChannelId + afkChannelIds: $afkChannelIds + moderatorRoleIds: $moderatorRoleIds + adminRoleIds: $adminRoleIds + }) { + id + messageDeleteTimer + notificationChatId + maxVoiceStateHours + xpPerMessage + xpPerReaction + maxMessageXpPerHour + xpPerOntimeHour + xpPerEventParticipation + xpPerAchievement + afkCommandChannelId + helpVoiceChannelId + teamChannelId + loginMessageChannelId + afkChannelIds + moderatorRoleIds + adminRoleIds + + server { + id + } + } + } + } + `; } diff --git a/kdb-web/src/app/models/graphql/queries.model.ts b/kdb-web/src/app/models/graphql/queries.model.ts index 5311141e..55998599 100644 --- a/kdb-web/src/app/models/graphql/queries.model.ts +++ b/kdb-web/src/app/models/graphql/queries.model.ts @@ -1,22 +1,5 @@ export class Queries { - static technicianConfigQuery = ` - query technicianConfigQuery { - technicianConfig { - id - helpCommandReferenceUrl - waitForRestart - waitForShutdown - cacheMaxMessages - pingURLs - technicianIds - - createdAt - modifiedAt - } - } - `; - static guildsQuery = ` query GuildsQuery($id: ID) { guilds(filter: {id: $id}) { @@ -71,7 +54,7 @@ export class Queries { } } } - ` + `; static levelQuery = ` query LevelsList($serverId: ID, $filter: LevelFilter, $page: Page, $sort: Sort) { @@ -361,4 +344,52 @@ export class Queries { } } `; + + static technicianConfigQuery = ` + query technicianConfigQuery { + technicianConfig { + id + helpCommandReferenceUrl + waitForRestart + waitForShutdown + cacheMaxMessages + pingURLs + technicianIds + + createdAt + modifiedAt + } + } + `; + + static serverConfigQuery = ` + query serverConfigQuery($serverId: ID) { + servers(filter: { id: $serverId }) { + name + config { + id + messageDeleteTimer + notificationChatId + maxVoiceStateHours + xpPerMessage + xpPerReaction + maxMessageXpPerHour + xpPerOntimeHour + xpPerEventParticipation + xpPerAchievement + afkCommandChannelId + helpVoiceChannelId + teamChannelId + loginMessageChannelId + afkChannelIds + moderatorRoleIds + adminRoleIds + + server { + id + } + } + } + } + `; } diff --git a/kdb-web/src/app/models/graphql/query.model.ts b/kdb-web/src/app/models/graphql/query.model.ts index 1cc35083..42eeb69d 100644 --- a/kdb-web/src/app/models/graphql/query.model.ts +++ b/kdb-web/src/app/models/graphql/query.model.ts @@ -5,6 +5,7 @@ import { Guild } from "../data/discord.model"; import { Level } from "../data/level.model"; import { Achievement, AchievementAttribute } from "../data/achievement.model"; import { TechnicianConfig } from "../config/technician-config.model"; +import { ServerConfig } from "../config/server-config.model"; export interface Query { serverCount: number; @@ -15,6 +16,10 @@ export interface TechnicianConfigQuery { technicianConfig: TechnicianConfig; } +export interface ServerConfigQuery { + config: ServerConfig; +} + export interface SingleDiscordQuery { guilds: Guild[]; } diff --git a/kdb-web/src/app/models/graphql/result.model.ts b/kdb-web/src/app/models/graphql/result.model.ts index 37445578..573f98ce 100644 --- a/kdb-web/src/app/models/graphql/result.model.ts +++ b/kdb-web/src/app/models/graphql/result.model.ts @@ -4,6 +4,7 @@ import { Level } from "../data/level.model"; import { Server } from "../data/server.model"; import { Achievement } from "../data/achievement.model"; import { TechnicianConfig } from "../config/technician-config.model"; +import { ServerConfig } from "../config/server-config.model"; export interface GraphQLResult { data: { @@ -54,6 +55,12 @@ export interface TechnicianConfigMutationResult { }; } +export interface ServerConfigMutationResult { + serverConfig: { + updateServerConfig?: ServerConfig + }; +} + export interface AchievementMutationResult { achievement: { createAchievement?: Achievement diff --git a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html index 76707043..b7e68a1b 100644 --- a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html +++ b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html @@ -160,103 +160,13 @@ - -
-
-
-
- {{'admin.settings.bot.ping_urls' | translate}}: -
-
- - -
-
- -
-
-
- - - - - - - - - {{pingUrl.value}} - - - - -
- - - - - -
- - -
-
-
-
-
-
- -
-
-
- {{'admin.settings.bot.technicianIds' | translate}}: -
-
- - -
-
- -
-
-
- - - - - - - - - {{technicianId.value}} - - - - -
- - - - - -
- - -
-
-
-
-
+ +
+ (click)="saveTechnicianConfig()">
diff --git a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts index c0cb39b8..c4b691a7 100644 --- a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts +++ b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from "@angular/core"; -import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; import { TranslateService } from "@ngx-translate/core"; import { catchError } from "rxjs/operators"; import { SettingsDTO } from "src/app/models/config/settings.dto"; @@ -17,17 +17,7 @@ import { DataService } from "../../../../../services/data/data.service"; import { TechnicianConfigMutationResult } from "../../../../../models/graphql/result.model"; import { Mutations } from "../../../../../models/graphql/mutations.model"; import { AuthService } from "../../../../../services/auth/auth.service"; -import { Table } from "primeng/table"; -type PingUrl = { - id: number; - value: string; -}; - -type TechnicianId = { - id: number; - value: string; -}; @Component({ selector: "app-settings", @@ -37,16 +27,6 @@ type TechnicianId = { export class SettingsComponent implements OnInit { testMailForm!: FormGroup; - technicianConfigForm: FormGroup = this.formBuilder.group({ - helpCommandReferenceUrl: [null, [Validators.required]], - waitForRestart: [null, [Validators.required]], - waitForShutdown: [null, [Validators.required]], - cacheMaxMessages: [null, [Validators.required]] - }); - pingUrls: PingUrl[] = []; - clonedPingUrls: { [s: number]: PingUrl } = {}; - technicianIds: TechnicianId[] = []; - clonedTechnicianIds: { [s: number]: TechnicianId } = {}; data: SettingsDTO = { webVersion: "", apiVersion: "", @@ -114,23 +94,6 @@ export class SettingsComponent implements OnInit { this.testMailForm = this.formBuilder.group({ mail: [null, [Validators.required, Validators.email]] }); - this.technicianConfigForm = this.formBuilder.group({ - helpCommandReferenceUrl: [this.config.helpCommandReferenceUrl, [Validators.required]], - waitForRestart: [this.config.waitForRestart, [Validators.required]], - waitForShutdown: [this.config.waitForShutdown, [Validators.required]], - cacheMaxMessages: [this.config.cacheMaxMessages, [Validators.required]] - }); - let id = 0; - for (const url of this.config.pingURLs ?? []) { - this.pingUrls.push({ id: id, value: url }); - id++; - } - - id = 0; - for (const technicianId of this.config.technicianIds ?? []) { - this.technicianIds.push({ id: id, value: technicianId }); - id++; - } } testMail(): void { @@ -187,16 +150,11 @@ export class SettingsComponent implements OnInit { waitForRestart: this.config.waitForRestart, waitForShutdown: this.config.waitForShutdown, cacheMaxMessages: this.config.cacheMaxMessages, - pingURLs: this.pingUrls.filter(value => value.value).map(value => { - return value.value; - }), - technicianIds: this.technicianIds.filter(value => value.value).map(value => { - return value.value; - }) + pingURLs: this.config.pingURLs, + technicianIds: this.config.technicianIds } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("admin.settings.message.technician_config_create_failed"), this.translate.instant("admin.settings.message.technician_config_create_failed_d")); return throwError(err); })).subscribe(result => { this.spinner.hideSpinner(); @@ -204,71 +162,5 @@ export class SettingsComponent implements OnInit { }); } - pingUrlsAddNew(table: Table) { - const id = Math.max.apply(Math, this.pingUrls.map(url => { - return url.id ?? 0; - })) + 1; - const newItem = { id: id, value: "" }; - this.pingUrls.push(newItem); - - table.initRowEdit(newItem); - const index = this.pingUrls.findIndex(l => l.id == newItem.id); - this.pingUrlsEditInit(newItem, index); - } - - pingUrlsEditInit(url: PingUrl, index: number) { - this.clonedPingUrls[index] = { ...url }; - } - - pingUrlsDelete(index: number) { - this.pingUrls.splice(index, 1); - } - - pingUrlsEditSave(url: PingUrl, index: number) { - if (!url.value || this.pingUrls[index] == this.clonedPingUrls[index]) { - return; - } - - delete this.clonedPingUrls[index]; - } - - pingUrlsEditCancel(url: PingUrl, index: number) { - this.pingUrls[index] = this.clonedPingUrls[index]; - delete this.clonedPingUrls[index]; - } - - - technicianIdsAddNew(table: Table) { - const id = Math.max.apply(Math, this.technicianIds.map(url => { - return url.id ?? 0; - })) + 1; - const newItem = { id: id, value: "" }; - this.technicianIds.push(newItem); - - table.initRowEdit(newItem); - const index = this.technicianIds.findIndex(l => l.id == newItem.id); - this.technicianIdsEditInit(newItem, index); - } - - technicianIdsEditInit(url: PingUrl, index: number) { - this.clonedTechnicianIds[index] = { ...url }; - } - - technicianIdsDelete(index: number) { - this.technicianIds.splice(index, 1); - } - - technicianIdsEditSave(url: PingUrl, index: number) { - if (!url.value || this.technicianIds[index] == this.clonedTechnicianIds[index]) { - return; - } - - delete this.clonedTechnicianIds[index]; - } - - technicianIdsEditCancel(url: PingUrl, index: number) { - this.technicianIds[index] = this.clonedTechnicianIds[index]; - delete this.clonedTechnicianIds[index]; - } } diff --git a/kdb-web/src/app/modules/shared/components/config-list/config-list.component.html b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.html new file mode 100644 index 00000000..4e3ea7d2 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.html @@ -0,0 +1,45 @@ +
+
+
+ {{translationKey | translate}}: +
+
+ + +
+
+ +
+
+
+ + + + + + + + + {{value.value}} + + + + +
+ + + + + +
+ + +
+
+
+
+
+
diff --git a/kdb-web/src/app/modules/shared/components/config-list/config-list.component.scss b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/shared/components/config-list/config-list.component.spec.ts b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.spec.ts new file mode 100644 index 00000000..b571849c --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConfigListComponent } from './config-list.component'; + +describe('ConfigListComponent', () => { + let component: ConfigListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ConfigListComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ConfigListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/shared/components/config-list/config-list.component.ts b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.ts new file mode 100644 index 00000000..7fc92215 --- /dev/null +++ b/kdb-web/src/app/modules/shared/components/config-list/config-list.component.ts @@ -0,0 +1,72 @@ +import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { Table } from "primeng/table"; + +@Component({ + selector: "app-config-list", + templateUrl: "./config-list.component.html", + styleUrls: ["./config-list.component.scss"] +}) +export class ConfigListComponent { + internal_data: any[] = []; + + @Input() translationKey: string = ""; + + @Input() + set data(val: any[]) { + this.dataChange.emit(val); + let id = 0; + this.internal_data = val.map(value => { + value = { id: id, value: value }; + id++; + return value; + }); + } + + get data() { + return this.getData(); + } + + @Output() dataChange: EventEmitter = new EventEmitter(); + clonedData: { [s: number]: any } = {}; + + private getData(): any[] { + return this.internal_data.map(value => { + return value.value; + }); + } + + addNew(table: Table) { + const id = Math.max.apply(Math, this.internal_data.map(value => { + return value.id ?? 0; + })) + 1; + const newItem = { id: id, value: "" }; + this.internal_data.push(newItem); + + table.initRowEdit(newItem); + const index = this.internal_data.findIndex(l => l.id == newItem.id); + this.editInit(newItem, index); + } + + editInit(value: any, index: number) { + this.clonedData[index] = { ...value }; + } + + delete(index: number) { + this.internal_data.splice(index, 1); + this.dataChange.emit(this.getData()); + } + + editSave(value: any, index: number) { + if (!value.value || this.internal_data[index] == this.clonedData[index]) { + return; + } + + delete this.clonedData[index]; + this.dataChange.emit(this.getData()); + } + + editCancel(index: number) { + this.internal_data[index] = this.clonedData[index]; + delete this.clonedData[index]; + } +} diff --git a/kdb-web/src/app/modules/shared/shared.module.ts b/kdb-web/src/app/modules/shared/shared.module.ts index 61a5db6c..b92aa75b 100644 --- a/kdb-web/src/app/modules/shared/shared.module.ts +++ b/kdb-web/src/app/modules/shared/shared.module.ts @@ -25,6 +25,7 @@ import { ImageModule } from "primeng/image"; import { SidebarModule } from "primeng/sidebar"; import { HistoryBtnComponent } from './components/history-btn/history-btn.component'; import { DataViewModule, DataViewLayoutOptions } from 'primeng/dataview'; +import { ConfigListComponent } from './components/config-list/config-list.component'; @NgModule({ @@ -33,6 +34,7 @@ import { DataViewModule, DataViewLayoutOptions } from 'primeng/dataview'; IpAddressPipe, BoolPipe, HistoryBtnComponent, + ConfigListComponent, ], imports: [ CommonModule, @@ -59,34 +61,35 @@ import { DataViewModule, DataViewLayoutOptions } from 'primeng/dataview'; SidebarModule, DataViewModule, ], - exports: [ - ButtonModule, - PasswordModule, - MenuModule, - DialogModule, - ProgressSpinnerModule, - HttpClientModule, - FormsModule, - ReactiveFormsModule, - ToastModule, - ConfirmDialogModule, - TableModule, - InputTextModule, - CheckboxModule, - DropdownModule, - TranslateModule, - DynamicDialogModule, - PanelMenuModule, - PanelModule, - AuthRolePipe, - IpAddressPipe, - BoolPipe, - InputNumberModule, - ImageModule, - SidebarModule, - HistoryBtnComponent, - DataViewModule, - DataViewLayoutOptions - ] + exports: [ + ButtonModule, + PasswordModule, + MenuModule, + DialogModule, + ProgressSpinnerModule, + HttpClientModule, + FormsModule, + ReactiveFormsModule, + ToastModule, + ConfirmDialogModule, + TableModule, + InputTextModule, + CheckboxModule, + DropdownModule, + TranslateModule, + DynamicDialogModule, + PanelMenuModule, + PanelModule, + AuthRolePipe, + IpAddressPipe, + BoolPipe, + InputNumberModule, + ImageModule, + SidebarModule, + HistoryBtnComponent, + DataViewModule, + DataViewLayoutOptions, + ConfigListComponent + ] }) export class SharedModule { } diff --git a/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts b/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts index 114d1d8a..6a65d21c 100644 --- a/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts +++ b/kdb-web/src/app/modules/view/server/achievements/components/achievement/achievement.component.ts @@ -236,7 +236,6 @@ export class AchievementComponent implements OnInit, OnDestroy { ).pipe(catchError(err => { this.isEditingNew = false; this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.achievements.message.achievement_create_failed"), this.translate.instant("view.server.achievements.message.achievement_create_failed_d")); return throwError(err); })).subscribe(result => { this.isEditingNew = false; @@ -258,7 +257,6 @@ export class AchievementComponent implements OnInit, OnDestroy { } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.achievements.message.achievement_update_failed"), this.translate.instant("view.server.achievements.message.achievement_update_failed_d", { name: newAchievement.name })); return throwError(err); })).subscribe(_ => { this.spinner.hideSpinner(); @@ -290,7 +288,6 @@ export class AchievementComponent implements OnInit, OnDestroy { } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.achievements.message.achievement_delete_failed"), this.translate.instant("view.server.achievements.message.achievement_delete_failed_d", { name: achievement.name })); return throwError(err); })).subscribe(l => { this.spinner.hideSpinner(); diff --git a/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts b/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts index 5fab0523..25257687 100644 --- a/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts +++ b/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles-rules/auto-roles-rules.component.ts @@ -222,7 +222,6 @@ export class AutoRolesRulesComponent implements OnInit, OnDestroy { ).pipe(catchError(err => { this.isEditingNew = false; this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_create_failed"), this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_create_failed_d")); return throwError(err); })).subscribe(result => { this.isEditingNew = false; @@ -241,7 +240,6 @@ export class AutoRolesRulesComponent implements OnInit, OnDestroy { } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_update_failed"), this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_update_failed_d")); return throwError(err); })).subscribe(result => { this.spinner.hideSpinner(); @@ -272,7 +270,6 @@ export class AutoRolesRulesComponent implements OnInit, OnDestroy { } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_delete_failed"), this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_delete_failed_d", { id: autoRoleRule.id })); return throwError(err); })).subscribe(_ => { this.spinner.hideSpinner(); diff --git a/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.ts b/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.ts index 77f249ad..876f3b4a 100644 --- a/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.ts +++ b/kdb-web/src/app/modules/view/server/auto-role/components/auto-roles/auto-roles.component.ts @@ -208,7 +208,6 @@ export class AutoRolesComponent implements OnInit, OnDestroy { ).pipe(catchError(err => { this.isEditingNew = false; this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.auto_roles.message.auto_role_create_failed"), this.translate.instant("view.server.auto_roles.message.auto_role_create_failed_d")); return throwError(err); })).subscribe(result => { this.isEditingNew = false; @@ -240,7 +239,6 @@ export class AutoRolesComponent implements OnInit, OnDestroy { } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.auto_roles.message.auto_role_delete_failed"), this.translate.instant("view.server.auto_roles.message.auto_role_delete_failed_d", { id: autoRole.id })); return throwError(err); })).subscribe(_ => { this.spinner.hideSpinner(); diff --git a/kdb-web/src/app/modules/view/server/config/components/config/config.component.html b/kdb-web/src/app/modules/view/server/config/components/config/config.component.html new file mode 100644 index 00000000..9f7701c1 --- /dev/null +++ b/kdb-web/src/app/modules/view/server/config/components/config/config.component.html @@ -0,0 +1,123 @@ +

+ {{'view.server.config.header' | translate}} +

+ +
+
+

+ {{'view.server.config.bot.header' | translate}} +

+
+ +
+
+
+
{{'view.server.config.bot.message_delete_timer' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.notification_chat_id' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.max_voice_state_hours' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.xp_per_message' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.xp_per_reaction' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.max_message_xp_per_hour' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.xp_per_ontime_hour' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.xp_per_event_participation' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.xp_per_achievement' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.afk_command_channel_id' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.help_voice_channel_id' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.team_channel_id' | translate}}:
+ +
+
+ +
+
+
{{'view.server.config.bot.login_message_channel_id' | translate}}:
+ +
+
+ +
+ + + + +
+ +
+ +
+
diff --git a/kdb-web/src/app/modules/view/server/config/components/config/config.component.scss b/kdb-web/src/app/modules/view/server/config/components/config/config.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/view/server/config/components/config/config.component.spec.ts b/kdb-web/src/app/modules/view/server/config/components/config/config.component.spec.ts new file mode 100644 index 00000000..94dd19ba --- /dev/null +++ b/kdb-web/src/app/modules/view/server/config/components/config/config.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConfigComponent } from './config.component'; + +describe('ConfigComponent', () => { + let component: ConfigComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ConfigComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ConfigComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/view/server/config/components/config/config.component.ts b/kdb-web/src/app/modules/view/server/config/components/config/config.component.ts new file mode 100644 index 00000000..0de50e7c --- /dev/null +++ b/kdb-web/src/app/modules/view/server/config/components/config/config.component.ts @@ -0,0 +1,116 @@ +import { Component, OnInit } from "@angular/core"; +import { FormBuilder } from "@angular/forms"; +import { TranslateService } from "@ngx-translate/core"; +import { catchError } from "rxjs/operators"; +import { GuiService } from "src/app/services/gui/gui.service"; +import { SettingsService } from "src/app/services/settings/settings.service"; +import { SpinnerService } from "src/app/services/spinner/spinner.service"; +import { ToastService } from "src/app/services/toast/toast.service"; +import { throwError } from "rxjs"; +import { Query, ServerConfigQuery } from "../../../../../../models/graphql/query.model"; +import { Queries } from "../../../../../../models/graphql/queries.model"; +import { DataService } from "../../../../../../services/data/data.service"; +import { ServerConfigMutationResult } from "../../../../../../models/graphql/result.model"; +import { Mutations } from "../../../../../../models/graphql/mutations.model"; +import { AuthService } from "../../../../../../services/auth/auth.service"; +import { ServerConfig } from "../../../../../../models/config/server-config.model"; +import { Server } from "../../../../../../models/data/server.model"; +import { ActivatedRoute } from "@angular/router"; +import { Table } from "primeng/table"; + +type AFKChannelId = { + id: number; + value: string; +}; + +@Component({ + selector: "app-config", + templateUrl: "./config.component.html", + styleUrls: ["./config.component.scss"] +}) +export class ConfigComponent implements OnInit { + config: ServerConfig = { + messageDeleteTimer: 0, + afkChannelIds: [], + moderatorRoleIds: [], + adminRoleIds: [] + }; + + + afkChannelIds: AFKChannelId[] = []; + clonedAfkChannelIds: { [s: number]: AFKChannelId } = {}; + + private server: Server = {}; + + constructor( + private data: DataService, + private settingsService: SettingsService, + private spinnerService: SpinnerService, + private guiService: GuiService, + private formBuilder: FormBuilder, + private toastService: ToastService, + private translate: TranslateService, + private authService: AuthService, + private spinner: SpinnerService, + private route: ActivatedRoute + ) { + } + + ngOnInit(): void { + this.spinnerService.showSpinner(); + + this.data.getServerFromRoute(this.route).then(async server => { + this.server = server; + this.loadConfig(); + }); + } + + loadConfig() { + this.data.query(Queries.serverConfigQuery, { + serverId: this.server.id + }, + (data: Query) => { + return data.servers[0]; + }).subscribe(data => { + this.config = data.config; + + let id = 0; + for (const afkChannelId of this.config.afkChannelIds ?? []) { + this.afkChannelIds.push({ id: id, value: afkChannelId }); + id++; + } + + this.spinnerService.hideSpinner(); + }); + } + + saveServerConfig() { + this.spinner.showSpinner(); + this.data.mutation(Mutations.updateServerConfig, { + id: this.config.id, + messageDeleteTimer: this.config.messageDeleteTimer, + notificationChatId: this.config.notificationChatId, + maxVoiceStateHours: this.config.maxVoiceStateHours, + xpPerMessage: this.config.xpPerMessage, + xpPerReaction: this.config.xpPerReaction, + maxMessageXpPerHour: this.config.maxMessageXpPerHour, + xpPerOntimeHour: this.config.xpPerOntimeHour, + xpPerEventParticipation: this.config.xpPerEventParticipation, + xpPerAchievement: this.config.xpPerAchievement, + afkCommandChannelId: this.config.afkCommandChannelId, + helpVoiceChannelId: this.config.helpVoiceChannelId, + teamChannelId: this.config.teamChannelId, + loginMessageChannelId: this.config.loginMessageChannelId, + afkChannelIds: this.config.afkChannelIds, + moderatorRoleIds: this.config.moderatorRoleIds, + adminRoleIds: this.config.adminRoleIds + } + ).pipe(catchError(err => { + this.spinner.hideSpinner(); + return throwError(err); + })).subscribe(result => { + this.spinner.hideSpinner(); + this.toastService.success(this.translate.instant("view.server.config.message.technician_config_create"), this.translate.instant("view.server.config.message.technician_config_create_d")); + }); + } +} diff --git a/kdb-web/src/app/modules/view/server/config/config-routing.module.ts b/kdb-web/src/app/modules/view/server/config/config-routing.module.ts new file mode 100644 index 00000000..afbbd84c --- /dev/null +++ b/kdb-web/src/app/modules/view/server/config/config-routing.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ConfigComponent } from "./components/config/config.component"; + +const routes: Routes = [ + {path: '', component: ConfigComponent}, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ConfigRoutingModule { } diff --git a/kdb-web/src/app/modules/view/server/config/config.module.ts b/kdb-web/src/app/modules/view/server/config/config.module.ts new file mode 100644 index 00000000..0dd2131b --- /dev/null +++ b/kdb-web/src/app/modules/view/server/config/config.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ConfigRoutingModule } from './config-routing.module'; +import { ConfigComponent } from './components/config/config.component'; +import { SharedModule } from "../../../shared/shared.module"; + + +@NgModule({ + declarations: [ + ConfigComponent + ], + imports: [ + CommonModule, + ConfigRoutingModule, + SharedModule, + ] +}) +export class ConfigModule { } diff --git a/kdb-web/src/app/modules/view/server/levels/components/levels/levels.component.ts b/kdb-web/src/app/modules/view/server/levels/components/levels/levels.component.ts index ae4e48ce..63e1e446 100644 --- a/kdb-web/src/app/modules/view/server/levels/components/levels/levels.component.ts +++ b/kdb-web/src/app/modules/view/server/levels/components/levels/levels.component.ts @@ -196,7 +196,6 @@ export class LevelsComponent implements OnInit, OnDestroy { ).pipe(catchError(err => { this.isEditingNew = false; this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.levels.message.level_create_failed"), this.translate.instant("view.server.levels.message.level_create_failed_d")); return throwError(err); })).subscribe(result => { this.isEditingNew = false; @@ -217,7 +216,6 @@ export class LevelsComponent implements OnInit, OnDestroy { } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.levels.message.level_update_failed"), this.translate.instant("view.server.levels.message.level_update_failed_d", { name: newLevel.name })); return throwError(err); })).subscribe(_ => { this.spinner.hideSpinner(); @@ -249,7 +247,6 @@ export class LevelsComponent implements OnInit, OnDestroy { } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.levels.message.level_delete_failed"), this.translate.instant("view.server.levels.message.level_delete_failed_d", { name: level.name })); return throwError(err); })).subscribe(l => { this.spinner.hideSpinner(); diff --git a/kdb-web/src/app/modules/view/server/members/members.component.ts b/kdb-web/src/app/modules/view/server/members/members.component.ts index 9cf115d1..e46afb61 100644 --- a/kdb-web/src/app/modules/view/server/members/members.component.ts +++ b/kdb-web/src/app/modules/view/server/members/members.component.ts @@ -237,7 +237,6 @@ export class MembersComponent implements OnInit, OnDestroy { } ).pipe(catchError(err => { this.spinner.hideSpinner(); - this.toastService.error(this.translate.instant("view.server.members.message.user_change_failed"), this.translate.instant("view.server.members.message.user_change_failed_d", { name: newUser.name })); return throwError(err); })).subscribe(_ => { this.spinner.hideSpinner(); diff --git a/kdb-web/src/app/modules/view/server/server-routing.module.ts b/kdb-web/src/app/modules/view/server/server-routing.module.ts index a83dab96..c5626e9b 100644 --- a/kdb-web/src/app/modules/view/server/server-routing.module.ts +++ b/kdb-web/src/app/modules/view/server/server-routing.module.ts @@ -10,7 +10,8 @@ const routes: Routes = [ { path: "members/:memberId", component: ProfileComponent }, { path: "auto-roles", loadChildren: () => import("./auto-role/auto-role.module").then(m => m.AutoRoleModule) }, { path: "levels", loadChildren: () => import("./levels/levels.module").then(m => m.LevelsModule) }, - { path: "achievements", loadChildren: () => import("./achievements/achievements.module").then(m => m.AchievementsModule) } + { path: "achievements", loadChildren: () => import("./achievements/achievements.module").then(m => m.AchievementsModule) }, + { path: "config", loadChildren: () => import("./config/config.module").then(m => m.ConfigModule) } ]; @NgModule({ diff --git a/kdb-web/src/app/services/data/data.service.ts b/kdb-web/src/app/services/data/data.service.ts index 54d13057..9d7d13b3 100644 --- a/kdb-web/src/app/services/data/data.service.ts +++ b/kdb-web/src/app/services/data/data.service.ts @@ -10,6 +10,8 @@ import { Query } from "../../models/graphql/query.model"; import { SidebarService } from "../sidebar/sidebar.service"; import { SpinnerService } from "../spinner/spinner.service"; import { GraphQLResult } from "../../models/graphql/result.model"; +import { ToastService } from "../toast/toast.service"; +import { TranslateService } from "@ngx-translate/core"; @Injectable({ providedIn: "root" @@ -21,7 +23,9 @@ export class DataService { private http: HttpClient, private sidebar: SidebarService, private spinner: SpinnerService, - private router: Router + private router: Router, + private toast: ToastService, + private translate: TranslateService, ) { } @@ -72,6 +76,10 @@ export class DataService { }) .pipe(map(d => { if (d.errors && d.errors.length > 0) { + d.errors.forEach((error: Error) => { + this.toast.error(this.translate.instant("common.error"), error.message) + }); + throw new Error(d.errors.toString()); } return d.data; diff --git a/kdb-web/src/app/services/sidebar/sidebar.service.ts b/kdb-web/src/app/services/sidebar/sidebar.service.ts index 81ab4548..196b7e25 100644 --- a/kdb-web/src/app/services/sidebar/sidebar.service.ts +++ b/kdb-web/src/app/services/sidebar/sidebar.service.ts @@ -3,7 +3,7 @@ import { MenuItem } from "primeng/api"; import { BehaviorSubject } from "rxjs"; import { AuthRoles } from "../../models/auth/auth-roles.enum"; import { AuthService } from "../auth/auth.service"; -import { LangChangeEvent, TranslateService } from "@ngx-translate/core"; +import { TranslateService } from "@ngx-translate/core"; import { NavigationEnd, Router } from "@angular/router"; import { ThemeService } from "../theme/theme.service"; import { Server } from "../../models/data/server.model"; @@ -25,6 +25,7 @@ export class SidebarService { serverAutoRoles: MenuItem = {}; serverLevels: MenuItem = {}; serverAchievements: MenuItem = {}; + serverConfig: MenuItem = {}; serverMenu: MenuItem = {}; adminConfig: MenuItem = {}; adminUsers: MenuItem = {}; @@ -110,12 +111,19 @@ export class SidebarService { routerLink: `server/${this.server$.value?.id}/achievements` }; + this.serverConfig = { + label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.config") : "", + icon: "pi pi-cog", + visible: true, + routerLink: `server/${this.server$.value?.id}/config` + }; + this.serverMenu = { label: this.isSidebarOpen ? this.server$.value?.name : "", icon: "pi pi-server", visible: false, expanded: true, - items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles, this.serverLevels, this.serverAchievements] + items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles, this.serverLevels, this.serverAchievements, this.serverConfig] }; this.adminConfig = { label: this.isSidebarOpen ? this.translateService.instant("sidebar.config") : "", @@ -151,6 +159,7 @@ export class SidebarService { this.serverAutoRoles.visible = !!user?.isModerator; this.serverLevels.visible = !!user?.isModerator; this.serverAchievements.visible = !!user?.isModerator; + this.serverConfig.visible = !!user?.isAdmin; } else { this.serverMenu.visible = false; } diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json index e499fedf..f346df54 100644 --- a/kdb-web/src/assets/i18n/de.json +++ b/kdb-web/src/assets/i18n/de.json @@ -37,7 +37,7 @@ "header": "Bot", "help_url": "Befehlsreferenz", "ping_urls": "Ping Adressen", - "technicianIds": "Techniker Ids", + "technician_ids": "Techniker Ids", "wait_for_restart": "Wartezeit vor Neustart", "wait_for_shutdown": "Wartezeit vor Herunterfahren" }, @@ -156,7 +156,7 @@ "id": "Id", "leftServer": "Server verlassen", "messageId": "Nachricht Id", - "minXp": "Min. XP", + "min_xp": "Min. XP", "name": "Name", "operator": "Operator", "permissions": "Berechtigung", @@ -407,6 +407,28 @@ } } }, + "config": { + "bot": { + "admin_roles": "Admin Rollen", + "afk_channels": "AFK Sprachkanäle", + "afk_command_channel_id": "AFK Kanal für den Befehl /afk", + "header": "Bot Konfiguration", + "help_voice_channel_id": "Sprachkanal für Hilfsbenachrichtung", + "login_message_channel_id": "Kanal für die Nachricht vom Bot nach Start", + "max_message_xp_per_hour": "Maximale XP pro Stunde durch Nachrichten", + "max_voice_state_hours": "Maximale Stunden für eine ontime nach Bot neustart", + "message_delete_timer": "Zeit bis zum löschen einer Botnachricht in sekunden", + "moderator_roles": "Moderator Rollen", + "notification_chat_id": "Benachrichtungskanal", + "team_channel_id": "Team chat", + "xp_per_achievement": "XP für Errungenschaft", + "xp_per_event_participation": "XP für Event Teilnahme", + "xp_per_message": "XP für eine Nachricht", + "xp_per_ontime_hour": "XP für eine Stunde im Sprachkanal", + "xp_per_reaction": "XP für eine Reaktion" + }, + "header": "Server Konfiguration" + }, "dashboard": { "deleted_message_count": "Gelöschte Nachrichten", "header": "Server dashboard", diff --git a/kdb-web/src/assets/i18n/en.json b/kdb-web/src/assets/i18n/en.json index 3d709daa..7dd4fa29 100644 --- a/kdb-web/src/assets/i18n/en.json +++ b/kdb-web/src/assets/i18n/en.json @@ -37,7 +37,7 @@ "header": "Bot", "help_url": "Help URL", "ping_urls": "Ping addresses", - "technicianIds": "Technician Ids", + "technician_ids": "Technician Ids", "wait_for_restart": "Time to wait before restart", "wait_for_shutdown": "Time to wait before shutdown" }, @@ -156,7 +156,7 @@ "id": "Id", "leftServer": "Left server", "messageId": "Message Id", - "minXp": "Min. XP", + "min_xp": "Min. XP", "name": "Name", "operator": "Operator", "permissions": "Permissions", @@ -407,6 +407,28 @@ } } }, + "config": { + "bot": { + "admin_roles": "Admin Roles", + "afk_channels": "AFK Voicechannel", + "afk_command_channel_id": "AFK Channel for the command /afk", + "header": "Bot configuration", + "help_voice_channel_id": "Voicechannel für help notifications", + "login_message_channel_id": "Channel for bot message after start", + "max_message_xp_per_hour": "Max xp per hour with message", + "max_voice_state_hours": "Max ontime hours after bot restart", + "message_delete_timer": "Time to wait before delete bot messages", + "moderator_roles": "Moderator roles", + "notification_chat_id": "Notification channel", + "team_channel_id": "Team chat", + "xp_per_achievement": "XP for achievement", + "xp_per_event_participation": "XP for event participation", + "xp_per_message": "XP for message", + "xp_per_ontime_hour": "XP for one hour in an voice channel", + "xp_per_reaction": "XP for an reaction" + }, + "header": "Server configuration" + }, "dashboard": { "deleted_message_count": "Deleted messages", "header": "Server dashboard", @@ -487,7 +509,7 @@ "ontime": "Ontime", "permission_denied": "Access denied!", "permission_denied_d": "You have to be moderator to see other profiles!", - "xp": "Xp" + "xp": "XP" } }, "user_settings": {