1.0.0 #253
| @@ -69,11 +69,11 @@ class ApiKey(TableABC): | ||||
|         return str( | ||||
|             f""" | ||||
|             INSERT INTO `ApiKeys` ( | ||||
|                 `Identifier`, `Key`, {"" if self._creator is None else "`CreatorId`,"} `CreatedAt`, `LastModifiedAt` | ||||
|                 `Identifier`, `Key`, `CreatorId`, `CreatedAt`, `LastModifiedAt` | ||||
|             ) VALUES ( | ||||
|                 '{self._identifier}', | ||||
|                 '{self._key}', | ||||
|                 {"" if self._creator is None else f"{self._creator.id},"} | ||||
|                 {"NULL" if self._creator is None else f"'{self._creator.id}'"}, | ||||
|                 '{self._created_at}', | ||||
|                 '{self._modified_at}' | ||||
|             ); | ||||
| @@ -87,7 +87,7 @@ class ApiKey(TableABC): | ||||
|             UPDATE `ApiKeys` | ||||
|             SET `Identifier` = '{self._identifier}', | ||||
|             `Key` = '{self._key}', | ||||
|             {"" if self._creator is None else f"`CreatorId` = {self._creator.id},"} | ||||
|             `CreatorId` = {"NULL" if self._creator is None else f"'{self._creator.id}'"}, | ||||
|             `LastModifiedAt` = '{self._modified_at}' | ||||
|             WHERE `Id` = {self._id}; | ||||
|         """ | ||||
|   | ||||
| @@ -215,10 +215,10 @@ class AuthUser(TableABC): | ||||
|                 `EMail`, | ||||
|                 `Password`, | ||||
|                 `PasswordSalt`, | ||||
|                 {"" if self._refresh_token is None else f"`RefreshToken`,"} | ||||
|                 {"" if self._confirmation_id is None else f"`ConfirmationId`,"} | ||||
|                 {"" if self._forgot_password_id is None else f"`ForgotPasswordId`,"} | ||||
|                 {"" if self._oauth_id is None else f"`OAuthId`,"} | ||||
|                 `RefreshToken`, | ||||
|                 `ConfirmationId`, | ||||
|                 `ForgotPasswordId`, | ||||
|                 `OAuthId`, | ||||
|                 `RefreshTokenExpiryTime`, | ||||
|                 `AuthRole`, | ||||
|                 `CreatedAt`, | ||||
| @@ -230,10 +230,10 @@ class AuthUser(TableABC): | ||||
|                 '{self._email}', | ||||
|                 '{self._password}',  | ||||
|                 '{self._password_salt}',  | ||||
|                 {"" if self._refresh_token is None else f"'{self._refresh_token}',"} | ||||
|                 {"" if self._confirmation_id is None else f"'{self._confirmation_id}',"} | ||||
|                 {"" if self._forgot_password_id is None else f"'{self._forgot_password_id}',"} | ||||
|                 {"" if self._oauth_id is None else f"'{self._oauth_id}',"} | ||||
|                 {"NULL" if self._refresh_token is None else f"'{self._refresh_token}'"}, | ||||
|                 {"NULL" if self._confirmation_id is None else f"'{self._confirmation_id}'"}, | ||||
|                 {"NULL" if self._forgot_password_id is None else f"'{self._forgot_password_id}'"}, | ||||
|                 {"NULL" if self._oauth_id is None else f"'{self._oauth_id}'"}, | ||||
|                 '{self._refresh_token_expire_time.isoformat()}', | ||||
|                 {self._auth_role_id.value}, | ||||
|                 '{self._created_at}',  | ||||
| @@ -252,10 +252,10 @@ class AuthUser(TableABC): | ||||
|             `EMail` = '{self._email}',  | ||||
|             `Password` = '{self._password}',  | ||||
|             `PasswordSalt` = '{self._password_salt}',  | ||||
|             {'' if self._refresh_token is None else f"`RefreshToken` = '{self._refresh_token}',"}  | ||||
|             {'' if self._confirmation_id is None else f"'`ConfirmationId` = '{self._confirmation_id}',"}  | ||||
|             {'' if self._forgot_password_id is None else f"`ForgotPasswordId` = '{self._forgot_password_id}',"} | ||||
|             {'' if self._oauth_id is None else f"`OAuthId` = '{self._oauth_id}',"}  | ||||
|             `RefreshToken` = {"NULL" if self._refresh_token is None else f"'{self._refresh_token}'"}, | ||||
|             `ConfirmationId` = {"NULL" if self._confirmation_id is None else f"'{self._confirmation_id}'"}, | ||||
|             `ForgotPasswordId` = {"NULL" if self._forgot_password_id is None else f"'{self._forgot_password_id}'"}, | ||||
|             `OAuthId` = {"NULL" if self._oauth_id is None else f"'{self._oauth_id}'"}, | ||||
|             `RefreshTokenExpiryTime` = '{self._refresh_token_expire_time.isoformat()}', | ||||
|             `AuthRole` = {self._auth_role_id.value}, | ||||
|             `LastModifiedAt` = '{self._modified_at}' | ||||
|   | ||||
| @@ -150,11 +150,11 @@ class User(TableABC): | ||||
|         return str( | ||||
|             f""" | ||||
|             INSERT INTO `Users` ( | ||||
|                 `DiscordId`, `XP`, {"" if self._minecraft_id is None else "`MinecraftId`,"} `ServerId`, `CreatedAt`, `LastModifiedAt` | ||||
|                 `DiscordId`, `XP`, `MinecraftId`, `ServerId`, `CreatedAt`, `LastModifiedAt` | ||||
|             ) VALUES ( | ||||
|                 {self._discord_id}, | ||||
|                 {self._xp}, | ||||
|                 {"" if self._minecraft_id is None else f"'{self._minecraft_id}',"} | ||||
|                 {"NULL" if self._minecraft_id is None else f"'{self._minecraft_id}'"}, | ||||
|                 {self._server.id}, | ||||
|                 '{self._created_at}', | ||||
|                 '{self._modified_at}' | ||||
| @@ -168,7 +168,7 @@ class User(TableABC): | ||||
|             f""" | ||||
|             UPDATE `Users` | ||||
|             SET `XP` = {self._xp}, | ||||
|             {"" if self._minecraft_id is None else f"`MinecraftId` = '{self._minecraft_id}',"} | ||||
|             `MinecraftId` = {"NULL" if self._minecraft_id is None else f"'{self._minecraft_id}'"}, | ||||
|             `LastModifiedAt` = '{self._modified_at}' | ||||
|             WHERE `UserId` = {self._user_id}; | ||||
|         """ | ||||
|   | ||||
| @@ -101,12 +101,12 @@ class UserJoinedGameServer(TableABC): | ||||
|         return str( | ||||
|             f""" | ||||
|             INSERT INTO `UserJoinedGameServer` ( | ||||
|                 `UserId`, `GameServer`, `JoinedOn`, {"" if self._leaved_on is None else "`LeavedOn`,"} `CreatedAt`, `LastModifiedAt` | ||||
|                 `UserId`, `GameServer`, `JoinedOn`, `LeavedOn`, `CreatedAt`, `LastModifiedAt` | ||||
|             ) VALUES ( | ||||
|                 {self._user.id}, | ||||
|                 '{self._game_server}', | ||||
|                 '{self._joined_on}', | ||||
|                 {"" if self._leaved_on is None else f"'{self._leaved_on}',"} | ||||
|                 {"NULL" if self._leaved_on is None else f"'{self._leaved_on}'"}, | ||||
|                 '{self._created_at}', | ||||
|                 '{self._modified_at}' | ||||
|             ); | ||||
| @@ -118,8 +118,7 @@ class UserJoinedGameServer(TableABC): | ||||
|         return str( | ||||
|             f""" | ||||
|             UPDATE `UserJoinedGameServer` | ||||
|             SET | ||||
|             {"" if self._leaved_on is None else f"`LeavedOn` = '{self._leaved_on}',"} | ||||
|             SET `LeavedOn` = {"NULL" if self._leaved_on is None else f"'{self._leaved_on}'"}, | ||||
|             `LastModifiedAt` = '{self._modified_at}' | ||||
|             WHERE `Id` = {self._id}; | ||||
|         """ | ||||
|   | ||||
| @@ -100,11 +100,11 @@ class UserJoinedServer(TableABC): | ||||
|         return str( | ||||
|             f""" | ||||
|             INSERT INTO `UserJoinedServers` ( | ||||
|                 `UserId`, `JoinedOn`, {"" if self._leaved_on is None else "`LeavedOn`,"} `CreatedAt`, `LastModifiedAt` | ||||
|                 `UserId`, `JoinedOn`, `LeavedOn`, `CreatedAt`, `LastModifiedAt` | ||||
|             ) VALUES ( | ||||
|                 {self._user.id}, | ||||
|                 '{self._joined_on}', | ||||
|                 {"" if self._leaved_on is None else f"'{self._leaved_on}',"} | ||||
|                 {"NULL" if self._leaved_on is None else f"'{self._leaved_on}'"}, | ||||
|                 '{self._created_at}', | ||||
|                 '{self._modified_at}' | ||||
|             ); | ||||
| @@ -116,8 +116,7 @@ class UserJoinedServer(TableABC): | ||||
|         return str( | ||||
|             f""" | ||||
|             UPDATE `UserJoinedServers` | ||||
|             SET | ||||
|             {"" if self._leaved_on is None else f"`LeavedOn` = '{self._leaved_on}',"} | ||||
|             SET `LeavedOn` = {"NULL" if self._leaved_on is None else f"'{self._leaved_on}'"}, | ||||
|             `LastModifiedAt` = '{self._modified_at}' | ||||
|             WHERE `UserId` = {self._user.id}; | ||||
|         """ | ||||
|   | ||||
| @@ -108,12 +108,12 @@ class UserJoinedVoiceChannel(TableABC): | ||||
|         return str( | ||||
|             f""" | ||||
|             INSERT INTO `UserJoinedVoiceChannel` ( | ||||
|                 `UserId`, `DiscordChannelId`, `JoinedOn`, {"" if self._leaved_on is None else "`LeavedOn`,"} `CreatedAt`, `LastModifiedAt` | ||||
|                 `UserId`, `DiscordChannelId`, `JoinedOn`, `LeavedOn`, `CreatedAt`, `LastModifiedAt` | ||||
|             ) VALUES ( | ||||
|                 {self._user.id}, | ||||
|                 {self._channel_id}, | ||||
|                 '{self._joined_on}', | ||||
|                 {"" if self._leaved_on is None else f"'{self._leaved_on}',"} | ||||
|                 {"NULL" if self._leaved_on is None else f"'{self._leaved_on}'"}, | ||||
|                 '{self._created_at}', | ||||
|                 '{self._modified_at}' | ||||
|             ); | ||||
| @@ -125,8 +125,7 @@ class UserJoinedVoiceChannel(TableABC): | ||||
|         return str( | ||||
|             f""" | ||||
|             UPDATE `UserJoinedVoiceChannel` | ||||
|             SET | ||||
|             {"" if self._leaved_on is None else f"`LeavedOn` = '{self._leaved_on}',"} | ||||
|             SET `LeavedOn` = {"NULL" if self._leaved_on is None else f"'{self._leaved_on}'"}, | ||||
|             `LastModifiedAt` = '{self._modified_at}' | ||||
|             WHERE `JoinId` = {self._join_id}; | ||||
|         """ | ||||
|   | ||||
| @@ -28,7 +28,7 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): | ||||
|  | ||||
|     @staticmethod | ||||
|     def _get_value_from_result(value: any) -> Optional[any]: | ||||
|         if isinstance(value, str) and "null" in value: | ||||
|         if isinstance(value, str) and "NULL" in value: | ||||
|             return None | ||||
|  | ||||
|         return value | ||||
|   | ||||
| @@ -33,9 +33,9 @@ class AutoRoleMutation(QueryABC): | ||||
|  | ||||
|         def get_new(x: AutoRole): | ||||
|             return ( | ||||
|                 x.server.id == input["serverId"] | ||||
|                 and x.discord_channel_id == input["channelId"] | ||||
|                 and x.discord_message_id == input["messageId"] | ||||
|                 x.server.id == int(input["serverId"]) | ||||
|                 and x.discord_channel_id == int(input["channelId"]) | ||||
|                 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() | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "kdb-web", | ||||
|     "version": "1.0.0", | ||||
|     "version": "1.0.dev220", | ||||
|     "scripts": { | ||||
|         "ng": "ng", | ||||
|         "update-version": "ts-node-esm update-version.ts", | ||||
|   | ||||
| @@ -1,29 +1,27 @@ | ||||
| import { Component, OnInit } from "@angular/core"; | ||||
| import { Component, OnDestroy, OnInit } from "@angular/core"; | ||||
| import { TranslateService } from "@ngx-translate/core"; | ||||
| import { PrimeNGConfig } from "primeng/api"; | ||||
| import { AuthService } from "./services/auth/auth.service"; | ||||
| import { SocketService } from "./services/socket/socket.service"; | ||||
| import { ThemeService } from "./services/theme/theme.service"; | ||||
| import { ActivatedRoute, Router } from "@angular/router"; | ||||
| import { SpinnerService } from "./services/spinner/spinner.service"; | ||||
| import { DataService } from "./services/data/data.service"; | ||||
| 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"; | ||||
| import { Subject } from "rxjs"; | ||||
| import { Themes } from "./models/view/themes.enum"; | ||||
| import { takeUntil } from "rxjs/operators"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: "app-root", | ||||
|   templateUrl: "./app.component.html", | ||||
|   styleUrls: ["./app.component.scss"] | ||||
| }) | ||||
| export class AppComponent implements OnInit { | ||||
| export class AppComponent implements OnInit, OnDestroy { | ||||
|  | ||||
|   themeName!: string; | ||||
|   sidebarWidth!: string; | ||||
|   themeName: string = Themes.Default; | ||||
|   sidebarWidth: string = '175px'; | ||||
|  | ||||
|   isLoggedIn: boolean = false; | ||||
|  | ||||
|   private unsubscriber = new Subject<void>(); | ||||
|  | ||||
|   constructor( | ||||
|     private authService: AuthService, | ||||
|     private themeService: ThemeService, | ||||
| @@ -31,13 +29,19 @@ export class AppComponent implements OnInit { | ||||
|     private translateService: TranslateService, | ||||
|     private config: PrimeNGConfig, | ||||
|   ) { | ||||
|     this.themeService.sidebarWidth$.subscribe(value => { | ||||
|     this.themeService.sidebarWidth$.pipe( | ||||
|       takeUntil(this.unsubscriber) | ||||
|     ).subscribe(value => { | ||||
|       this.sidebarWidth = value; | ||||
|     }); | ||||
|     this.themeService.themeName$.subscribe(value => { | ||||
|     this.themeService.themeName$.pipe( | ||||
|       takeUntil(this.unsubscriber) | ||||
|     ).subscribe(value => { | ||||
|       this.themeName = value; | ||||
|     }); | ||||
|     this.authService.isLoggedIn$.subscribe(value => { | ||||
|     this.authService.isLoggedIn$.pipe( | ||||
|       takeUntil(this.unsubscriber) | ||||
|     ).subscribe(value => { | ||||
|       this.isLoggedIn = value; | ||||
|     }); | ||||
|   } | ||||
| @@ -49,6 +53,11 @@ export class AppComponent implements OnInit { | ||||
|     this.socket.startSocket(); | ||||
|   } | ||||
|  | ||||
|   ngOnDestroy() { | ||||
|     this.unsubscriber.next(); | ||||
|     this.unsubscriber.unsubscribe(); | ||||
|   } | ||||
|  | ||||
|   loadLang(): void { | ||||
|     let lang = localStorage.getItem(`default_lang`); | ||||
|     if (!lang) { | ||||
|   | ||||
| @@ -1,19 +1,21 @@ | ||||
| import { Component, OnInit } from "@angular/core"; | ||||
| import { Component, OnDestroy, OnInit } from "@angular/core"; | ||||
| import { TranslateService } from "@ngx-translate/core"; | ||||
| import { MenuItem } from "primeng/api"; | ||||
| import { AuthService } from "src/app/services/auth/auth.service"; | ||||
| import { ThemeService } from "src/app/services/theme/theme.service"; | ||||
| import { SidebarService } from "../../services/sidebar/sidebar.service"; | ||||
| import { Subject } from "rxjs"; | ||||
| import { takeUntil } from "rxjs/operators"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: "app-sidebar", | ||||
|   templateUrl: "./sidebar.component.html", | ||||
|   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( | ||||
|     private authService: AuthService, | ||||
| @@ -21,7 +23,9 @@ export class SidebarComponent implements OnInit { | ||||
|     private themeService: ThemeService, | ||||
|     private sidebar: SidebarService | ||||
|   ) { | ||||
|     this.sidebar.menuItems$.subscribe(value => { | ||||
|     this.sidebar.menuItems$.pipe( | ||||
|       takeUntil(this.unsubscriber) | ||||
|     ).subscribe(value => { | ||||
|       this.menuItems = value; | ||||
|     }); | ||||
|   } | ||||
| @@ -30,4 +34,9 @@ export class SidebarComponent implements OnInit { | ||||
|     this.themeService.loadMenu(); | ||||
|   } | ||||
|  | ||||
|   ngOnDestroy() { | ||||
|     this.unsubscriber.next(); | ||||
|     this.unsubscriber.complete(); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| export interface Data { | ||||
|   createdAt: string; | ||||
|   modifiedAt: string; | ||||
|   createdAt?: string; | ||||
|   modifiedAt?: string; | ||||
| } | ||||
|   | ||||
| @@ -2,9 +2,9 @@ export interface Guild { | ||||
|   id?: string; | ||||
|   name?: string; | ||||
|  | ||||
|   channels: [Channel] | ||||
|   roles: [Role] | ||||
|   emojis: [Emoji] | ||||
|   channels: Channel[]; | ||||
|   roles: Role[]; | ||||
|   emojis: Emoji[]; | ||||
| } | ||||
|  | ||||
| export interface Channel { | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { Data } from "./data.model"; | ||||
| import { Server, ServerFilter } from "./server.model"; | ||||
| import {Data} from "./data.model"; | ||||
| import {Server, ServerFilter} from "./server.model"; | ||||
|  | ||||
| export interface Level extends Data { | ||||
|   id?: number; | ||||
| @@ -12,6 +12,6 @@ export interface Level extends Data { | ||||
|  | ||||
| export interface LevelFilter { | ||||
|   id?: number; | ||||
|   name?: String; | ||||
|   name?: string; | ||||
|   server?: ServerFilter; | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   `; | ||||
| } | ||||
|   | ||||
| @@ -26,6 +26,7 @@ export class Queries { | ||||
|  | ||||
|   static serversQuery = ` | ||||
|     query ServerInfo($filter: ServerFilter, $page: Page, $sort: Sort) { | ||||
|       serverCount | ||||
|       servers(filter: $filter, page: $page, sort: $sort) { | ||||
|         id | ||||
|         name | ||||
| @@ -46,112 +47,76 @@ export class Queries { | ||||
|   `; | ||||
|  | ||||
|   static levelQuery = ` | ||||
|     query LevelsList($filter: LevelFilter, $page: Page, $sort: Sort) { | ||||
|       levelCount | ||||
|       levels(filter: $filter, page: $page, sort: $sort) { | ||||
|         id | ||||
|         name | ||||
|         color | ||||
|         minXp | ||||
|         permissions | ||||
|         server { | ||||
|     query LevelsList($serverId: ID, $filter: LevelFilter, $page: Page, $sort: Sort) { | ||||
|       servers(filter: {id: $serverId}) { | ||||
|         levelCount | ||||
|         levels(filter: $filter, page: $page, sort: $sort) { | ||||
|           id | ||||
|           name | ||||
|           color | ||||
|           minXp | ||||
|           permissions | ||||
|           server { | ||||
|             id | ||||
|             name | ||||
|           } | ||||
|           createdAt | ||||
|           modifiedAt | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   `; | ||||
|  | ||||
|   static usersQuery = ` | ||||
|     query UsersList($filter: UserFilter, $page: Page, $sort: Sort) { | ||||
|       userCount | ||||
|       users(filter: $filter, page: $page, sort: $sort) { | ||||
|         id | ||||
|         discordId | ||||
|         name | ||||
|         xp | ||||
|         ontime | ||||
|         level { | ||||
|     query UsersList($serverId: ID, $filter: UserFilter, $page: Page, $sort: Sort) { | ||||
|       servers(filter: {id: $serverId}) { | ||||
|         userCount | ||||
|         users(filter: $filter, page: $page, sort: $sort) { | ||||
|           id | ||||
|           discordId | ||||
|           name | ||||
|         } | ||||
|         server { | ||||
|           id | ||||
|           name | ||||
|         } | ||||
|         leftServer | ||||
|           xp | ||||
|           ontime | ||||
|           minecraftId | ||||
|           level { | ||||
|             id | ||||
|             name | ||||
|           } | ||||
|           leftServer | ||||
|           server { | ||||
|             id | ||||
|             name | ||||
|           } | ||||
|  | ||||
|         joinedServerCount | ||||
|         joinedServers { | ||||
|           id | ||||
|         } | ||||
|           joinedServerCount | ||||
|           joinedServers { | ||||
|             id | ||||
|             joinedOn | ||||
|             leavedOn | ||||
|           } | ||||
|  | ||||
|         joinedVoiceChannelCount | ||||
|         joinedVoiceChannels { | ||||
|           id | ||||
|           channelId | ||||
|           channelName | ||||
|         } | ||||
|           joinedVoiceChannelCount | ||||
|           joinedVoiceChannels { | ||||
|             id | ||||
|             channelId | ||||
|             channelName | ||||
|             time | ||||
|             joinedOn | ||||
|             leavedOn | ||||
|           } | ||||
|  | ||||
|         userJoinedGameServerCount | ||||
|         userJoinedGameServers { | ||||
|           id | ||||
|           gameServer | ||||
|         } | ||||
|           userJoinedGameServerCount | ||||
|           userJoinedGameServers { | ||||
|             id | ||||
|             gameServer | ||||
|             time | ||||
|             joinedOn | ||||
|             leavedOn | ||||
|           } | ||||
|  | ||||
|         createdAt | ||||
|         modifiedAt | ||||
|       } | ||||
|     } | ||||
|   `; | ||||
|  | ||||
|   static singleUserQuery = ` | ||||
|     query singleUserQuery($filter: UserFilter) { | ||||
|       users(filter: $filter) { | ||||
|         id | ||||
|         discordId | ||||
|         name | ||||
|         xp | ||||
|         ontime | ||||
|         minecraftId | ||||
|         level { | ||||
|           id | ||||
|           name | ||||
|           createdAt | ||||
|           modifiedAt | ||||
|         } | ||||
|         leftServer | ||||
|         server { | ||||
|           id | ||||
|           name | ||||
|         } | ||||
|  | ||||
|         joinedServerCount | ||||
|         joinedServers { | ||||
|           id | ||||
|           joinedOn | ||||
|           leavedOn | ||||
|         } | ||||
|  | ||||
|         joinedVoiceChannelCount | ||||
|         joinedVoiceChannels { | ||||
|           id | ||||
|           channelId | ||||
|           channelName | ||||
|           time | ||||
|           joinedOn | ||||
|           leavedOn | ||||
|         } | ||||
|  | ||||
|         userJoinedGameServerCount | ||||
|         userJoinedGameServers { | ||||
|           id | ||||
|           gameServer | ||||
|           time | ||||
|           joinedOn | ||||
|           leavedOn | ||||
|         } | ||||
|  | ||||
|         createdAt | ||||
|         modifiedAt | ||||
|       } | ||||
|     } | ||||
|   `; | ||||
|   | ||||
| @@ -1,13 +1,19 @@ | ||||
| import { User } from "../data/user.model"; | ||||
| import { AutoRole, AutoRoleRule } from "../data/auto_role.model"; | ||||
| import { Level } from "../data/level.model"; | ||||
| import { Server } from "../data/server.model"; | ||||
|  | ||||
| export interface GraphQLResult { | ||||
|   data: any; | ||||
|   data: { | ||||
|     servers?: Server[]; | ||||
|   }; | ||||
|   errors?: []; | ||||
| } | ||||
|  | ||||
| export interface QueryResult { | ||||
|   data: any; | ||||
|   data: { | ||||
|     servers?: Server[]; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export interface UpdateUserMutationResult { | ||||
| @@ -31,3 +37,11 @@ export interface AutoRoleRuleMutationResult { | ||||
|     deleteAutoRoleRule?: AutoRoleRule | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export interface LevelMutationResult { | ||||
|   level: { | ||||
|     createLevel?: Level | ||||
|     updateLevel?: Level | ||||
|     deleteLevel?: Level | ||||
|   }; | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| 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 { AuthUserDTO } from "src/app/models/auth/auth-user.dto"; | ||||
| 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 { AuthUserSelectCriterion } from "src/app/models/selection/auth-user/auth-user-select-criterion.dto"; | ||||
| import { LazyLoadEvent } from "primeng/api"; | ||||
| import { throwError } from "rxjs"; | ||||
| import { Subject, throwError } from "rxjs"; | ||||
| import { TranslateService } from "@ngx-translate/core"; | ||||
|  | ||||
|  | ||||
| @@ -62,6 +62,8 @@ export class AuthUserComponent implements OnInit { | ||||
|   searchCriterions!: AuthUserSelectCriterion; | ||||
|   totalRecords!: number; | ||||
|  | ||||
|   private unsubscriber = new Subject(); | ||||
|  | ||||
|   constructor( | ||||
|     private authService: AuthService, | ||||
|     private spinnerService: SpinnerService, | ||||
| @@ -97,6 +99,7 @@ export class AuthUserComponent implements OnInit { | ||||
|     }); | ||||
|  | ||||
|     this.filterForm.valueChanges.pipe( | ||||
|       takeUntil(this.unsubscriber), | ||||
|       debounceTime(600) | ||||
|     ).subscribe(changes => { | ||||
|       if (changes.firstName) { | ||||
|   | ||||
| @@ -1,30 +1,30 @@ | ||||
| import {Component, OnInit} from "@angular/core"; | ||||
| import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; | ||||
| import {Router} from "@angular/router"; | ||||
| import {TranslateService} from "@ngx-translate/core"; | ||||
| import {debounceTime, throwError} from "rxjs"; | ||||
| import {ConfirmationDialogService} from "src/app/services/confirmation-dialog/confirmation-dialog.service"; | ||||
| import {DataService} from "src/app/services/data/data.service"; | ||||
| import {SpinnerService} from "src/app/services/spinner/spinner.service"; | ||||
| import {ToastService} from "src/app/services/toast/toast.service"; | ||||
| import {Server, ServerFilter} from "../../../../../models/data/server.model"; | ||||
| import {catchError} from "rxjs/operators"; | ||||
| import {Queries} from "../../../../../models/graphql/queries.model"; | ||||
| import {Page} from "../../../../../models/graphql/filter/page.model"; | ||||
| import {Sort} from "../../../../../models/graphql/filter/sort.model"; | ||||
| import {Query} from "../../../../../models/graphql/query.model"; | ||||
| import {SidebarService} from "../../../../../services/sidebar/sidebar.service"; | ||||
| import { Component, OnDestroy, OnInit } from "@angular/core"; | ||||
| import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; | ||||
| import { Router } from "@angular/router"; | ||||
| import { TranslateService } from "@ngx-translate/core"; | ||||
| import { debounceTime, Subject, throwError } from "rxjs"; | ||||
| import { ConfirmationDialogService } from "src/app/services/confirmation-dialog/confirmation-dialog.service"; | ||||
| import { DataService } from "src/app/services/data/data.service"; | ||||
| import { SpinnerService } from "src/app/services/spinner/spinner.service"; | ||||
| import { ToastService } from "src/app/services/toast/toast.service"; | ||||
| import { Server, ServerFilter } from "../../../../../models/data/server.model"; | ||||
| import { catchError, takeUntil } from "rxjs/operators"; | ||||
| import { Queries } from "../../../../../models/graphql/queries.model"; | ||||
| import { Page } from "../../../../../models/graphql/filter/page.model"; | ||||
| import { Sort } from "../../../../../models/graphql/filter/sort.model"; | ||||
| import { Query } from "../../../../../models/graphql/query.model"; | ||||
| import { SidebarService } from "../../../../../services/sidebar/sidebar.service"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: "app-dashboard", | ||||
|   templateUrl: "./dashboard.component.html", | ||||
|   styleUrls: ["./dashboard.component.scss"] | ||||
| }) | ||||
| export class DashboardComponent implements OnInit { | ||||
| export class DashboardComponent implements OnInit, OnDestroy { | ||||
|  | ||||
|   servers: Server[] = []; | ||||
|  | ||||
|   totalRecords!: number; | ||||
|   totalRecords: number = 0; | ||||
|  | ||||
|   filter: ServerFilter = {}; | ||||
|  | ||||
| @@ -33,12 +33,14 @@ export class DashboardComponent implements OnInit { | ||||
|     pageSize: 10 | ||||
|   }; | ||||
|  | ||||
|   sort!: Sort; | ||||
|   sort: Sort = {}; | ||||
|  | ||||
|   filterForm!: FormGroup<{ | ||||
|     name: FormControl<string | null>, | ||||
|   }>; | ||||
|  | ||||
|   private unsubscriber = new Subject<void>(); | ||||
|  | ||||
|   constructor( | ||||
|     private data: DataService, | ||||
|     private spinnerService: SpinnerService, | ||||
| @@ -58,12 +60,18 @@ export class DashboardComponent implements OnInit { | ||||
|     this.loadNextPage(); | ||||
|   } | ||||
|  | ||||
|   public ngOnDestroy() { | ||||
|     this.unsubscriber.next(); | ||||
|     this.unsubscriber.complete(); | ||||
|   } | ||||
|  | ||||
|   setFilterForm() { | ||||
|     this.filterForm = this.fb.group({ | ||||
|       name: new FormControl<string | null>(null) | ||||
|     }); | ||||
|  | ||||
|     this.filterForm.valueChanges.pipe( | ||||
|       takeUntil(this.unsubscriber), | ||||
|       debounceTime(600) | ||||
|     ).subscribe(async changes => { | ||||
|       if (changes.name == "") { | ||||
|   | ||||
| @@ -17,11 +17,11 @@ import { SidebarService } from "../../../../../../services/sidebar/sidebar.servi | ||||
| import { AutoRoleRuleQuery, SingleDiscordQuery } from "../../../../../../models/graphql/query.model"; | ||||
| import { Queries } from "../../../../../../models/graphql/queries.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 { AutoRoleMutationResult, AutoRoleRuleMutationResult } from "../../../../../../models/graphql/result.model"; | ||||
| import { Mutations } from "../../../../../../models/graphql/mutations.model"; | ||||
| import { throwError } from "rxjs"; | ||||
| import { Subject, throwError } from "rxjs"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: "app-auto-roles-rules", | ||||
| @@ -30,8 +30,8 @@ import { throwError } from "rxjs"; | ||||
| }) | ||||
| export class AutoRolesRulesComponent implements OnInit { | ||||
|  | ||||
|   rules!: AutoRoleRule[]; | ||||
|   guild!: Guild; | ||||
|   rules: AutoRoleRule[] = []; | ||||
|   guild: Guild = { channels: [], emojis: [], roles: [] }; | ||||
|   emojis: MenuItem[] = []; | ||||
|   roles: MenuItem[] = []; | ||||
|   loading = true; | ||||
| @@ -42,7 +42,6 @@ export class AutoRolesRulesComponent implements OnInit { | ||||
|   isEditingNew: boolean = false; | ||||
|  | ||||
|   newAutoRoleTemplate: AutoRoleRule = { | ||||
|     id: 0, | ||||
|     createdAt: "", | ||||
|     modifiedAt: "" | ||||
|   }; | ||||
| @@ -63,7 +62,9 @@ export class AutoRolesRulesComponent implements OnInit { | ||||
|     sortDirection: undefined | ||||
|   }; | ||||
|  | ||||
|   totalRecords!: number; | ||||
|   totalRecords: number = 0; | ||||
|   private unsubscriber = new Subject<void>(); | ||||
|   private server: Server = {}; | ||||
|  | ||||
|   constructor( | ||||
|     private authService: AuthService, | ||||
| @@ -87,7 +88,7 @@ export class AutoRolesRulesComponent implements OnInit { | ||||
|  | ||||
|     this.setFilterForm(); | ||||
|     this.data.getServerFromRoute(this.route).then(server => { | ||||
|  | ||||
|       this.server = server; | ||||
|       this.spinner.showSpinner(); | ||||
|       if (!this.route.snapshot.params["autoRoleId"]) { | ||||
|         this.spinner.hideSpinner(); | ||||
| @@ -121,7 +122,7 @@ export class AutoRolesRulesComponent implements OnInit { | ||||
|   public loadNextPage(): void { | ||||
|     this.loading = true; | ||||
|     this.data.query<AutoRoleRuleQuery>(Queries.autoRoleRulesQuery, { | ||||
|         id: this.sidebar.server$.value?.id, filter: this.filter, page: this.page, sort: this.sort | ||||
|         serverId: this.server.id, autoRoleId: this.autoRoleId, filter: this.filter, page: this.page, sort: this.sort | ||||
|       }, | ||||
|       (x: { servers: Server[] }) => { | ||||
|         if (!x.servers[0].autoRoles || x.servers[0].autoRoles?.length == 0) { | ||||
| @@ -145,6 +146,7 @@ export class AutoRolesRulesComponent implements OnInit { | ||||
|     }); | ||||
|  | ||||
|     this.filterForm.valueChanges.pipe( | ||||
|       takeUntil(this.unsubscriber), | ||||
|       debounceTime(600) | ||||
|     ).subscribe(changes => { | ||||
|       if (changes.id) { | ||||
| @@ -212,13 +214,14 @@ export class AutoRolesRulesComponent implements OnInit { | ||||
|           roleId: newAutoRoleRule.roleId | ||||
|         } | ||||
|       ).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; | ||||
|         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.isEditingNew = false; | ||||
|         this.loadNextPage(); | ||||
|       }); | ||||
|       return; | ||||
|   | ||||
| @@ -14,11 +14,11 @@ import { SidebarService } from "../../../../../../services/sidebar/sidebar.servi | ||||
| import { ActivatedRoute } from "@angular/router"; | ||||
| import { AutoRoleQuery, SingleDiscordQuery } from "../../../../../../models/graphql/query.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 { AutoRoleMutationResult } from "../../../../../../models/graphql/result.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 { ChannelType, Guild } from "../../../../../../models/data/discord.model"; | ||||
| import { Server } from "../../../../../../models/data/server.model"; | ||||
| @@ -29,16 +29,15 @@ import { Server } from "../../../../../../models/data/server.model"; | ||||
|   styleUrls: ["./auto-roles.component.scss"] | ||||
| }) | ||||
| export class AutoRolesComponent implements OnInit { | ||||
|   auto_roles!: AutoRole[]; | ||||
|   guild!: Guild; | ||||
|   channels!: MenuItem[]; | ||||
|   auto_roles: AutoRole[] = []; | ||||
|   guild: Guild = { channels: [], emojis: [], roles: [] }; | ||||
|   channels: MenuItem[] = []; | ||||
|   loading = true; | ||||
|  | ||||
|   clonedUsers: { [s: string]: User; } = {}; | ||||
|   isEditingNew: boolean = false; | ||||
|  | ||||
|   newAutoRoleTemplate: AutoRole = { | ||||
|     id: 0, | ||||
|     createdAt: "", | ||||
|     modifiedAt: "" | ||||
|   }; | ||||
| @@ -60,7 +59,9 @@ export class AutoRolesComponent implements OnInit { | ||||
|     sortDirection: undefined | ||||
|   }; | ||||
|  | ||||
|   totalRecords!: number; | ||||
|   totalRecords: number = 0; | ||||
|   private unsubscriber = new Subject<void>(); | ||||
|   private server: Server = {}; | ||||
|  | ||||
|   constructor( | ||||
|     private authService: AuthService, | ||||
| @@ -79,6 +80,7 @@ export class AutoRolesComponent implements OnInit { | ||||
|  | ||||
|     this.setFilterForm(); | ||||
|     this.data.getServerFromRoute(this.route).then(server => { | ||||
|       this.server = server; | ||||
|       this.spinner.showSpinner(); | ||||
|       this.data.query<SingleDiscordQuery>(Queries.guildsQuery, { | ||||
|           filter: { | ||||
| @@ -101,7 +103,7 @@ export class AutoRolesComponent implements OnInit { | ||||
|   public loadNextPage(): void { | ||||
|     this.loading = true; | ||||
|     this.data.query<AutoRoleQuery>(Queries.autoRolesQuery, { | ||||
|         id: this.sidebar.server$.value?.id, filter: this.filter, page: this.page, sort: this.sort | ||||
|         serverId: this.server.id, filter: this.filter, page: this.page, sort: this.sort | ||||
|       }, | ||||
|       (x: { servers: Server[] }) => { | ||||
|         return x.servers[0]; | ||||
| @@ -123,6 +125,7 @@ export class AutoRolesComponent implements OnInit { | ||||
|     }); | ||||
|  | ||||
|     this.filterForm.valueChanges.pipe( | ||||
|       takeUntil(this.unsubscriber), | ||||
|       debounceTime(600) | ||||
|     ).subscribe(changes => { | ||||
|       if (changes.id) { | ||||
| @@ -190,15 +193,17 @@ export class AutoRolesComponent implements OnInit { | ||||
|  | ||||
|     this.spinner.showSpinner(); | ||||
|     this.data.mutation<AutoRoleMutationResult>(Mutations.createAutoRole, { | ||||
|         serverId: this.sidebar.server$.value?.id, | ||||
|         serverId: this.server.id, | ||||
|         channelId: newAutoRole.channelId, | ||||
|         messageId: newAutoRole.messageId | ||||
|       } | ||||
|     ).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; | ||||
|       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.loadNextPage(); | ||||
|   | ||||
| @@ -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> | ||||
|  | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -0,0 +1,272 @@ | ||||
| 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, Query } 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"; | ||||
| import { Server } from "../../../../../../models/data/server.model"; | ||||
|  | ||||
| @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 = 0; | ||||
|  | ||||
|   public clonedLevels: { [s: string]: Level; } = {}; | ||||
|  | ||||
|   private unsubscriber = new Subject<void>(); | ||||
|   private server: Server = {}; | ||||
|  | ||||
|   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.setFilterForm(); | ||||
|     this.data.getServerFromRoute(this.route).then(server => { | ||||
|       this.server = server; | ||||
|       this.loadNextPage(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public ngOnDestroy(): void { | ||||
|     this.unsubscriber.next(); | ||||
|     this.unsubscriber.complete(); | ||||
|   } | ||||
|  | ||||
|   public loadNextPage(): void { | ||||
|     this.loading = true; | ||||
|     this.data.query<LevelListQuery>(Queries.levelQuery, { | ||||
|         serverId: this.server.id, filter: this.filter, page: this.page, sort: this.sort | ||||
|       }, | ||||
|       (data: Query) => { | ||||
|           return data.servers[0]; | ||||
|         } | ||||
|     ).subscribe(data => { | ||||
|       this.totalRecords = data.levelCount; | ||||
|       this.levels = data.levels; | ||||
|       this.spinner.hideSpinner(); | ||||
|       this.loading = false; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public setFilterForm(): void { | ||||
|     this.filterForm = this.fb.group({ | ||||
|       id: new FormControl<number | null>(null), | ||||
|       name: new FormControl<string | null>(null), | ||||
|       color: new FormControl<string | null>(null), | ||||
|       min_xp: new FormControl<number | null>(null), | ||||
|       permissions: new FormControl<string | null>(null) | ||||
|     }); | ||||
|  | ||||
|     this.filterForm.valueChanges.pipe( | ||||
|       takeUntil(this.unsubscriber), | ||||
|       debounceTime(600) | ||||
|     ).subscribe(changes => { | ||||
|       if (changes.id) { | ||||
|         this.filter.id = changes.id; | ||||
|       } else { | ||||
|         this.filter.id = undefined; | ||||
|       } | ||||
|  | ||||
|       if (changes.name) { | ||||
|         this.filter.name = changes.name; | ||||
|       } else { | ||||
|         this.filter.name = undefined; | ||||
|       } | ||||
|  | ||||
|       if (this.page.pageSize) | ||||
|         this.page.pageSize = 10; | ||||
|  | ||||
|       if (this.page.pageIndex) | ||||
|         this.page.pageIndex = 0; | ||||
|  | ||||
|       this.loadNextPage(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public newLevelTemplate: Level = { | ||||
|     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.server.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; | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -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 { | ||||
|  | ||||
| } | ||||
							
								
								
									
										18
									
								
								kdb-web/src/app/modules/view/server/levels/levels.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								kdb-web/src/app/modules/view/server/levels/levels.module.ts
									
									
									
									
									
										Normal 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 { | ||||
| } | ||||
| @@ -164,7 +164,8 @@ | ||||
|             <p-cellEditor> | ||||
|               <ng-template pTemplate="input"> | ||||
|                 <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 pTemplate="output"> | ||||
|                 {{member.xp}} | ||||
| @@ -225,12 +226,13 @@ | ||||
|             <div class="btn-wrapper"> | ||||
|               <button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-pencil" | ||||
|                       (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" | ||||
|                       icon="pi pi-check-circle" (click)="onRowEditSave(dt, member, ri)"></button> | ||||
|               <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> | ||||
|           </td> | ||||
|         </tr> | ||||
|   | ||||
| @@ -5,19 +5,21 @@ import { SpinnerService } from "../../../../services/spinner/spinner.service"; | ||||
| import { ToastService } from "../../../../services/toast/toast.service"; | ||||
| import { ConfirmationDialogService } from "../../../../services/confirmation-dialog/confirmation-dialog.service"; | ||||
| import { TranslateService } from "@ngx-translate/core"; | ||||
| import { catchError, debounceTime } from "rxjs/operators"; | ||||
| import { catchError, debounceTime, takeUntil } from "rxjs/operators"; | ||||
| import { LazyLoadEvent, MenuItem } from "primeng/api"; | ||||
| import { Table } from "primeng/table"; | ||||
| import { User, UserFilter } from "../../../../models/data/user.model"; | ||||
| import { Queries } from "../../../../models/graphql/queries.model"; | ||||
| import { LevelListQuery, UserListQuery } from "../../../../models/graphql/query.model"; | ||||
| import { LevelListQuery, Query, UserListQuery } from "../../../../models/graphql/query.model"; | ||||
| import { DataService } from "../../../../services/data/data.service"; | ||||
| import { Page } from "../../../../models/graphql/filter/page.model"; | ||||
| import { Sort, SortDirection } from "../../../../models/graphql/filter/sort.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 { ActivatedRoute } from "@angular/router"; | ||||
| import { Level } from "../../../../models/data/level.model"; | ||||
| import { Server } from "../../../../models/data/server.model"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: "app-members", | ||||
| @@ -26,7 +28,6 @@ import { ActivatedRoute } from "@angular/router"; | ||||
| }) | ||||
| export class MembersComponent implements OnInit { | ||||
|   members!: User[]; | ||||
|   // levelsFilter!: MenuItem[]; | ||||
|   levels!: MenuItem[]; | ||||
|   leftServerOptions = [ | ||||
|     { label: this.translate.instant("common.bool_as_string.true"), value: false }, | ||||
| @@ -64,7 +65,7 @@ export class MembersComponent implements OnInit { | ||||
|     discordId: FormControl<number | null>, | ||||
|     name: FormControl<string | null>, | ||||
|     leftServer: FormControl<boolean | null>, | ||||
|     level: FormControl<number | null> | ||||
|     level: FormControl<Level | null> | ||||
|   }>; | ||||
|  | ||||
|   filter: UserFilter = {}; | ||||
| @@ -78,6 +79,8 @@ export class MembersComponent implements OnInit { | ||||
|   }; | ||||
|  | ||||
|   totalRecords!: number; | ||||
|   private unsubscriber = new Subject<void>(); | ||||
|   private server: Server = {}; | ||||
|  | ||||
|   constructor( | ||||
|     private authService: AuthService, | ||||
| @@ -95,11 +98,13 @@ export class MembersComponent implements OnInit { | ||||
|     this.setFilterForm(); | ||||
|  | ||||
|     this.data.getServerFromRoute(this.route).then(server => { | ||||
|       this.server = server; | ||||
|       this.spinner.showSpinner(); | ||||
|       this.data.query<LevelListQuery>(Queries.levelQuery, { | ||||
|           filter: { | ||||
|             server: { id: server.id } | ||||
|           } | ||||
|           serverId: server.id | ||||
|         }, | ||||
|         (data: Query) => { | ||||
|           return data.servers[0]; | ||||
|         } | ||||
|       ).subscribe(data => { | ||||
|         this.levels = data.levels.map(level => { | ||||
| @@ -114,7 +119,10 @@ export class MembersComponent implements OnInit { | ||||
|     this.spinner.showSpinner(); | ||||
|     this.loading = true; | ||||
|     this.data.query<UserListQuery>(Queries.usersQuery, { | ||||
|         filter: this.filter, page: this.page, sort: this.sort | ||||
|         serverId: this.server.id, filter: this.filter, page: this.page, sort: this.sort | ||||
|       }, | ||||
|       (x: { servers: Server[] }) => { | ||||
|         return x.servers[0]; | ||||
|       } | ||||
|     ).subscribe(data => { | ||||
|       this.totalRecords = data.userCount; | ||||
| @@ -130,10 +138,11 @@ export class MembersComponent implements OnInit { | ||||
|       discordId: new FormControl<number | null>(null), | ||||
|       name: [""], | ||||
|       leftServer: new FormControl<boolean | null>(null), | ||||
|       level: new FormControl<number | null>(null) | ||||
|       level: new FormControl<Level | null>(null) | ||||
|     }); | ||||
|  | ||||
|     this.filterForm.valueChanges.pipe( | ||||
|       takeUntil(this.unsubscriber), | ||||
|       debounceTime(600) | ||||
|     ).subscribe(changes => { | ||||
|       if (changes.id) { | ||||
| @@ -162,7 +171,7 @@ export class MembersComponent implements OnInit { | ||||
|  | ||||
|       if (changes.level) { | ||||
|         this.filter.level = { | ||||
|           id: changes.level | ||||
|           id: changes.level.id | ||||
|         }; | ||||
|       } else { | ||||
|         this.filter.level = undefined; | ||||
| @@ -233,7 +242,7 @@ export class MembersComponent implements OnInit { | ||||
|  | ||||
|   } | ||||
|  | ||||
|   onRowEditCancel(user: User, index: number) { | ||||
|   onRowEditCancel(index: number) { | ||||
|     if (this.isEditingNew) { | ||||
|       this.members.splice(index, 1); | ||||
|       delete this.clonedUsers[index]; | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import { UserDTO } from "../../../../models/auth/auth-user.dto"; | ||||
| import { AuthService } from "src/app/services/auth/auth.service"; | ||||
| import { ToastService } from "src/app/services/toast/toast.service"; | ||||
| import { TranslateService } from "@ngx-translate/core"; | ||||
| import { Server } from "../../../../models/data/server.model"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: "app-profile", | ||||
| @@ -18,6 +19,7 @@ import { TranslateService } from "@ngx-translate/core"; | ||||
| export class ProfileComponent implements OnInit { | ||||
|  | ||||
|   user: User = { createdAt: "", modifiedAt: "" }; | ||||
|   private server: Server = {}; | ||||
|  | ||||
|   constructor( | ||||
|     private route: ActivatedRoute, | ||||
| @@ -36,6 +38,7 @@ export class ProfileComponent implements OnInit { | ||||
|         this.router.navigate([`/server/${server.id}`]); | ||||
|         return; | ||||
|       } | ||||
|       this.server = server; | ||||
|  | ||||
|       let authUser = await this.auth.getLoggedInUser(); | ||||
|       this.spinner.showSpinner(); | ||||
| @@ -47,16 +50,20 @@ export class ProfileComponent implements OnInit { | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       this.data.query<User>(Queries.singleUserQuery, { | ||||
|       this.data.query<UserListQuery>(Queries.usersQuery, { | ||||
|           serverId: this.server.id, | ||||
|           filter: { | ||||
|             id: this.route.snapshot.params["memberId"] | ||||
|           } | ||||
|         }, | ||||
|         function(data: UserListQuery) { | ||||
|           return data.users.length > 0 ? data.users[0] : null; | ||||
|         (x: { servers: Server[] }) => { | ||||
|           return x.servers[0]; | ||||
|         } | ||||
|       ).subscribe(user => { | ||||
|         this.user = user; | ||||
|       ).subscribe(users => { | ||||
|         if (!users.users[0]) { | ||||
|           this.router.navigate([`/server/${server.id}`]); | ||||
|         } | ||||
|         this.user = users.users[0]; | ||||
|         this.spinner.hideSpinner(); | ||||
|       }); | ||||
|     }); | ||||
|   | ||||
| @@ -5,14 +5,16 @@ import { ProfileComponent } from "./profile/profile.component"; | ||||
| import { MembersComponent } from "./members/members.component"; | ||||
|  | ||||
| const routes: Routes = [ | ||||
|   { path: '', component: ServerDashboardComponent }, | ||||
|   { path: 'members', component: MembersComponent }, | ||||
|   { path: 'members/:memberId', component: ProfileComponent }, | ||||
|   { path: 'auto-roles', loadChildren: () => import('./auto-role/auto-role.module').then(m => m.AutoRoleModule)}, | ||||
|   { path: "", component: ServerDashboardComponent }, | ||||
|   { path: "members", component: MembersComponent }, | ||||
|   { 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) } | ||||
| ]; | ||||
|  | ||||
| @NgModule({ | ||||
|   imports: [RouterModule.forChild(routes)], | ||||
|   exports: [RouterModule] | ||||
| }) | ||||
| export class ServerRoutingModule { } | ||||
| export class ServerRoutingModule { | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ export class DataService { | ||||
|   public getServerFromRoute(route: ActivatedRoute): Promise<Server> { | ||||
|     return new Promise((resolve, reject) => { | ||||
|       this.spinner.showSpinner(); | ||||
|       if (!route.snapshot.params["serverId"]) { | ||||
|       if (!route.snapshot.params["serverId"] || route.snapshot.params["serverId"] == "undefined") { | ||||
|         this.spinner.hideSpinner(); | ||||
|         this.router.navigate(["/dashboard"]); | ||||
|         reject(); | ||||
|   | ||||
| @@ -18,16 +18,16 @@ export class SidebarService { | ||||
|   menuItems$ = new BehaviorSubject<MenuItem[]>(new Array<MenuItem>()); | ||||
|   server$ = new BehaviorSubject<Server | null>(null); | ||||
|  | ||||
|   dashboard!: MenuItem; | ||||
|   serverDashboard!: MenuItem; | ||||
|   serverProfile!: MenuItem; | ||||
|   serverMembers!: MenuItem; | ||||
|   serverAutoRoles!: MenuItem; | ||||
|   serverAutoRoleRules!: MenuItem; | ||||
|   serverMenu!: MenuItem; | ||||
|   adminConfig!: MenuItem; | ||||
|   adminUsers!: MenuItem; | ||||
|   adminMenu!: MenuItem; | ||||
|   dashboard: MenuItem = {}; | ||||
|   serverDashboard: MenuItem = {}; | ||||
|   serverProfile: MenuItem = {}; | ||||
|   serverMembers: MenuItem = {}; | ||||
|   serverAutoRoles: MenuItem = {}; | ||||
|   serverLevels: MenuItem = {}; | ||||
|   serverMenu: MenuItem = {}; | ||||
|   adminConfig: MenuItem = {}; | ||||
|   adminUsers: MenuItem = {}; | ||||
|   adminMenu: MenuItem = {}; | ||||
|  | ||||
|   constructor( | ||||
|     private themeService: ThemeService, | ||||
| @@ -66,7 +66,11 @@ export class SidebarService { | ||||
|   } | ||||
|  | ||||
|   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 = { | ||||
|       label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.dashboard") : "", | ||||
|       icon: "pi pi-th-large", | ||||
| @@ -74,7 +78,7 @@ export class SidebarService { | ||||
|     }; | ||||
|     this.serverProfile = { | ||||
|       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}` | ||||
|     }; | ||||
|     this.serverMembers = { | ||||
| @@ -91,14 +95,25 @@ export class SidebarService { | ||||
|       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 = { | ||||
|       label: this.isSidebarOpen ? this.server$.value?.name : "", | ||||
|       icon: "pi pi-server", | ||||
|       visible: false, | ||||
|       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 = { | ||||
|       label: this.isSidebarOpen ? this.translateService.instant("sidebar.auth_user_list") : "", | ||||
|       icon: "pi pi-user-edit", | ||||
| @@ -126,6 +141,7 @@ export class SidebarService { | ||||
|         this.serverMenu.visible = true; | ||||
|         this.serverMembers.visible = !!user?.isModerator; | ||||
|         this.serverAutoRoles.visible = !!user?.isAdmin; | ||||
|         this.serverLevels.visible = !!user?.isAdmin; | ||||
|       } else { | ||||
|         this.serverMenu.visible = false; | ||||
|       } | ||||
|   | ||||
| @@ -12,7 +12,6 @@ export class ThemeService { | ||||
|  | ||||
|   sidebarWidth = '150px'; | ||||
|   isSidebarOpen = false; | ||||
|   hasLangChanged = false; | ||||
|  | ||||
|   themeName$ = new BehaviorSubject<string>(Themes.Default); | ||||
|   isSidebarOpen$ = new BehaviorSubject<boolean>(true); | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|     "WebVersion": { | ||||
|         "Major": "1", | ||||
|         "Minor": "0", | ||||
|         "Micro": "0" | ||||
|         "Micro": "dev220" | ||||
|     }, | ||||
|     "Themes": [ | ||||
|         { | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
|       "dashboard": "Dashboard", | ||||
|       "profile": "Dein Profil", | ||||
|       "members": "Mitglieder", | ||||
|       "levels": "Level", | ||||
|       "auto_roles": "Auto Rollen" | ||||
|     }, | ||||
|     "server_empty": "Kein Server ausgewählt", | ||||
| @@ -220,7 +221,7 @@ | ||||
|         "message": { | ||||
|           "user_changed": "Benutzer 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!" | ||||
|         } | ||||
|       }, | ||||
| @@ -281,6 +282,38 @@ | ||||
|             "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": {}, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user