From 85452c9a7469df5facb40c16d54a34c42b4f8190 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 17 Feb 2023 17:53:54 +0100 Subject: [PATCH] Added members view #130 --- kdb-bot/src/bot_graphql/abc/query_abc.py | 6 +- .../src/bot_graphql/filter/level_filter.py | 2 +- kdb-bot/src/bot_graphql/filter/user_filter.py | 32 ++- kdb-bot/src/bot_graphql/model/user.gql | 3 + .../bot_graphql/mutations/user_mutation.py | 11 + kdb-bot/src/bot_graphql/queries/user_query.py | 4 + kdb-web/src/app/models/data/level.model.ts | 17 ++ kdb-web/src/app/models/data/server.model.ts | 14 +- kdb-web/src/app/models/data/user.model.ts | 36 +++ .../src/app/models/graphql/mutations.model.ts | 17 ++ .../src/app/models/graphql/queries.model.ts | 57 +++++ kdb-web/src/app/models/graphql/query.model.ts | 12 + .../src/app/models/graphql/result.model.ts | 8 +- .../auth-user/auth-user.component.html | 20 +- .../server/members/members.component.html | 192 ++++++++++++++ .../server/members/members.component.scss | 0 .../server/members/members.component.spec.ts | 23 ++ .../view/server/members/members.component.ts | 235 ++++++++++++++++++ .../view/server/server-routing.module.ts | 2 + .../app/modules/view/server/server.module.ts | 4 +- kdb-web/src/app/services/data/data.service.ts | 10 + kdb-web/src/assets/i18n/de.json | 24 ++ kdb-web/src/styles.scss | 28 ++- kdb-web/src/styles/primeng-fixes.scss | 16 +- 24 files changed, 724 insertions(+), 49 deletions(-) create mode 100644 kdb-web/src/app/models/data/level.model.ts create mode 100644 kdb-web/src/app/models/data/user.model.ts create mode 100644 kdb-web/src/app/models/graphql/mutations.model.ts create mode 100644 kdb-web/src/app/modules/view/server/members/members.component.html create mode 100644 kdb-web/src/app/modules/view/server/members/members.component.scss create mode 100644 kdb-web/src/app/modules/view/server/members/members.component.spec.ts create mode 100644 kdb-web/src/app/modules/view/server/members/members.component.ts diff --git a/kdb-bot/src/bot_graphql/abc/query_abc.py b/kdb-bot/src/bot_graphql/abc/query_abc.py index b9e9f444..11fd724a 100644 --- a/kdb-bot/src/bot_graphql/abc/query_abc.py +++ b/kdb-bot/src/bot_graphql/abc/query_abc.py @@ -180,12 +180,12 @@ class QueryABC(ObjectType): # @FilterABC.resolve_filter_annotation def _resolve_collection(self, collection: List, *_, filter: FilterABC = None, page: Page = None, sort: Sort = None): if filter is not None: - return filter.filter(collection) + collection = filter.filter(collection) if page is not None: - return page.filter(collection) + collection = page.filter(collection) if sort is not None: - return sort.filter(collection) + collection = sort.filter(collection) return collection diff --git a/kdb-bot/src/bot_graphql/filter/level_filter.py b/kdb-bot/src/bot_graphql/filter/level_filter.py index c34ae63f..333c1f5a 100644 --- a/kdb-bot/src/bot_graphql/filter/level_filter.py +++ b/kdb-bot/src/bot_graphql/filter/level_filter.py @@ -25,7 +25,7 @@ class LevelFilter(FilterABC): if "server" in values: from bot_graphql.filter.server_filter import ServerFilter - self._server: ServerFilter = self._services.get_service(LevelFilter) + self._server: ServerFilter = self._services.get_service(ServerFilter) self._server.from_dict(values["server"]) def filter(self, query: List[Level]) -> List[Level]: diff --git a/kdb-bot/src/bot_graphql/filter/user_filter.py b/kdb-bot/src/bot_graphql/filter/user_filter.py index d5122d87..7520fd9b 100644 --- a/kdb-bot/src/bot_graphql/filter/user_filter.py +++ b/kdb-bot/src/bot_graphql/filter/user_filter.py @@ -5,6 +5,7 @@ from cpl_discord.service import DiscordBotServiceABC from cpl_query.extension import List from bot_core.abc.client_utils_abc import ClientUtilsABC +from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.model.user import User from bot_graphql.abc.filter_abc import FilterABC from bot_graphql.filter.level_filter import LevelFilter @@ -18,6 +19,7 @@ class UserFilter(FilterABC): bot: DiscordBotServiceABC, client_utils: ClientUtilsABC, levels: LevelService, + user_joined_servers: UserJoinedServerRepositoryABC, ): FilterABC.__init__(self) @@ -25,6 +27,7 @@ class UserFilter(FilterABC): self._bot = bot self._client_utils = client_utils self._levels = levels + self._user_joined_servers = user_joined_servers self._id = None self._discord_id = None @@ -34,6 +37,7 @@ class UserFilter(FilterABC): self._ontime = None self._level: Optional[LevelFilter] = None self._server = None + self._left_server = None def from_dict(self, values: dict): if "id" in values: @@ -64,18 +68,26 @@ class UserFilter(FilterABC): self._server: ServerFilter = self._services.get_service(ServerFilter) self._server.from_dict(values["server"]) + if "leftServer" in values: + self._left_server = values["leftServer"] + def filter(self, query: List[User]) -> List[User]: if self._id is not None: - query = query.where(lambda x: x.user_id == self._id) + query = query.where(lambda x: x.user_id == self._id or str(self._id) in str(x.user_id)) if self._discord_id is not None: - query = query.where(lambda x: x.discord_id == self._discord_id) + query = query.where( + lambda x: x.discord_id == self._discord_id or str(self._discord_id) in str(x.discord_id) + ) if self._name is not None: - query = query.where( - lambda x: self._bot.get_user(x.discord_id).name == self._name - or self._name in self._bot.get_user(x.discord_id).name - ) + + def _get_member(user: User): + guild = self._bot.get_guild(user.server.discord_server_id) + member = guild.get_member(user.discord_id) + return member is not None and (member.name == self._name or self._name in member.name) + + query = query.where(_get_member) if self._xp is not None: query = query.where(lambda x: x.xp == self._xp) @@ -94,4 +106,12 @@ class UserFilter(FilterABC): servers = self._server.filter(query.select(lambda x: x.server)).select(lambda x: x.server_id) query = query.where(lambda x: x.server.server_id in servers) + if self._left_server is not None: + + def _has_user_left_server(user: User): + active_join = self._user_joined_servers.find_active_user_joined_server_by_user_id(user.user_id) + return (active_join is None) == self._left_server + + query = query.where(_has_user_left_server) + return query diff --git a/kdb-bot/src/bot_graphql/model/user.gql b/kdb-bot/src/bot_graphql/model/user.gql index 853b1ad4..ee8b27cb 100644 --- a/kdb-bot/src/bot_graphql/model/user.gql +++ b/kdb-bot/src/bot_graphql/model/user.gql @@ -17,6 +17,7 @@ type User implements TableQuery { userJoinedGameServers(filter: UserJoinedGameServerFilter, page: Page, sort: Sort): [UserJoinedGameServer] server: Server + leftServer: Boolean createdAt: String modifiedAt: String @@ -31,6 +32,7 @@ input UserFilter { ontime: Float level: LevelFilter server: ServerFilter + leftServer: Boolean } type UserMutation { @@ -40,4 +42,5 @@ type UserMutation { input UserInput { id: ID xp: Int + levelId: ID } \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/mutations/user_mutation.py b/kdb-bot/src/bot_graphql/mutations/user_mutation.py index 354441e4..4a31a615 100644 --- a/kdb-bot/src/bot_graphql/mutations/user_mutation.py +++ b/kdb-bot/src/bot_graphql/mutations/user_mutation.py @@ -1,10 +1,12 @@ from cpl_core.database.context import DatabaseContextABC from cpl_discord.service import DiscordBotServiceABC +from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.model.user_role_enum import UserRoleEnum from bot_graphql.abc.query_abc import QueryABC +from modules.level.service.level_service import LevelService from modules.permission.service.permission_service import PermissionService @@ -16,6 +18,8 @@ class UserMutation(QueryABC): bot: DiscordBotServiceABC, db: DatabaseContextABC, permissions: PermissionService, + levels: LevelRepositoryABC, + level_service: LevelService, ): QueryABC.__init__(self, "UserMutation") @@ -24,6 +28,8 @@ class UserMutation(QueryABC): self._bot = bot self._db = db self._permissions = permissions + self._levels = levels + self._level_service = level_service self.set_field("updateUser", self.resolve_update_user) @@ -33,8 +39,13 @@ class UserMutation(QueryABC): user.xp = input["xp"] if "xp" in input else user.xp + if "levelId" in input: + level = self._levels.get_level_by_id(input["levelId"]) + user.xp = level.min_xp + self._users.update_user(user) self._db.save_changes() + self._level_service.set_level(user) user = self._users.get_user_by_id(input["id"]) return user diff --git a/kdb-bot/src/bot_graphql/queries/user_query.py b/kdb-bot/src/bot_graphql/queries/user_query.py index 0c527515..0b584581 100644 --- a/kdb-bot/src/bot_graphql/queries/user_query.py +++ b/kdb-bot/src/bot_graphql/queries/user_query.py @@ -57,6 +57,7 @@ class UserQuery(DataQueryABC): UserJoinedGameServerFilter, ) self.set_field("server", self.resolve_server) + self.set_field("leftServer", self.resolve_left_server) @staticmethod def resolve_id(user: User, *_): @@ -88,3 +89,6 @@ class UserQuery(DataQueryABC): @staticmethod def resolve_server(user: User, *_): return user.server + + def resolve_left_server(self, user: User, *_): + return self._ujs.find_active_user_joined_server_by_user_id(user.user_id) is None diff --git a/kdb-web/src/app/models/data/level.model.ts b/kdb-web/src/app/models/data/level.model.ts new file mode 100644 index 00000000..932070a7 --- /dev/null +++ b/kdb-web/src/app/models/data/level.model.ts @@ -0,0 +1,17 @@ +import { Data } from "./data.model"; +import { Server, ServerFilter } from "./server.model"; + +export interface Level extends Data { + id?: number; + name?: string; + color?: string; + minXp?: number; + permissions?: string; + server?: Server; +} + +export interface LevelFilter { + id?: number; + name?: String; + server?: ServerFilter; +} diff --git a/kdb-web/src/app/models/data/server.model.ts b/kdb-web/src/app/models/data/server.model.ts index 6be87ab6..a6ae0d99 100644 --- a/kdb-web/src/app/models/data/server.model.ts +++ b/kdb-web/src/app/models/data/server.model.ts @@ -1,8 +1,10 @@ import { Data } from "./data.model"; +import { User } from "./user.model"; +import { Level } from "./level.model"; export interface Server extends Data { id?: number; - discordId?: number; + discordId?: String; name?: string; iconURL?: string; autoRoleCount?: number; @@ -10,7 +12,13 @@ export interface Server extends Data { clientCount?: number; clients?: []; levelCount?: number; - levels?: []; + levels?: Level[]; userCount?: number; - users?: []; + users?: User[]; +} + +export interface ServerFilter { + id?: number; + discordId?: String; + name?: String; } diff --git a/kdb-web/src/app/models/data/user.model.ts b/kdb-web/src/app/models/data/user.model.ts new file mode 100644 index 00000000..494ec567 --- /dev/null +++ b/kdb-web/src/app/models/data/user.model.ts @@ -0,0 +1,36 @@ +import { Data } from "./data.model"; +import { Level, LevelFilter } from "./level.model"; +import { Server, ServerFilter } from "./server.model"; + +export interface User extends Data { + id?: number; + discordId?: number; + name?: string; + xp?: number; + minecraftId?: number; + ontime?: number; + level?: Level; + server?: Server; + leftServer?: boolean; + + joinedServerCount?: number; + joinedServers?: []; + + joinedVoiceChannelCount?: number; + joinedVoiceChannels?: []; + + userJoinedGameServerCount?: number; + userJoinedGameServers?: []; +} + +export interface UserFilter { + id?: number; + discordId?: number; + name?: string; + xp?: number; + minecraftId?: number; + ontime?: number; + level?: LevelFilter; + server?: ServerFilter; + leftServer?: boolean; +} diff --git a/kdb-web/src/app/models/graphql/mutations.model.ts b/kdb-web/src/app/models/graphql/mutations.model.ts new file mode 100644 index 00000000..f7be1c11 --- /dev/null +++ b/kdb-web/src/app/models/graphql/mutations.model.ts @@ -0,0 +1,17 @@ +export class Mutations { + static updateUser = ` + mutation updateUser($id: ID, $xp: Int, $levelId: ID) { + user { + updateUser(input: { id: $id, xp: $xp, levelId: $levelId }) { + id + name + xp + level { + id + name + } + } + } + } + `; +} diff --git a/kdb-web/src/app/models/graphql/queries.model.ts b/kdb-web/src/app/models/graphql/queries.model.ts index ce12391d..4fe54101 100644 --- a/kdb-web/src/app/models/graphql/queries.model.ts +++ b/kdb-web/src/app/models/graphql/queries.model.ts @@ -21,4 +21,61 @@ 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 { + id + name + } + } + } + `; + + static usersQuery= ` + query UsersList($filter: UserFilter, $page: Page, $sort: Sort) { + userCount + users(filter: $filter, page: $page, sort: $sort) { + id + discordId + name + xp + ontime + level { + id + name + } + server { + id + name + } + leftServer + + joinedServerCount + joinedServers { + id + } + + joinedVoiceChannelCount + joinedVoiceChannels { + id + channelId + channelName + } + + userJoinedGameServerCount + userJoinedGameServers { + id + gameServer + } + } + } + `; } diff --git a/kdb-web/src/app/models/graphql/query.model.ts b/kdb-web/src/app/models/graphql/query.model.ts index 0e62f16a..ed6eb037 100644 --- a/kdb-web/src/app/models/graphql/query.model.ts +++ b/kdb-web/src/app/models/graphql/query.model.ts @@ -1,6 +1,18 @@ import { Server } from "../data/server.model"; +import { User } from "../data/user.model"; export interface Query { serverCount: number; servers: Server[]; } + +export interface UserListQuery { + userCount: number; + users: User[]; +} + +export interface LevelListQuery { + levelCount: number; + levels: User[]; +} + diff --git a/kdb-web/src/app/models/graphql/result.model.ts b/kdb-web/src/app/models/graphql/result.model.ts index 4d3d5144..bc28521d 100644 --- a/kdb-web/src/app/models/graphql/result.model.ts +++ b/kdb-web/src/app/models/graphql/result.model.ts @@ -1,5 +1,11 @@ -import { Query } from "./query.model"; +import { User } from "../data/user.model"; export interface QueryResult { data: any; } + +export interface UpdateUserMutationResult { + user: { + updateUser: User + }; +} diff --git a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html index 86d29795..29e5916b 100644 --- a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html +++ b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html @@ -32,7 +32,7 @@
{{'admin.auth_users.headers.first_name' | translate}}
- +
@@ -70,24 +70,11 @@ - -
-
{{'admin.auth_users.headers.created_at' | translate}}
-
- - - -
-
{{'admin.auth_users.headers.modified_at' | translate}}
-
- -
{{'admin.auth_users.headers.actions' | translate}}
- @@ -108,7 +95,7 @@
- +
@@ -223,7 +210,7 @@ - + {{'admin.auth_users.no_entries_found' | translate}} @@ -234,3 +221,4 @@ + diff --git a/kdb-web/src/app/modules/view/server/members/members.component.html b/kdb-web/src/app/modules/view/server/members/members.component.html new file mode 100644 index 00000000..841daf53 --- /dev/null +++ b/kdb-web/src/app/modules/view/server/members/members.component.html @@ -0,0 +1,192 @@ +

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

