Compare commits

...

8 Commits

Author SHA1 Message Date
42b71525bd Merge pull request '#133' (#224) from #133 into 1.0.0
Reviewed-on: sh-edraft.de/kd_discord_bot#224
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
Reviewed-by: Jonas <joni.drescher@web.de>
2023-02-22 19:29:16 +01:00
b9d33c5fd0 Merge branch '1.0.0' into #133 2023-02-21 22:45:30 +01:00
9ea1b14852 Optimized code #133 2023-02-21 22:44:42 +01:00
23ee963d65 Fixed formattings #133 2023-02-21 17:29:08 +01:00
f3024d2ea5 Fixed changes of rebase #133 2023-02-21 17:24:18 +01:00
Jonas Drescher
e463b19a73 added level table 2023-02-21 17:11:56 +01:00
15214b1c99 Fixed user filter #133 2023-02-21 17:10:34 +01:00
Jonas Drescher
dfcd958965 WIP: levels table 2023-02-21 17:10:32 +01:00
28 changed files with 777 additions and 96 deletions

View File

@ -33,9 +33,9 @@ class AutoRoleMutation(QueryABC):
def get_new(x: AutoRole): def get_new(x: AutoRole):
return ( return (
x.server.id == input["serverId"] x.server.id == int(input["serverId"])
and x.discord_channel_id == input["channelId"] and x.discord_channel_id == int(input["channelId"])
and x.discord_message_id == input["messageId"] and x.discord_message_id == int(input["messageId"])
) )
return self._auto_roles.get_auto_roles_by_server_id(auto_role.server.id).where(get_new).last() return self._auto_roles.get_auto_roles_by_server_id(auto_role.server.id).where(get_new).last()

View File

@ -1,6 +1,6 @@
{ {
"name": "kdb-web", "name": "kdb-web",
"version": "1.0.0", "version": "1.0.dev133",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"update-version": "ts-node-esm update-version.ts", "update-version": "ts-node-esm update-version.ts",

View File

@ -1,29 +1,27 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { PrimeNGConfig } from "primeng/api"; import { PrimeNGConfig } from "primeng/api";
import { AuthService } from "./services/auth/auth.service"; import { AuthService } from "./services/auth/auth.service";
import { SocketService } from "./services/socket/socket.service"; import { SocketService } from "./services/socket/socket.service";
import { ThemeService } from "./services/theme/theme.service"; import { ThemeService } from "./services/theme/theme.service";
import { ActivatedRoute, Router } from "@angular/router"; import { Subject } from "rxjs";
import { SpinnerService } from "./services/spinner/spinner.service"; import { Themes } from "./models/view/themes.enum";
import { DataService } from "./services/data/data.service"; import { takeUntil } from "rxjs/operators";
import { SidebarService } from "./services/sidebar/sidebar.service";
import { Server } from "./models/data/server.model";
import { Queries } from "./models/graphql/queries.model";
import { Query } from "./models/graphql/query.model";
@Component({ @Component({
selector: "app-root", selector: "app-root",
templateUrl: "./app.component.html", templateUrl: "./app.component.html",
styleUrls: ["./app.component.scss"] styleUrls: ["./app.component.scss"]
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit, OnDestroy {
themeName!: string; themeName: string = Themes.Default;
sidebarWidth!: string; sidebarWidth: string = '175px';
isLoggedIn: boolean = false; isLoggedIn: boolean = false;
private unsubscriber = new Subject<void>();
constructor( constructor(
private authService: AuthService, private authService: AuthService,
private themeService: ThemeService, private themeService: ThemeService,
@ -31,13 +29,19 @@ export class AppComponent implements OnInit {
private translateService: TranslateService, private translateService: TranslateService,
private config: PrimeNGConfig, private config: PrimeNGConfig,
) { ) {
this.themeService.sidebarWidth$.subscribe(value => { this.themeService.sidebarWidth$.pipe(
takeUntil(this.unsubscriber)
).subscribe(value => {
this.sidebarWidth = value; this.sidebarWidth = value;
}); });
this.themeService.themeName$.subscribe(value => { this.themeService.themeName$.pipe(
takeUntil(this.unsubscriber)
).subscribe(value => {
this.themeName = value; this.themeName = value;
}); });
this.authService.isLoggedIn$.subscribe(value => { this.authService.isLoggedIn$.pipe(
takeUntil(this.unsubscriber)
).subscribe(value => {
this.isLoggedIn = value; this.isLoggedIn = value;
}); });
} }
@ -49,6 +53,11 @@ export class AppComponent implements OnInit {
this.socket.startSocket(); this.socket.startSocket();
} }
ngOnDestroy() {
this.unsubscriber.next();
this.unsubscriber.unsubscribe();
}
loadLang(): void { loadLang(): void {
let lang = localStorage.getItem(`default_lang`); let lang = localStorage.getItem(`default_lang`);
if (!lang) { if (!lang) {

View File

@ -1,19 +1,21 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { MenuItem } from "primeng/api"; import { MenuItem } from "primeng/api";
import { AuthService } from "src/app/services/auth/auth.service"; import { AuthService } from "src/app/services/auth/auth.service";
import { ThemeService } from "src/app/services/theme/theme.service"; import { ThemeService } from "src/app/services/theme/theme.service";
import { SidebarService } from "../../services/sidebar/sidebar.service"; import { SidebarService } from "../../services/sidebar/sidebar.service";
import { Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
@Component({ @Component({
selector: "app-sidebar", selector: "app-sidebar",
templateUrl: "./sidebar.component.html", templateUrl: "./sidebar.component.html",
styleUrls: ["./sidebar.component.scss"] styleUrls: ["./sidebar.component.scss"]
}) })
export class SidebarComponent implements OnInit { export class SidebarComponent implements OnInit, OnDestroy {
isSidebarOpen!: boolean; menuItems: MenuItem[]= [];
menuItems!: MenuItem[]; private unsubscriber = new Subject<void>();
constructor( constructor(
private authService: AuthService, private authService: AuthService,
@ -21,7 +23,9 @@ export class SidebarComponent implements OnInit {
private themeService: ThemeService, private themeService: ThemeService,
private sidebar: SidebarService private sidebar: SidebarService
) { ) {
this.sidebar.menuItems$.subscribe(value => { this.sidebar.menuItems$.pipe(
takeUntil(this.unsubscriber)
).subscribe(value => {
this.menuItems = value; this.menuItems = value;
}); });
} }
@ -30,4 +34,9 @@ export class SidebarComponent implements OnInit {
this.themeService.loadMenu(); this.themeService.loadMenu();
} }
ngOnDestroy() {
this.unsubscriber.next();
this.unsubscriber.complete();
}
} }

View File

@ -1,4 +1,4 @@
export interface Data { export interface Data {
createdAt: string; createdAt?: string;
modifiedAt: string; modifiedAt?: string;
} }

View File

@ -2,9 +2,9 @@ export interface Guild {
id?: string; id?: string;
name?: string; name?: string;
channels: [Channel] channels: Channel[];
roles: [Role] roles: Role[];
emojis: [Emoji] emojis: Emoji[];
} }
export interface Channel { export interface Channel {

View File

@ -1,5 +1,5 @@
import { Data } from "./data.model"; import {Data} from "./data.model";
import { Server, ServerFilter } from "./server.model"; import {Server, ServerFilter} from "./server.model";
export interface Level extends Data { export interface Level extends Data {
id?: number; id?: number;
@ -12,6 +12,6 @@ export interface Level extends Data {
export interface LevelFilter { export interface LevelFilter {
id?: number; id?: number;
name?: String; name?: string;
server?: ServerFilter; server?: ServerFilter;
} }

View File

@ -79,4 +79,46 @@ export class Mutations {
} }
} }
`; `;
static createLevel = `
mutation createLevel($name: String, $color: String, $minXp: Int, $permissions: String, $serverId: ID) {
level {
createLevel(input: { name: $name, color: $color, minXp: $minXp, permissions: $permissions, serverId: $serverId}) {
id
name
color
minXp
permissions
server {
id
}
}
}
}
`;
static updateLevel = `
mutation updateLevel($id: ID, $name: String, $color: String, $minXp: Int, $permissions: String) {
level {
updateLevel(input: { id: $id, name: $name, color: $color, minXp: $minXp, permissions: $permissions }) {
id
name
color
minXp
permissions
}
}
}
`;
static deleteLevel = `
mutation deleteLevel($id: ID) {
level {
deleteLevel(id: $id) {
id
name
}
}
}
`;
} }

View File

@ -58,6 +58,8 @@ export class Queries {
id id
name name
} }
createdAt
modifiedAt
} }
} }
`; `;

View File

@ -1,5 +1,6 @@
import { User } from "../data/user.model"; import { User } from "../data/user.model";
import { AutoRole, AutoRoleRule } from "../data/auto_role.model"; import { AutoRole, AutoRoleRule } from "../data/auto_role.model";
import { Level } from "../data/level.model";
export interface GraphQLResult { export interface GraphQLResult {
data: any; data: any;
@ -31,3 +32,11 @@ export interface AutoRoleRuleMutationResult {
deleteAutoRoleRule?: AutoRoleRule deleteAutoRoleRule?: AutoRoleRule
}; };
} }
export interface LevelMutationResult {
level: {
createLevel?: Level
updateLevel?: Level
deleteLevel?: Level
};
}

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { catchError, debounceTime } from "rxjs/operators"; import { catchError, debounceTime, takeUntil } from "rxjs/operators";
import { AuthRoles } from "src/app/models/auth/auth-roles.enum"; import { AuthRoles } from "src/app/models/auth/auth-roles.enum";
import { AuthUserDTO } from "src/app/models/auth/auth-user.dto"; import { AuthUserDTO } from "src/app/models/auth/auth-user.dto";
import { AuthService } from "src/app/services/auth/auth.service"; import { AuthService } from "src/app/services/auth/auth.service";
@ -13,7 +13,7 @@ import { ErrorDTO } from "src/app/models/error/error-dto";
import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; import { FormBuilder, FormControl, FormGroup } from "@angular/forms";
import { AuthUserSelectCriterion } from "src/app/models/selection/auth-user/auth-user-select-criterion.dto"; import { AuthUserSelectCriterion } from "src/app/models/selection/auth-user/auth-user-select-criterion.dto";
import { LazyLoadEvent } from "primeng/api"; import { LazyLoadEvent } from "primeng/api";
import { throwError } from "rxjs"; import { Subject, throwError } from "rxjs";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
@ -62,6 +62,8 @@ export class AuthUserComponent implements OnInit {
searchCriterions!: AuthUserSelectCriterion; searchCriterions!: AuthUserSelectCriterion;
totalRecords!: number; totalRecords!: number;
private unsubscriber = new Subject();
constructor( constructor(
private authService: AuthService, private authService: AuthService,
private spinnerService: SpinnerService, private spinnerService: SpinnerService,
@ -97,6 +99,7 @@ export class AuthUserComponent implements OnInit {
}); });
this.filterForm.valueChanges.pipe( this.filterForm.valueChanges.pipe(
takeUntil(this.unsubscriber),
debounceTime(600) debounceTime(600)
).subscribe(changes => { ).subscribe(changes => {
if (changes.firstName) { if (changes.firstName) {

View File

@ -1,30 +1,30 @@
import {Component, OnInit} from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; import { FormBuilder, FormControl, FormGroup } from "@angular/forms";
import {Router} from "@angular/router"; import { Router } from "@angular/router";
import {TranslateService} from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import {debounceTime, throwError} from "rxjs"; import { debounceTime, Subject, throwError } from "rxjs";
import {ConfirmationDialogService} from "src/app/services/confirmation-dialog/confirmation-dialog.service"; import { ConfirmationDialogService } from "src/app/services/confirmation-dialog/confirmation-dialog.service";
import {DataService} from "src/app/services/data/data.service"; import { DataService } from "src/app/services/data/data.service";
import {SpinnerService} from "src/app/services/spinner/spinner.service"; import { SpinnerService } from "src/app/services/spinner/spinner.service";
import {ToastService} from "src/app/services/toast/toast.service"; import { ToastService } from "src/app/services/toast/toast.service";
import {Server, ServerFilter} from "../../../../../models/data/server.model"; import { Server, ServerFilter } from "../../../../../models/data/server.model";
import {catchError} from "rxjs/operators"; import { catchError, takeUntil } from "rxjs/operators";
import {Queries} from "../../../../../models/graphql/queries.model"; import { Queries } from "../../../../../models/graphql/queries.model";
import {Page} from "../../../../../models/graphql/filter/page.model"; import { Page } from "../../../../../models/graphql/filter/page.model";
import {Sort} from "../../../../../models/graphql/filter/sort.model"; import { Sort } from "../../../../../models/graphql/filter/sort.model";
import {Query} from "../../../../../models/graphql/query.model"; import { Query } from "../../../../../models/graphql/query.model";
import {SidebarService} from "../../../../../services/sidebar/sidebar.service"; import { SidebarService } from "../../../../../services/sidebar/sidebar.service";
@Component({ @Component({
selector: "app-dashboard", selector: "app-dashboard",
templateUrl: "./dashboard.component.html", templateUrl: "./dashboard.component.html",
styleUrls: ["./dashboard.component.scss"] styleUrls: ["./dashboard.component.scss"]
}) })
export class DashboardComponent implements OnInit { export class DashboardComponent implements OnInit, OnDestroy {
servers: Server[] = []; servers: Server[] = [];
totalRecords!: number; totalRecords: number = 0;
filter: ServerFilter = {}; filter: ServerFilter = {};
@ -33,12 +33,14 @@ export class DashboardComponent implements OnInit {
pageSize: 10 pageSize: 10
}; };
sort!: Sort; sort: Sort = {};
filterForm!: FormGroup<{ filterForm!: FormGroup<{
name: FormControl<string | null>, name: FormControl<string | null>,
}>; }>;
private unsubscriber = new Subject<void>();
constructor( constructor(
private data: DataService, private data: DataService,
private spinnerService: SpinnerService, private spinnerService: SpinnerService,
@ -58,12 +60,18 @@ export class DashboardComponent implements OnInit {
this.loadNextPage(); this.loadNextPage();
} }
public ngOnDestroy() {
this.unsubscriber.next();
this.unsubscriber.complete();
}
setFilterForm() { setFilterForm() {
this.filterForm = this.fb.group({ this.filterForm = this.fb.group({
name: new FormControl<string | null>(null) name: new FormControl<string | null>(null)
}); });
this.filterForm.valueChanges.pipe( this.filterForm.valueChanges.pipe(
takeUntil(this.unsubscriber),
debounceTime(600) debounceTime(600)
).subscribe(async changes => { ).subscribe(async changes => {
if (changes.name == "") { if (changes.name == "") {

View File

@ -17,11 +17,11 @@ import { SidebarService } from "../../../../../../services/sidebar/sidebar.servi
import { AutoRoleRuleQuery, SingleDiscordQuery } from "../../../../../../models/graphql/query.model"; import { AutoRoleRuleQuery, SingleDiscordQuery } from "../../../../../../models/graphql/query.model";
import { Queries } from "../../../../../../models/graphql/queries.model"; import { Queries } from "../../../../../../models/graphql/queries.model";
import { Server } from "../../../../../../models/data/server.model"; import { Server } from "../../../../../../models/data/server.model";
import { catchError, debounceTime } from "rxjs/operators"; import { catchError, debounceTime, takeUntil } from "rxjs/operators";
import { Table } from "primeng/table"; import { Table } from "primeng/table";
import { AutoRoleMutationResult, AutoRoleRuleMutationResult } from "../../../../../../models/graphql/result.model"; import { AutoRoleMutationResult, AutoRoleRuleMutationResult } from "../../../../../../models/graphql/result.model";
import { Mutations } from "../../../../../../models/graphql/mutations.model"; import { Mutations } from "../../../../../../models/graphql/mutations.model";
import { throwError } from "rxjs"; import { Subject, throwError } from "rxjs";
@Component({ @Component({
selector: "app-auto-roles-rules", selector: "app-auto-roles-rules",
@ -30,8 +30,8 @@ import { throwError } from "rxjs";
}) })
export class AutoRolesRulesComponent implements OnInit { export class AutoRolesRulesComponent implements OnInit {
rules!: AutoRoleRule[]; rules: AutoRoleRule[] = [];
guild!: Guild; guild: Guild = { channels: [], emojis: [], roles: [] };
emojis: MenuItem[] = []; emojis: MenuItem[] = [];
roles: MenuItem[] = []; roles: MenuItem[] = [];
loading = true; loading = true;
@ -63,7 +63,8 @@ export class AutoRolesRulesComponent implements OnInit {
sortDirection: undefined sortDirection: undefined
}; };
totalRecords!: number; totalRecords: number = 0;
private unsubscriber = new Subject<void>();
constructor( constructor(
private authService: AuthService, private authService: AuthService,
@ -121,7 +122,7 @@ export class AutoRolesRulesComponent implements OnInit {
public loadNextPage(): void { public loadNextPage(): void {
this.loading = true; this.loading = true;
this.data.query<AutoRoleRuleQuery>(Queries.autoRoleRulesQuery, { this.data.query<AutoRoleRuleQuery>(Queries.autoRoleRulesQuery, {
id: this.sidebar.server$.value?.id, filter: this.filter, page: this.page, sort: this.sort serverId: this.sidebar.server$.value?.id, autoRoleId: this.autoRoleId, filter: this.filter, page: this.page, sort: this.sort
}, },
(x: { servers: Server[] }) => { (x: { servers: Server[] }) => {
if (!x.servers[0].autoRoles || x.servers[0].autoRoles?.length == 0) { if (!x.servers[0].autoRoles || x.servers[0].autoRoles?.length == 0) {
@ -145,6 +146,7 @@ export class AutoRolesRulesComponent implements OnInit {
}); });
this.filterForm.valueChanges.pipe( this.filterForm.valueChanges.pipe(
takeUntil(this.unsubscriber),
debounceTime(600) debounceTime(600)
).subscribe(changes => { ).subscribe(changes => {
if (changes.id) { if (changes.id) {
@ -212,13 +214,14 @@ export class AutoRolesRulesComponent implements OnInit {
roleId: newAutoRoleRule.roleId roleId: newAutoRoleRule.roleId
} }
).pipe(catchError(err => { ).pipe(catchError(err => {
this.isEditingNew = false;
this.spinner.hideSpinner(); 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")); 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); return throwError(err);
})).subscribe(result => { })).subscribe(result => {
this.isEditingNew = false;
this.spinner.hideSpinner(); this.spinner.hideSpinner();
this.toastService.success(this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_created"), this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_create_d", { id: result.autoRoleRule.createAutoRoleRule?.id })); this.toastService.success(this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_created"), this.translate.instant("view.server.auto_roles.rules.message.auto_role_rule_create_d", { id: result.autoRoleRule.createAutoRoleRule?.id }));
this.isEditingNew = false;
this.loadNextPage(); this.loadNextPage();
}); });
return; return;

View File

@ -14,11 +14,11 @@ import { SidebarService } from "../../../../../../services/sidebar/sidebar.servi
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { AutoRoleQuery, SingleDiscordQuery } from "../../../../../../models/graphql/query.model"; import { AutoRoleQuery, SingleDiscordQuery } from "../../../../../../models/graphql/query.model";
import { Queries } from "../../../../../../models/graphql/queries.model"; import { Queries } from "../../../../../../models/graphql/queries.model";
import { catchError, debounceTime } from "rxjs/operators"; import { catchError, debounceTime, takeUntil } from "rxjs/operators";
import { Table } from "primeng/table"; import { Table } from "primeng/table";
import { AutoRoleMutationResult } from "../../../../../../models/graphql/result.model"; import { AutoRoleMutationResult } from "../../../../../../models/graphql/result.model";
import { Mutations } from "../../../../../../models/graphql/mutations.model"; import { Mutations } from "../../../../../../models/graphql/mutations.model";
import { throwError } from "rxjs"; import { Subject, throwError } from "rxjs";
import { AutoRole, AutoRoleFilter } from "../../../../../../models/data/auto_role.model"; import { AutoRole, AutoRoleFilter } from "../../../../../../models/data/auto_role.model";
import { ChannelType, Guild } from "../../../../../../models/data/discord.model"; import { ChannelType, Guild } from "../../../../../../models/data/discord.model";
import { Server } from "../../../../../../models/data/server.model"; import { Server } from "../../../../../../models/data/server.model";
@ -29,9 +29,9 @@ import { Server } from "../../../../../../models/data/server.model";
styleUrls: ["./auto-roles.component.scss"] styleUrls: ["./auto-roles.component.scss"]
}) })
export class AutoRolesComponent implements OnInit { export class AutoRolesComponent implements OnInit {
auto_roles!: AutoRole[]; auto_roles: AutoRole[] = [];
guild!: Guild; guild: Guild = { channels: [], emojis: [], roles: [] };
channels!: MenuItem[]; channels: MenuItem[] = [];
loading = true; loading = true;
clonedUsers: { [s: string]: User; } = {}; clonedUsers: { [s: string]: User; } = {};
@ -61,6 +61,7 @@ export class AutoRolesComponent implements OnInit {
}; };
totalRecords!: number; totalRecords!: number;
private unsubscriber = new Subject<void>();
constructor( constructor(
private authService: AuthService, private authService: AuthService,
@ -123,6 +124,7 @@ export class AutoRolesComponent implements OnInit {
}); });
this.filterForm.valueChanges.pipe( this.filterForm.valueChanges.pipe(
takeUntil(this.unsubscriber),
debounceTime(600) debounceTime(600)
).subscribe(changes => { ).subscribe(changes => {
if (changes.id) { if (changes.id) {
@ -195,10 +197,12 @@ export class AutoRolesComponent implements OnInit {
messageId: newAutoRole.messageId messageId: newAutoRole.messageId
} }
).pipe(catchError(err => { ).pipe(catchError(err => {
this.isEditingNew = false;
this.spinner.hideSpinner(); 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")); 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); return throwError(err);
})).subscribe(result => { })).subscribe(result => {
this.isEditingNew = false;
this.spinner.hideSpinner(); this.spinner.hideSpinner();
this.toastService.success(this.translate.instant("view.server.auto_roles.message.auto_role_created"), this.translate.instant("view.server.auto_roles.message.auto_role_create_d", { id: result.autoRole.createAutoRole?.id })); this.toastService.success(this.translate.instant("view.server.auto_roles.message.auto_role_created"), this.translate.instant("view.server.auto_roles.message.auto_role_create_d", { id: result.autoRole.createAutoRole?.id }));
this.loadNextPage(); this.loadNextPage();

View File

@ -0,0 +1,214 @@
<h1>
{{'view.server.levels.header' | translate}}
</h1>
<div class="content-wrapper">
<div class="content">
<p-table #dt [value]="levels" dataKey="id" editMode="row" [rowHover]="true" [rows]="10"
[rowsPerPageOptions]="[10,25,50]" [paginator]="true" [loading]="loading" [totalRecords]="totalRecords"
[lazy]="true" (onLazyLoad)="nextPage($event)">
<ng-template pTemplate="caption">
<div class="table-caption">
<div class="table-caption-text">
<ng-container *ngIf="!loading">{{levels.length}} {{'view.server.levels.of' | translate}}
{{dt.totalRecords}}
</ng-container>
{{'view.server.levels.levels' | translate}}
</div>
<div class="table-caption-btn-wrapper btn-wrapper">
<button pButton label="{{'admin.auth_users.add' | translate}}" class="icon-btn btn"
icon="pi pi-user-plus" (click)="addLevel(dt)" [disabled]="isEditingNew">
</button>
<button pButton label="{{'view.server.levels.reset_filters' | translate}}" icon="pi pi-undo"
class="icon-btn btn" (click)="resetFilters()">
</button>
</div>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="id">
<div class="table-header-label">
<div class="table-header-text">{{'view.server.levels.headers.id' | translate}}</div>
<p-sortIcon field="id" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th pSortableColumn="name">
<div class="table-header-label">
<div class="table-header-text">{{'view.server.levels.headers.name' | translate}}</div>
<p-sortIcon field="name" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th pSortableColumn="color">
<div class="table-header-label">
<div class="table-header-text">{{'view.server.levels.headers.color' | translate}}</div>
<p-sortIcon field="color" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th pSortableColumn="minXp">
<div class="table-header-label">
<div class="table-header-text">{{'view.server.levels.headers.min_xp' | translate}}</div>
<p-sortIcon field="minXp" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th pSortableColumn="permissions">
<div class="table-header-label">
<div class="table-header-text">{{'view.server.levels.headers.permissions' | translate}}</div>
<p-sortIcon field="permissions" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th>
<div class="table-header-label">
<div class="table-header-text">{{'common.created_at' | translate}}</div>
</div>
</th>
<th>
<div class="table-header-label">
<div class="table-header-text">{{'common.modified_at' | translate}}</div>
</div>
</th>
<th>
<div class="table-header-label">
<div class="table-header-text">{{'view.server.levels.headers.actions' | translate}}</div>
</div>
</th>
</tr>
<tr>
<th class="table-header-small">
<form [formGroup]="filterForm">
<input type="text" pInputText formControlName="id"
placeholder="{{'view.server.levels.headers.id' | translate}}">
</form>
</th>
<th>
<form [formGroup]="filterForm">
<input type="text" pInputText formControlName="name"
placeholder="{{'view.server.levels.headers.name' | translate}}">
</form>
</th>
<th></th>
<th></th>
<th></th>
<th class="table-header-small-dropdown"></th>
<th class="table-header-small-dropdown"></th>
<th class="table-header-actions"></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-level let-editing="editing" let-ri="rowIndex">
<tr [pEditableRow]="level">
<td>
<p-cellEditor>
<ng-template pTemplate="input">
{{level.id}}
</ng-template>
<ng-template pTemplate="output">
{{level.id}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="level.name">
</ng-template>
<ng-template pTemplate="output">
{{level.name}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="level.color">
</ng-template>
<ng-template pTemplate="output">
{{level.color}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText min="0" type="number" [(ngModel)]="level.minXp">
</ng-template>
<ng-template pTemplate="output">
{{level.minXp}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText min="0" type="text" [(ngModel)]="level.permissions">
</ng-template>
<ng-template pTemplate="output">
{{level.permissions}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
{{level.createdAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
<ng-template pTemplate="output">
{{level.createdAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
{{level.modifiedAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
<ng-template pTemplate="output">
{{level.modifiedAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
</p-cellEditor>
</td>
<td>
<div class="btn-wrapper">
<button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-pencil"
(click)="onRowEditInit(dt, level, ri)"></button>
<button *ngIf="!editing" pButton class="btn icon-btn danger-icon-btn" icon="pi pi-trash"
(click)="deleteLevel(level)"></button>
<button *ngIf="editing" pButton pSaveEditableRow class="btn icon-btn"
icon="pi pi-check-circle" (click)="onRowEditSave(dt, level, ri)"></button>
<button *ngIf="editing" pButton pCancelEditableRow class="btn icon-btn danger-icon-btn"
icon="pi pi-times-circle" (click)="onRowEditCancel(ri)"></button>
</div>
</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr></tr>
<tr>
<td colspan="10">{{'view.server.levels.no_entries_found' | translate}}</td>
</tr>
<tr></tr>
</ng-template>
<ng-template pTemplate="paginatorleft">
</ng-template>
</p-table>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LevelsComponent } from './levels.component';
describe('LevelsComponent', () => {
let component: LevelsComponent;
let fixture: ComponentFixture<LevelsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LevelsComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(LevelsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,266 @@
import { Component, OnDestroy, OnInit } from "@angular/core";
import { AuthService } from "../../../../../../services/auth/auth.service";
import { SpinnerService } from "../../../../../../services/spinner/spinner.service";
import { ToastService } from "../../../../../../services/toast/toast.service";
import { ConfirmationDialogService } from "../../../../../../services/confirmation-dialog/confirmation-dialog.service";
import { FormBuilder, FormControl, FormGroup } from "@angular/forms";
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 { Page } from "../../../../../../models/graphql/filter/page.model";
import { Sort, SortDirection } from "../../../../../../models/graphql/filter/sort.model";
import { Level, LevelFilter } from "../../../../../../models/data/level.model";
import { LevelListQuery } from "../../../../../../models/graphql/query.model";
import { Queries } from "../../../../../../models/graphql/queries.model";
import { catchError, debounceTime, takeUntil } from "rxjs/operators";
import { LazyLoadEvent } from "primeng/api";
import { Table } from "primeng/table";
import { User } from "../../../../../../models/data/user.model";
import { LevelMutationResult, UpdateUserMutationResult } from "../../../../../../models/graphql/result.model";
import { Mutations } from "../../../../../../models/graphql/mutations.model";
import { Subject, throwError } from "rxjs";
@Component({
selector: "app-levels",
templateUrl: "./levels.component.html",
styleUrls: ["./levels.component.scss"]
})
export class LevelsComponent implements OnInit, OnDestroy {
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 filter: LevelFilter = {};
public page: Page = {
pageSize: undefined,
pageIndex: undefined
};
public sort: Sort = {
sortColumn: undefined,
sortDirection: undefined
};
public totalRecords!: number;
public clonedLevels: { [s: string]: Level; } = {};
private unsubscriber = new Subject<void>();
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) {
}
public ngOnInit(): void {
this.data.getServerFromRoute(this.route).then(server => {
this.setFilterForm();
this.loadNextPage();
});
}
public ngOnDestroy(): void {
this.unsubscriber.next();
this.unsubscriber.complete();
}
public loadNextPage(): void {
this.loading = true;
this.data.query<LevelListQuery>(Queries.levelQuery, {
filter: this.filter, page: this.page, sort: this.sort
}
).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 = {
id: 0,
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 onRowEditSave(table: Table, newLevel: Level, index: number): void {
// const oldUser = this.clonedUsers[index];
// delete this.clonedUsers[index];
// if (JSON.stringify(oldUser) === JSON.stringify(newUser) && !this.isEditingNew) {
// console.log(1, oldUser, newUser, JSON.stringify(oldUser) === JSON.stringify(newUser), !this.isEditingNew);
// return;
// }
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.sidebar.server$.value?.id
}
).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;
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.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();
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();
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;
}
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<LevelMutationResult>(Mutations.deleteLevel, {
id: level.id
}
).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();
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));
newLevel.id = Math.max.apply(Math, this.levels.map(l => {
return l.id ?? 0;
})) + 1;
this.levels.push(newLevel);
table.initRowEdit(newLevel);
const index = this.levels.findIndex(l => l.id == newLevel.id);
this.onRowEditInit(table, newLevel, index);
this.isEditingNew = true;
}
}

View File

@ -0,0 +1,16 @@
import {NgModule} from "@angular/core";
import {RouterModule, Routes} from "@angular/router";
import {LevelsComponent} from "./components/levels/levels.component";
const routes: Routes = [
{path: '', component: LevelsComponent},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LevelsRoutingModule {
}

View File

@ -0,0 +1,18 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {LevelsComponent} from './components/levels/levels.component'
import {SharedModule} from "../../../shared/shared.module";
import {LevelsRoutingModule} from "./levels-routing.module";
@NgModule({
declarations: [
LevelsComponent
],
imports: [
CommonModule,
LevelsRoutingModule,
SharedModule
]
})
export class LevelsModule {
}

View File

@ -164,7 +164,8 @@
<p-cellEditor> <p-cellEditor>
<ng-template pTemplate="input"> <ng-template pTemplate="input">
<p-inputNumber class="table-edit-input" styleClass="input-number" [(ngModel)]="member.xp" mode="decimal" [min]="0" [useGrouping]="false" [showButtons]="true" <p-inputNumber class="table-edit-input" styleClass="input-number" [(ngModel)]="member.xp" mode="decimal" [min]="0" [useGrouping]="false" [showButtons]="true"
inputId="minmax-buttons"></p-inputNumber> inputId="minmax-buttons">
</p-inputNumber>
</ng-template> </ng-template>
<ng-template pTemplate="output"> <ng-template pTemplate="output">
{{member.xp}} {{member.xp}}
@ -225,12 +226,13 @@
<div class="btn-wrapper"> <div class="btn-wrapper">
<button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-pencil" <button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-pencil"
(click)="onRowEditInit(dt, member, ri)"></button> (click)="onRowEditInit(dt, member, ri)"></button>
<button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-user" [routerLink]="member.id"></button> <button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-user"
[routerLink]="member.id"></button>
<button *ngIf="editing" pButton pSaveEditableRow class="btn icon-btn" <button *ngIf="editing" pButton pSaveEditableRow class="btn icon-btn"
icon="pi pi-check-circle" (click)="onRowEditSave(dt, member, ri)"></button> icon="pi pi-check-circle" (click)="onRowEditSave(dt, member, ri)"></button>
<button *ngIf="editing" pButton pCancelEditableRow class="btn icon-btn danger-icon-btn" <button *ngIf="editing" pButton pCancelEditableRow class="btn icon-btn danger-icon-btn"
icon="pi pi-times-circle" (click)="onRowEditCancel(member, ri)"></button> icon="pi pi-times-circle" (click)="onRowEditCancel(ri)"></button>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -5,7 +5,7 @@ import { SpinnerService } from "../../../../services/spinner/spinner.service";
import { ToastService } from "../../../../services/toast/toast.service"; import { ToastService } from "../../../../services/toast/toast.service";
import { ConfirmationDialogService } from "../../../../services/confirmation-dialog/confirmation-dialog.service"; import { ConfirmationDialogService } from "../../../../services/confirmation-dialog/confirmation-dialog.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { catchError, debounceTime } from "rxjs/operators"; import { catchError, debounceTime, takeUntil } from "rxjs/operators";
import { LazyLoadEvent, MenuItem } from "primeng/api"; import { LazyLoadEvent, MenuItem } from "primeng/api";
import { Table } from "primeng/table"; import { Table } from "primeng/table";
import { User, UserFilter } from "../../../../models/data/user.model"; import { User, UserFilter } from "../../../../models/data/user.model";
@ -15,9 +15,10 @@ import { DataService } from "../../../../services/data/data.service";
import { Page } from "../../../../models/graphql/filter/page.model"; import { Page } from "../../../../models/graphql/filter/page.model";
import { Sort, SortDirection } from "../../../../models/graphql/filter/sort.model"; import { Sort, SortDirection } from "../../../../models/graphql/filter/sort.model";
import { Mutations } from "../../../../models/graphql/mutations.model"; import { Mutations } from "../../../../models/graphql/mutations.model";
import { throwError } from "rxjs"; import { Subject, throwError } from "rxjs";
import { UpdateUserMutationResult } from "../../../../models/graphql/result.model"; import { UpdateUserMutationResult } from "../../../../models/graphql/result.model";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { Level } from "../../../../models/data/level.model";
@Component({ @Component({
selector: "app-members", selector: "app-members",
@ -64,7 +65,7 @@ export class MembersComponent implements OnInit {
discordId: FormControl<number | null>, discordId: FormControl<number | null>,
name: FormControl<string | null>, name: FormControl<string | null>,
leftServer: FormControl<boolean | null>, leftServer: FormControl<boolean | null>,
level: FormControl<number | null> level: FormControl<Level | null>
}>; }>;
filter: UserFilter = {}; filter: UserFilter = {};
@ -78,6 +79,7 @@ export class MembersComponent implements OnInit {
}; };
totalRecords!: number; totalRecords!: number;
private unsubscriber = new Subject<void>();
constructor( constructor(
private authService: AuthService, private authService: AuthService,
@ -130,10 +132,11 @@ export class MembersComponent implements OnInit {
discordId: new FormControl<number | null>(null), discordId: new FormControl<number | null>(null),
name: [""], name: [""],
leftServer: new FormControl<boolean | null>(null), leftServer: new FormControl<boolean | null>(null),
level: new FormControl<number | null>(null) level: new FormControl<Level | null>(null)
}); });
this.filterForm.valueChanges.pipe( this.filterForm.valueChanges.pipe(
takeUntil(this.unsubscriber),
debounceTime(600) debounceTime(600)
).subscribe(changes => { ).subscribe(changes => {
if (changes.id) { if (changes.id) {
@ -162,7 +165,7 @@ export class MembersComponent implements OnInit {
if (changes.level) { if (changes.level) {
this.filter.level = { this.filter.level = {
id: changes.level id: changes.level.id
}; };
} else { } else {
this.filter.level = undefined; this.filter.level = undefined;
@ -233,7 +236,7 @@ export class MembersComponent implements OnInit {
} }
onRowEditCancel(user: User, index: number) { onRowEditCancel(index: number) {
if (this.isEditingNew) { if (this.isEditingNew) {
this.members.splice(index, 1); this.members.splice(index, 1);
delete this.clonedUsers[index]; delete this.clonedUsers[index];

View File

@ -5,14 +5,16 @@ import { ProfileComponent } from "./profile/profile.component";
import { MembersComponent } from "./members/members.component"; import { MembersComponent } from "./members/members.component";
const routes: Routes = [ const routes: Routes = [
{ path: '', component: ServerDashboardComponent }, { path: "", component: ServerDashboardComponent },
{ path: 'members', component: MembersComponent }, { path: "members", component: MembersComponent },
{ path: 'members/:memberId', component: ProfileComponent }, { path: "members/:memberId", component: ProfileComponent },
{ path: 'auto-roles', loadChildren: () => import('./auto-role/auto-role.module').then(m => m.AutoRoleModule)}, { 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) }
]; ];
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],
exports: [RouterModule] exports: [RouterModule]
}) })
export class ServerRoutingModule { } export class ServerRoutingModule {
}

View File

@ -28,7 +28,7 @@ export class DataService {
public getServerFromRoute(route: ActivatedRoute): Promise<Server> { public getServerFromRoute(route: ActivatedRoute): Promise<Server> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.spinner.showSpinner(); this.spinner.showSpinner();
if (!route.snapshot.params["serverId"]) { if (!route.snapshot.params["serverId"] || route.snapshot.params["serverId"] == "undefined") {
this.spinner.hideSpinner(); this.spinner.hideSpinner();
this.router.navigate(["/dashboard"]); this.router.navigate(["/dashboard"]);
reject(); reject();

View File

@ -18,16 +18,16 @@ export class SidebarService {
menuItems$ = new BehaviorSubject<MenuItem[]>(new Array<MenuItem>()); menuItems$ = new BehaviorSubject<MenuItem[]>(new Array<MenuItem>());
server$ = new BehaviorSubject<Server | null>(null); server$ = new BehaviorSubject<Server | null>(null);
dashboard!: MenuItem; dashboard: MenuItem = {};
serverDashboard!: MenuItem; serverDashboard: MenuItem = {};
serverProfile!: MenuItem; serverProfile: MenuItem = {};
serverMembers!: MenuItem; serverMembers: MenuItem = {};
serverAutoRoles!: MenuItem; serverAutoRoles: MenuItem = {};
serverAutoRoleRules!: MenuItem; serverLevels: MenuItem = {};
serverMenu!: MenuItem; serverMenu: MenuItem = {};
adminConfig!: MenuItem; adminConfig: MenuItem = {};
adminUsers!: MenuItem; adminUsers: MenuItem = {};
adminMenu!: MenuItem; adminMenu: MenuItem = {};
constructor( constructor(
private themeService: ThemeService, private themeService: ThemeService,
@ -66,7 +66,11 @@ export class SidebarService {
} }
async buildMenu(user: UserDTO | null, hasPermission: boolean) { async buildMenu(user: UserDTO | null, hasPermission: boolean) {
this.dashboard = { label: this.isSidebarOpen ? this.translateService.instant("sidebar.dashboard") : "", icon: "pi pi-th-large", routerLink: "dashboard" }; this.dashboard = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.dashboard") : "",
icon: "pi pi-th-large",
routerLink: "dashboard"
};
this.serverDashboard = { this.serverDashboard = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.dashboard") : "", label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.dashboard") : "",
icon: "pi pi-th-large", icon: "pi pi-th-large",
@ -74,7 +78,7 @@ export class SidebarService {
}; };
this.serverProfile = { this.serverProfile = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.profile") : "", label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.profile") : "",
icon: "pi pi-user", icon: "pi pi-id-card",
routerLink: `server/${this.server$.value?.id}/members/${user?.id}` routerLink: `server/${this.server$.value?.id}/members/${user?.id}`
}; };
this.serverMembers = { this.serverMembers = {
@ -91,14 +95,25 @@ export class SidebarService {
routerLink: `server/${this.server$.value?.id}/auto-roles` routerLink: `server/${this.server$.value?.id}/auto-roles`
}; };
this.serverLevels = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.levels") : "",
icon: "pi pi-book",
visible: true,
routerLink: `server/${this.server$.value?.id}/levels`
};
this.serverMenu = { this.serverMenu = {
label: this.isSidebarOpen ? this.server$.value?.name : "", label: this.isSidebarOpen ? this.server$.value?.name : "",
icon: "pi pi-server", icon: "pi pi-server",
visible: false, visible: false,
expanded: true, expanded: true,
items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles] items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles, this.serverLevels]
};
this.adminConfig = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.config") : "",
icon: "pi pi-cog",
routerLink: "/admin/settings"
}; };
this.adminConfig = { label: this.isSidebarOpen ? this.translateService.instant("sidebar.config") : "", icon: "pi pi-cog", routerLink: "/admin/settings" };
this.adminUsers = { this.adminUsers = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.auth_user_list") : "", label: this.isSidebarOpen ? this.translateService.instant("sidebar.auth_user_list") : "",
icon: "pi pi-user-edit", icon: "pi pi-user-edit",
@ -126,6 +141,7 @@ export class SidebarService {
this.serverMenu.visible = true; this.serverMenu.visible = true;
this.serverMembers.visible = !!user?.isModerator; this.serverMembers.visible = !!user?.isModerator;
this.serverAutoRoles.visible = !!user?.isAdmin; this.serverAutoRoles.visible = !!user?.isAdmin;
this.serverLevels.visible = !!user?.isAdmin;
} else { } else {
this.serverMenu.visible = false; this.serverMenu.visible = false;
} }

View File

@ -12,7 +12,6 @@ export class ThemeService {
sidebarWidth = '150px'; sidebarWidth = '150px';
isSidebarOpen = false; isSidebarOpen = false;
hasLangChanged = false;
themeName$ = new BehaviorSubject<string>(Themes.Default); themeName$ = new BehaviorSubject<string>(Themes.Default);
isSidebarOpen$ = new BehaviorSubject<boolean>(true); isSidebarOpen$ = new BehaviorSubject<boolean>(true);

View File

@ -3,7 +3,7 @@
"WebVersion": { "WebVersion": {
"Major": "1", "Major": "1",
"Minor": "0", "Minor": "0",
"Micro": "0" "Micro": "dev133"
}, },
"Themes": [ "Themes": [
{ {

View File

@ -11,6 +11,7 @@
"dashboard": "Dashboard", "dashboard": "Dashboard",
"profile": "Dein Profil", "profile": "Dein Profil",
"members": "Mitglieder", "members": "Mitglieder",
"levels": "Level",
"auto_roles": "Auto Rollen" "auto_roles": "Auto Rollen"
}, },
"server_empty": "Kein Server ausgewählt", "server_empty": "Kein Server ausgewählt",
@ -220,7 +221,7 @@
"message": { "message": {
"user_changed": "Benutzer geändert", "user_changed": "Benutzer geändert",
"user_changed_d": "Benutzer {{name}} erfolgreich geändert", "user_changed_d": "Benutzer {{name}} erfolgreich geändert",
"user_change_failed": "Benutzer änderung fehlgeschlagen", "user_change_failed": "Benutzer Änderung fehlgeschlagen",
"user_change_failed_d": "Benutzer {{name}} konnte nicht geändert werden!" "user_change_failed_d": "Benutzer {{name}} konnte nicht geändert werden!"
} }
}, },
@ -281,6 +282,38 @@
"auto_role_rule_delete_failed_d": "Die Löschung der Auto Rollen Regel {{id}} ist fehlgeschlagen!" "auto_role_rule_delete_failed_d": "Die Löschung der Auto Rollen Regel {{id}} ist fehlgeschlagen!"
} }
} }
},
"levels": {
"header": "Level",
"reset_filters": "Filter zurücksetzen",
"of": "von",
"add": "Hinzufügen",
"levels": "Level",
"headers": {
"id": "Id",
"name": "Name",
"color": "Farbe",
"min_xp": "Min. XP",
"permissions": "Rechte",
"actions": "Aktionen"
},
"no_entries_found": "Keine Einträge gefunden",
"message": {
"level_create": "Level erstellt",
"level_create_d": "Level {{name}} erfolgreich erstellt",
"level_create_failed": "Level Erstellung fehlgeschlagen",
"level_create_failed_d": "Die Erstellung des Levels ist fehlgeschlagen!",
"level_update": "Level bearbeitet",
"level_update_d": "Level {{name}} erfolgreich bearbeitet",
"level_update_failed": "Level Bearbeitung fehlgeschlagen",
"level_update_failed_d": "Die Bearbeitung des Levels ist fehlgeschlagen!",
"level_delete": "Level löschen",
"level_delete_q": "Sind Sie sich sicher, dass Sie das Level {{name}} löschen möchten?",
"level_deleted": "Level gelöscht",
"level_deleted_d": "Level {{name}} erfolgreich gelöscht",
"level_delete_failed": "Level Löschung fehlgeschlagen",
"level_delete_failed_d": "Die Löschung des Levels {{name}} ist fehlgeschlagen!"
}
} }
}, },
"user-list": {}, "user-list": {},