+
+
+ + + +
+
+ {{members.length}} {{'view.server.members.of' | translate}} + {{dt.totalRecords}} + + {{'view.server.members.members' | translate}} +
+ +
+ +
+
+
+ + + + +
+
{{'view.server.members.headers.id' | translate}}
+ +
+ + + +
+
{{'view.server.members.headers.discord_id' | translate}}
+ +
+ + + +
+
{{'view.server.members.headers.name' | translate}}
+ +
+ + + +
+
{{'view.server.members.headers.xp' | translate}}
+ +
+ + + +
+
{{'view.server.members.headers.ontime' | translate}}
+ +
+ + + +
+
{{'view.server.members.headers.level' | translate}}
+ +
+ + + +
+
{{'view.server.members.headers.actions' | translate}}
+
+ + + + +
+ +
+ + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + + +
+ + + + + + + {{member.id}} + + + {{member.id}} + + + + + + + {{member.discordId}} + + + {{member.discordId}} + + + + + + + {{member.name}} + + + {{member.name}} + + + + + + + + + + {{member.xp}} + + + + + + + {{member.ontime}} + + + {{member.ontime}} + + + + + + + + + + {{member.level.name}} + + + + +
+ + + + +
+ + +
+ + + + + {{'view.server.members.no_entries_found' | translate}} + + + + + + +
+
+
diff --git a/kdb-web/src/app/modules/view/server/members/members.component.scss b/kdb-web/src/app/modules/view/server/members/members.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/view/server/members/members.component.spec.ts b/kdb-web/src/app/modules/view/server/members/members.component.spec.ts new file mode 100644 index 00000000..f626726f --- /dev/null +++ b/kdb-web/src/app/modules/view/server/members/members.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MembersComponent } from './members.component'; + +describe('MembersComponent', () => { + let component: MembersComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MembersComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MembersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/view/server/members/members.component.ts b/kdb-web/src/app/modules/view/server/members/members.component.ts new file mode 100644 index 00000000..bec8df5a --- /dev/null +++ b/kdb-web/src/app/modules/view/server/members/members.component.ts @@ -0,0 +1,235 @@ +import { Component } from "@angular/core"; +import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; +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 { TranslateService } from "@ngx-translate/core"; +import { catchError, debounceTime } 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 { DataService } from "../../../../services/data/data.service"; +import { Page } from "../../../../models/graphql/filter/page.model"; +import { Sort } from "../../../../models/graphql/filter/sort.model"; +import { SidebarService } from "../../../../services/sidebar/sidebar.service"; +import { Mutations } from "../../../../models/graphql/mutations.model"; +import { throwError } from "rxjs"; +import { UpdateUserMutationResult } from "../../../../models/graphql/result.model"; + +@Component({ + selector: "app-members", + templateUrl: "./members.component.html", + styleUrls: ["./members.component.scss"] +}) +export class MembersComponent { + members!: User[]; + // levelsFilter!: MenuItem[]; + levels!: MenuItem[]; + loading = true; + + clonedUsers: { [s: string]: User; } = {}; + isEditingNew: boolean = false; + + newUserTemplate: User = { + id: 0, + discordId: 0, + name: "", + xp: 0, + ontime: 0, + level: undefined, + server: undefined, + + joinedServerCount: 0, + joinedServers: [], + + joinedVoiceChannelCount: 0, + joinedVoiceChannels: [], + + userJoinedGameServerCount: 0, + userJoinedGameServers: [], + + createdAt: "", + modifiedAt: "" + }; + + filterForm!: FormGroup<{ + id: FormControl, + discordId: FormControl, + name: FormControl, + level: FormControl + }>; + + filter: UserFilter = { + leftServer: false + }; + page: Page = { + pageSize: undefined, + pageIndex: undefined + }; + sort: Sort = { + sortColumn: undefined, + sortDirection: undefined + }; + + totalRecords!: number; + + 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 + ) { + } + + ngOnInit(): void { + this.spinner.showSpinner(); + this.data.query(Queries.levelQuery, { + filter: { + server: { id: this.sidebar.server$.value?.id } + } + } + ).subscribe(data => { + this.levels = data.levels.map(level => { + return { label: level.name, value: level }; + }); + this.spinner.hideSpinner(); + }); + + this.setFilterForm(); + this.loadNextPage(); + } + + setFilterForm() { + this.filterForm = this.fb.group({ + id: new FormControl(null), + discordId: new FormControl(null), + name: [""], + level: new FormControl(null) + }); + + this.filterForm.valueChanges.pipe( + debounceTime(600) + ).subscribe(changes => { + if (changes.id) { + this.filter.id = changes.id; + } else { + this.filter.id = undefined; + } + + if (changes.discordId) { + this.filter.discordId = changes.discordId; + } else { + this.filter.discordId = undefined; + } + + if (changes.name) { + this.filter.name = changes.name; + } else { + this.filter.name = undefined; + } + + if (changes.level) { + this.filter.level = { + id: changes.level + }; + } else { + this.filter.level = undefined; + } + + if (this.page.pageSize) + this.page.pageSize = 10; + + if (this.page.pageIndex) + this.page.pageIndex = 0; + + this.loadNextPage(); + }); + } + + loadNextPage() { + this.loading = true; + this.data.query(Queries.usersQuery, { + filter: this.filter, page: this.page, sort: this.sort + } + ).subscribe(data => { + this.totalRecords = data.userCount; + this.members = data.users; + this.spinner.hideSpinner(); + this.loading = false; + }); + } + + nextPage(event: LazyLoadEvent) { + 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 ? "asc" : event.sortOrder === -1 ? "desc" : "asc"; + + this.loadNextPage(); + } + + resetFilters() { + this.filterForm.reset(); + } + + onRowEditInit(table: Table, user: User, index: number) { + this.clonedUsers[index] = { ...user }; + } + + onRowEditSave(table: Table, newUser: User, index: number) { + // 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(newUser) === JSON.stringify(this.newUserTemplate)) { + this.isEditingNew = false; + this.members.splice(index, 1); + return; + } + + if (!newUser.id || !newUser.xp && !newUser.level?.id) { + return; + } + + this.spinner.showSpinner(); + this.data.mutation(Mutations.updateUser, { + id: newUser.id, + xp: newUser.xp, + levelId: newUser.level?.id + } + ).pipe(catchError(err => { + this.spinner.hideSpinner(); + this.toastService.error(this.translate.instant("view.server.members.message.user_change_failed"), this.translate.instant("view.server.members.message.user_change_failed_d", { name: newUser.name })); + return throwError(err); + })).subscribe(_ => { + this.spinner.hideSpinner(); + this.toastService.success(this.translate.instant("view.server.members.message.user_changed"), this.translate.instant("view.server.members.message.user_changed_d", { name: newUser.name })); + this.loadNextPage(); + }); + + } + + onRowEditCancel(user: User, index: number) { + if (this.isEditingNew) { + this.members.splice(index, 1); + delete this.clonedUsers[index]; + this.isEditingNew = false; + return; + } + + this.members[index] = this.clonedUsers[index]; + delete this.clonedUsers[index]; + } +} diff --git a/kdb-web/src/app/modules/view/server/server-routing.module.ts b/kdb-web/src/app/modules/view/server/server-routing.module.ts index 4d89b0f9..0b732e97 100644 --- a/kdb-web/src/app/modules/view/server/server-routing.module.ts +++ b/kdb-web/src/app/modules/view/server/server-routing.module.ts @@ -2,10 +2,12 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; import { ServerDashboardComponent } from "./server-dashboard/server-dashboard.component"; import { ProfileComponent } from "./profile/profile.component"; +import { MembersComponent } from "./members/members.component"; const routes: Routes = [ { path: '', component: ServerDashboardComponent }, { path: 'profile', component: ProfileComponent }, + { path: 'members', component: MembersComponent }, ]; @NgModule({ diff --git a/kdb-web/src/app/modules/view/server/server.module.ts b/kdb-web/src/app/modules/view/server/server.module.ts index 03adf64a..dad9964a 100644 --- a/kdb-web/src/app/modules/view/server/server.module.ts +++ b/kdb-web/src/app/modules/view/server/server.module.ts @@ -4,13 +4,15 @@ import { ServerDashboardComponent } from './server-dashboard/server-dashboard.co import { ServerRoutingModule } from './server-routing.module'; import { SharedModule } from '../../shared/shared.module'; import { ProfileComponent } from './profile/profile.component'; +import { MembersComponent } from './members/members.component'; @NgModule({ declarations: [ ServerDashboardComponent, - ProfileComponent + ProfileComponent, + MembersComponent ], imports: [ CommonModule, diff --git a/kdb-web/src/app/services/data/data.service.ts b/kdb-web/src/app/services/data/data.service.ts index af6f2a35..c0b516a7 100644 --- a/kdb-web/src/app/services/data/data.service.ts +++ b/kdb-web/src/app/services/data/data.service.ts @@ -37,4 +37,14 @@ export class DataService { .pipe(map((d) => f ? f(d) : d)); } + public mutation(query: string, variables?: object, f?: Function): Observable { + return this.http + .post<{ data: T }>(`${this.appsettings.getApiURL()}/api/graphql`, { + query: query, + variables: variables + }) + .pipe(map((d) => d.data)) + .pipe(map((d) => f ? f(d) : d)); + } + } diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json index e03ba0b5..a2f6b194 100644 --- a/kdb-web/src/assets/i18n/de.json +++ b/kdb-web/src/assets/i18n/de.json @@ -65,6 +65,7 @@ "first_name": "Vorname", "last_name": "Nachname", "e_mail": "E-Mail", + "auth_role": "Rolle", "active": "Aktiv", "role": "Rolle", "password": "Passwort", @@ -160,6 +161,29 @@ "header": "Server", "dashboard": { "header": "Server dashboard" + }, + "members": { + "header": "Mitglieder", + "of": "von", + "add": "Hinzufügen", + "reset_filters": "Filter zurücksetzen", + "members": "Mitgliedern", + "headers": { + "id": "ID", + "discord_id": "Discord ID", + "name": "Name", + "xp": "XP", + "ontime": "Ontime", + "level": "Level", + "actions": "Aktionen" + }, + "no_entries_found": "Keine Einträge gefunden", + "message": { + "user_changed": "Benutzer geändert", + "user_changed_d": "Benutzer {{name}} erfolgreich geändert", + "user_change_failed": "Benutzer änderung fehlgeschlagen", + "user_change_failed_d": "Benutzer {{name}} konnte nicht geändert werden!" + } } }, "user-list": {}, diff --git a/kdb-web/src/styles.scss b/kdb-web/src/styles.scss index 9d6c4649..ba48341f 100644 --- a/kdb-web/src/styles.scss +++ b/kdb-web/src/styles.scss @@ -60,7 +60,7 @@ header { text-align: right; flex: 1; justify-content: flex-end; - margin: 0px 5px 0px 0px; + margin: 0 5px 0 0; } .p-menu-overlay { @@ -122,7 +122,7 @@ header { display: flex; flex-direction: row; flex: 1; - margin: 1.5px 0px; + margin: 1.5px 0; } .content-column { @@ -147,7 +147,7 @@ header { } .content-divider { - margin: 5px 0px; + margin: 5px 0; } .content-input-field { @@ -165,7 +165,7 @@ header { } .input-field-info-text { - margin: 15px 0px; + margin: 15px 0; width: 100%; } @@ -212,6 +212,10 @@ header { } } + .table-header-small { + width: 25px; + } + .table-header-actions { width: 100px; } @@ -260,7 +264,7 @@ header { gap: 10px; .name { - margin: 0px; + margin: 0; justify-content: center; align-items: center; @@ -287,7 +291,7 @@ header { } .p-dialog-footer { - padding: 0px 20px 20px 20px !important; + padding: 0 20px 20px 20px !important; } .p-dialog-content { @@ -295,7 +299,7 @@ header { display: flex; flex-direction: row; flex: 1; - margin: 1.5px 0px; + margin: 1.5px 0; } .content-column { @@ -320,7 +324,7 @@ header { } .content-divider { - margin: 5px 0px; + margin: 5px 0; } .content-input-field { @@ -332,7 +336,7 @@ header { footer { width: 100%; height: $footerHeight; - padding: 0px 10px; + padding: 0 10px; display: flex; align-items: center; @@ -347,7 +351,7 @@ footer { // } .version-divider { - margin: 0px 5px; + margin: 0 5px; } // .backend-version { @@ -394,7 +398,7 @@ footer { } .input-field-info-text { - margin: 15px 0px; + margin: 15px 0; width: 100%; } @@ -435,7 +439,7 @@ footer { } .input-field { - margin: 15px 0px; + margin: 15px 0; input, .p-password { diff --git a/kdb-web/src/styles/primeng-fixes.scss b/kdb-web/src/styles/primeng-fixes.scss index 7361d673..dccf88d9 100644 --- a/kdb-web/src/styles/primeng-fixes.scss +++ b/kdb-web/src/styles/primeng-fixes.scss @@ -11,15 +11,15 @@ background: none !important; border: none !important; width: auto !important; - border-radius: 0px !important; + border-radius: 0 !important; padding: 0 !important; .p-menuitem-link, .p-panelmenu-header > a, .p-panelmenu-content .p-menuitem .p-menuitem-link { $distance: 10px; - padding: $distance 0px $distance $distance !important; - margin: 4px 0px 4px 6px !important; + padding: $distance 0 $distance $distance !important; + margin: 4px 0 4px 6px !important; } } @@ -44,8 +44,8 @@ header, .p-panelmenu-header > a { border: none !important; - border-radius: none !important; - font-weight: none !important; + border-radius: unset !important; + font-weight: unset !important; transition: none !important; } @@ -71,8 +71,12 @@ ui-menu .ui-menu-parent .ui-menu-child { box-shadow: none !important; } +.p-datatable > .p-datatable-wrapper { + overflow: visible !important; +} + .p-password { - padding: 0px !important; + padding: 0 !important; } .p-paginator {