diff --git a/bot/src/bot_data/abc/user_warnings_repository_abc.py b/bot/src/bot_data/abc/user_warnings_repository_abc.py index bb5d9e9b..0fb36df5 100644 --- a/bot/src/bot_data/abc/user_warnings_repository_abc.py +++ b/bot/src/bot_data/abc/user_warnings_repository_abc.py @@ -18,6 +18,10 @@ class UserWarningsRepositoryABC(ABC): def get_user_warnings_by_id(self, id: int) -> UserWarnings: pass + @abstractmethod + def get_user_warnings_by_server_id(self, server_id: int) -> List[UserWarnings]: + pass + @abstractmethod def get_user_warnings_by_user_id(self, user_id: int) -> List[UserWarnings]: pass diff --git a/bot/src/bot_data/model/user_warnings.py b/bot/src/bot_data/model/user_warnings.py index 99610a5f..40aeb064 100644 --- a/bot/src/bot_data/model/user_warnings.py +++ b/bot/src/bot_data/model/user_warnings.py @@ -59,6 +59,17 @@ class UserWarnings(TableABC): """ ) + @staticmethod + def get_select_by_server_id_string(id: int) -> str: + return str( + f""" + SELECT `UserWarnings`.* FROM `UserWarnings` + INNER JOIN `Users` + ON `Users`.`UserId` = `UserWarnings`.`UserId` + WHERE `Users`.`ServerId` = {id}; + """ + ) + @staticmethod def get_select_by_user_id_string(id: int) -> str: return str( diff --git a/bot/src/bot_data/service/user_warnings_repository_service.py b/bot/src/bot_data/service/user_warnings_repository_service.py index bae46987..e9e5b5d5 100644 --- a/bot/src/bot_data/service/user_warnings_repository_service.py +++ b/bot/src/bot_data/service/user_warnings_repository_service.py @@ -56,6 +56,20 @@ class UserWarningsRepositoryService(UserWarningsRepositoryABC): self._logger.trace(__name__, f"Send SQL command: {UserWarnings.get_select_by_id_string(id)}") return self._from_result(self._context.select(UserWarnings.get_select_by_id_string(id))[0]) + def get_user_warnings_by_server_id(self, server_id: int) -> List[UserWarnings]: + self._logger.trace( + __name__, + f"Send SQL command: {UserWarnings.get_select_by_server_id_string(server_id)}", + ) + + return List( + UserWarnings, + [ + self._from_result(warning) + for warning in self._context.select(UserWarnings.get_select_by_server_id_string(server_id)) + ], + ) + def get_user_warnings_by_user_id(self, user_id: int) -> List[UserWarnings]: self._logger.trace( __name__, diff --git a/bot/src/bot_graphql/graphql/server.gql b/bot/src/bot_graphql/graphql/server.gql index 63d07b3f..80beb832 100644 --- a/bot/src/bot_graphql/graphql/server.gql +++ b/bot/src/bot_graphql/graphql/server.gql @@ -29,6 +29,9 @@ type Server implements TableWithHistoryQuery { activeUserCount: Int users(filter: UserFilter, page: Page, sort: Sort): [User] + userWarningCount: Int + userWarnings(filter: UserWarningFilter, page: Page, sort: Sort): [UserWarning] + achievementCount: Int achievements(filter: AchievementFilter, page: Page, sort: Sort): [Achievement] diff --git a/bot/src/bot_graphql/mutations/user_mutation.py b/bot/src/bot_graphql/mutations/user_mutation.py index 8d5fb779..e2494c52 100644 --- a/bot/src/bot_graphql/mutations/user_mutation.py +++ b/bot/src/bot_graphql/mutations/user_mutation.py @@ -86,5 +86,9 @@ class UserMutation(QueryABC): continue member = self._bot.get_guild(user.server.discord_id).get_member(user.discord_id) - author = self._users.get_user_by_id(int(warning["author"])) + if "author" not in warning: + author = Route.get_user().users.where(lambda u: u.server.id == user.server.id).single() + else: + author = self._users.get_user_by_id(int(warning["author"])) + self._user_warning_service.add_warnings(member, warning["description"], author.discord_id) diff --git a/bot/src/bot_graphql/queries/server_query.py b/bot/src/bot_graphql/queries/server_query.py index 33ccf9dc..112a4555 100644 --- a/bot/src/bot_graphql/queries/server_query.py +++ b/bot/src/bot_graphql/queries/server_query.py @@ -17,6 +17,7 @@ from bot_data.abc.user_joined_voice_channel_repository_abc import ( UserJoinedVoiceChannelRepositoryABC, ) from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.abc.user_warnings_repository_abc import UserWarningsRepositoryABC from bot_data.model.server import Server from bot_data.model.server_config import ServerConfig from bot_data.model.server_history import ServerHistory @@ -28,6 +29,7 @@ from bot_graphql.filter.level_filter import LevelFilter from bot_graphql.filter.scheduled_event_filter import ScheduledEventFilter from bot_graphql.filter.short_role_name_filter import ShortRoleNameFilter from bot_graphql.filter.user_filter import UserFilter +from bot_graphql.filter.user_warning_filter import UserWarningFilter from bot_graphql.model.server_statistics import ServerStatistics @@ -48,6 +50,7 @@ class ServerQuery(DataQueryWithHistoryABC): short_role_names: ShortRoleNameRepositoryABC, scheduled_events: ScheduledEventRepositoryABC, server_configs: ServerConfigRepositoryABC, + user_warnings: UserWarningsRepositoryABC, ): DataQueryWithHistoryABC.__init__(self, "Server", "ServersHistory", ServerHistory, db) @@ -89,6 +92,11 @@ class ServerQuery(DataQueryWithHistoryABC): lambda server, *_: self._users.get_users_with_activity_by_server_id(server.id), UserFilter, ) + self.add_collection( + "userWarning", + lambda server, *_: user_warnings.get_user_warnings_by_server_id(server.id), + UserWarningFilter, + ) self.add_collection( "gameServer", lambda server, *_: game_servers.get_game_servers_by_server_id(server.id), diff --git a/web/src/app/models/graphql/queries.model.ts b/web/src/app/models/graphql/queries.model.ts index afa3ab68..e5448075 100644 --- a/web/src/app/models/graphql/queries.model.ts +++ b/web/src/app/models/graphql/queries.model.ts @@ -377,6 +377,22 @@ export class Queries { } `; + static liteUsersQuery = ` + 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 + + createdAt + modifiedAt + } + } + } + `; + static userProfile = ` query UserProfile($serverId: ID, $userId: ID, $page: Page, $sort: Sort) { userCount @@ -503,6 +519,29 @@ export class Queries { } `; + static userWarningsQuery = ` + query userWarnings($serverId: ID, $page: Page, $sort: Sort) { + servers(filter: {id: $serverId}) { + userWarningCount + userWarnings(page: $page, sort: $sort) { + id + user { + id + name + } + description + author { + id + name + } + + createdAt + modifiedAt + } + } + } + `; + static autoRolesQuery = ` query AutoRoleQuery($serverId: ID, $filter: AutoRoleFilter, $page: Page, $sort: Sort) { servers(filter: {id: $serverId}) { diff --git a/web/src/app/modules/view/server/members/members.component.ts b/web/src/app/modules/view/server/members/members.component.ts index ded4f2a4..d691311d 100644 --- a/web/src/app/modules/view/server/members/members.component.ts +++ b/web/src/app/modules/view/server/members/members.component.ts @@ -64,7 +64,9 @@ export class MembersComponent extends ComponentWithTable implements OnInit, OnDe level: FormControl }>; - filter: UserFilter = {}; + filter: UserFilter = { + leftServer: false + }; page: Page = { pageSize: undefined, pageIndex: undefined @@ -145,7 +147,7 @@ export class MembersComponent extends ComponentWithTable implements OnInit, OnDe id: new FormControl(null), discordId: new FormControl(null), name: [""], - leftServer: new FormControl(null), + leftServer: new FormControl(false), level: new FormControl(null) }); diff --git a/web/src/app/modules/view/server/server-routing.module.ts b/web/src/app/modules/view/server/server-routing.module.ts index 310e44a5..732c1af8 100644 --- a/web/src/app/modules/view/server/server-routing.module.ts +++ b/web/src/app/modules/view/server/server-routing.module.ts @@ -8,19 +8,55 @@ import { MemberRoles } from "../../../models/auth/auth-user.dto"; const routes: Routes = [ { path: "", component: ServerDashboardComponent }, - { path: "members", component: MembersComponent, canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, + { + path: "members", + component: MembersComponent, + canActivate: [AuthGuard], + data: { memberRole: MemberRoles.Moderator } + }, { path: "members/:memberId", component: ProfileComponent }, + { + path: "user-warnings", + loadChildren: () => import("./user-warning/user-warning.module").then(m => m.UserWarningModule), + canActivate: [AuthGuard], + data: { memberRole: MemberRoles.Moderator } + }, { path: "auto-roles", loadChildren: () => import("./auto-role/auto-role.module").then(m => m.AutoRoleModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, - { path: "levels", loadChildren: () => import("./levels/levels.module").then(m => m.LevelsModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, - { path: "achievements", loadChildren: () => import("./achievements/achievements.module").then(m => m.AchievementsModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, - { path: "short-role-names", loadChildren: () => import("./short-role-name/short-role-name.module").then(m => m.ShortRoleNameModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, - { path: "scheduled-events", loadChildren: () => import("./scheduled-events/scheduled-events.module").then(m => m.ScheduledEventsModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } }, - { path: "config", loadChildren: () => import("./config/config.module").then(m => m.ConfigModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Admin } } + { + path: "levels", + loadChildren: () => import("./levels/levels.module").then(m => m.LevelsModule), + canActivate: [AuthGuard], + data: { memberRole: MemberRoles.Moderator } + }, + { + path: "achievements", + loadChildren: () => import("./achievements/achievements.module").then(m => m.AchievementsModule), + canActivate: [AuthGuard], + data: { memberRole: MemberRoles.Moderator } + }, + { + path: "short-role-names", + loadChildren: () => import("./short-role-name/short-role-name.module").then(m => m.ShortRoleNameModule), + canActivate: [AuthGuard], + data: { memberRole: MemberRoles.Moderator } + }, + { + path: "scheduled-events", + loadChildren: () => import("./scheduled-events/scheduled-events.module").then(m => m.ScheduledEventsModule), + canActivate: [AuthGuard], + data: { memberRole: MemberRoles.Moderator } + }, + { + path: "config", + loadChildren: () => import("./config/config.module").then(m => m.ConfigModule), + canActivate: [AuthGuard], + data: { memberRole: MemberRoles.Admin } + } ]; @NgModule({ diff --git a/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.html b/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.html new file mode 100644 index 00000000..2cdc08c9 --- /dev/null +++ b/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.html @@ -0,0 +1,213 @@ +

+ {{'common.user_warnings' | translate}} +

+
+
+ + + +
+
+
+ {{userWarnings.length}} {{'common.of' | translate}} + {{dt.totalRecords}} + + {{'common.user_warnings' | translate}} +
+ + +
+ +
+ + + +
+
+
+ + + + +
+
{{'common.id' | translate}}
+ +
+ + + +
+
{{'common.description' | translate}}
+ +
+ + + +
+
{{'common.member' | translate}}
+ +
+ + + +
+
{{'common.author' | translate}}
+ +
+ + + +
+
{{'common.created_at' | translate}}
+
+ + + +
+
{{'common.modified_at' | translate}}
+
+ + + +
+
{{'common.actions' | translate}}
+
+ + + + + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + + + +
+ + + + + {{'common.id' | translate}}: + + + {{userWarning.id}} + + + {{userWarning.id}} + + + + + + {{'common.description' | translate}}: + + + + {{userWarning.description}} + + + {{userWarning.description}} + + + + + + {{'common.user' | translate}}: + + + + {{userWarning.user.name}} + + + {{userWarning.user.name}} + + + + + + {{'common.author' | translate}}: + + + {{'common.you' | translate}} + + + {{userWarning.author.name}} + + + + + + {{'common.created_at' | translate}}: + {{userWarning.createdAt | date:'dd.MM.yy HH:mm'}} + + + + {{'common.modified_at' | translate}}: + {{userWarning.modifiedAt | date:'dd.MM.yy HH:mm'}} + + +
+ + + + +
+ + +
+ + + + + {{'common.no_entries_found' | translate}} + + + + + + +
+
+
+ diff --git a/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.scss b/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.spec.ts b/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.spec.ts new file mode 100644 index 00000000..f9f5fbee --- /dev/null +++ b/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { UserWarningComponent } from "./user-warning.component"; + +describe("AchievementComponent", () => { + let component: UserWarningComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [UserWarningComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(UserWarningComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.ts b/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.ts new file mode 100644 index 00000000..d797cceb --- /dev/null +++ b/web/src/app/modules/view/server/user-warning/components/user-warning/user-warning.component.ts @@ -0,0 +1,240 @@ +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; +import { Page } from "../../../../../../models/graphql/filter/page.model"; +import { Sort, SortDirection } from "../../../../../../models/graphql/filter/sort.model"; +import { Subject, throwError } from "rxjs"; +import { Server } from "../../../../../../models/data/server.model"; +import { UserDTO } from "../../../../../../models/auth/auth-user.dto"; +import { Queries } from "../../../../../../models/graphql/queries.model"; +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 { DataService } from "../../../../../../services/data/data.service"; +import { SidebarService } from "../../../../../../services/sidebar/sidebar.service"; +import { ActivatedRoute } from "@angular/router"; +import { Query, UserListQuery, UserWarningQuery } from "../../../../../../models/graphql/query.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 { UpdateUserMutationResult } from "../../../../../../models/graphql/result.model"; +import { Mutations } from "../../../../../../models/graphql/mutations.model"; +import { ComponentWithTable } from "../../../../../../base/component-with-table"; +import { UserWarning, UserWarningFilter } from "../../../../../../models/data/user_warning.model"; + +@Component({ + selector: "app-user-warning", + templateUrl: "./user-warning.component.html", + styleUrls: ["./user-warning.component.scss"] +}) +export class UserWarningComponent extends ComponentWithTable implements OnInit, OnDestroy { + public userWarnings: UserWarning[] = []; + public users: User[] = []; + public loading = true; + + public filterForm!: FormGroup<{ + id: FormControl, + description: FormControl, + user: FormControl, + author: FormControl, + }>; + + public filter: UserWarningFilter = {}; + public page: Page = { + pageSize: undefined, + pageIndex: undefined + }; + public sort: Sort = { + sortColumn: undefined, + sortDirection: undefined + }; + + public totalRecords: number = 0; + + public clonedUserWarning: { [s: string]: UserWarning; } = {}; + + private unsubscriber = new Subject(); + private server: Server = {}; + public user: UserDTO | null = null; + + query: string = Queries.userWarningsQuery; + + public constructor( + private authService: AuthService, + private spinner: SpinnerService, + private toastService: ToastService, + private confirmDialog: ConfirmationDialogService, + private fb: FormBuilder, + private translate: TranslateService, + private data: DataService, + private sidebar: SidebarService, + private route: ActivatedRoute) { + super("UserWarning", ["id", "name"], + (oldElement: UserWarning, newElement: UserWarning) => { + return oldElement.id === newElement.id; + }); + } + + public ngOnInit(): void { + this.loading = true; + this.setFilterForm(); + this.data.getServerFromRoute(this.route).then(async server => { + this.server = server; + let authUser = await this.authService.getLoggedInUser(); + this.user = authUser?.users?.find(u => u.server == this.server.id) ?? null; + }); + } + + public ngOnDestroy(): void { + this.unsubscriber.next(); + this.unsubscriber.complete(); + } + + public loadNextPage(): void { + this.data.query(Queries.userWarningsQuery, { + serverId: this.server.id, filter: this.filter, page: this.page, sort: this.sort + }, + (data: Query) => { + return data.servers[0]; + } + ).subscribe(data => { + this.totalRecords = data.userWarningCount; + this.userWarnings = data.userWarnings; + + this.data.query(Queries.liteUsersQuery, { + serverId: this.server.id, filter: { + leftServer: false + } + }, + (x: { servers: Server[] }) => { + return x.servers[0]; + } + ).subscribe(data => { + this.users = data.users; + + this.spinner.hideSpinner(); + this.loading = false; + }); + }); + } + + public setFilterForm(): void { + this.filterForm = this.fb.group({ + id: new FormControl(null), + description: new FormControl(null), + user: new FormControl(null), + author: new FormControl(null) + }); + + this.filterForm.valueChanges.pipe( + takeUntil(this.unsubscriber), + debounceTime(600) + ).subscribe(changes => { + if (changes.id) { + this.filter.id = changes.id; + } else { + this.filter.id = undefined; + } + + if (this.page.pageSize) + this.page.pageSize = 10; + + if (this.page.pageIndex) + this.page.pageIndex = 0; + + this.loadNextPage(); + }); + } + + public newUserWarningTemplate: UserWarning = { + 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.clonedUserWarning[index] = { ...user }; + } + + public override onRowEditSave(newUserWarning: UserWarning, index: number): void { + if (this.isEditingNew && JSON.stringify(newUserWarning) === JSON.stringify(this.newUserWarningTemplate)) { + this.isEditingNew = false; + this.userWarnings.splice(index, 1); + return; + } + + if (!newUserWarning.id && !this.isEditingNew) { + return; + } + + this.updateUserByWarning(newUserWarning.user?.id ?? 0, this.userWarnings.filter(uw => uw.user?.id == newUserWarning.user?.id)); + } + + public onRowEditCancel(index: number): void { + if (this.isEditingNew) { + this.userWarnings.splice(index, 1); + delete this.clonedUserWarning[index]; + this.isEditingNew = false; + return; + } + + this.userWarnings[index] = this.clonedUserWarning[index]; + delete this.clonedUserWarning[index]; + } + + public addUserWarning(table: Table): void { + const newUserWarning = JSON.parse(JSON.stringify(this.newUserWarningTemplate)); + + this.userWarnings = [newUserWarning, ...this.userWarnings]; + + table.initRowEdit(newUserWarning); + + const index = this.userWarnings.findIndex(l => l.id == newUserWarning.id); + this.onRowEditInit(table, newUserWarning, index); + + this.isEditingNew = true; + } + + public deleteUserWarning(userWarning: UserWarning): void { + this.userWarnings.splice(this.userWarnings.findIndex(uw => uw.id == userWarning.id), 1); + this.updateUserByWarning(userWarning.user?.id ?? 0, this.userWarnings.filter(uw => uw.user?.id == userWarning.user?.id)); + } + + public updateUserByWarning(userId: number, userWarnings: UserWarning[]) { + this.spinner.showSpinner(); + this.data.mutation(Mutations.updateUser, { + id: userId, + userWarnings: userWarnings?.map(userWarning => { + return { + id: userWarning.id, + user: userWarning.user?.id ?? undefined, + description: userWarning.description, + author: userWarning.author?.id ?? undefined + }; + }) + } + ).pipe(catchError(err => { + this.spinner.hideSpinner(); + return throwError(err); + })).subscribe(_ => { + this.spinner.hideSpinner(); + this.toastService.success(this.translate.instant("view.server.user_warning.message.user_warning_updated"), this.translate.instant("view.server.user_warning.message.user_warning_updated")); + this.loadNextPage(); + }); + } +} diff --git a/web/src/app/modules/view/server/user-warning/user-warning-routing.module.ts b/web/src/app/modules/view/server/user-warning/user-warning-routing.module.ts new file mode 100644 index 00000000..827021d6 --- /dev/null +++ b/web/src/app/modules/view/server/user-warning/user-warning-routing.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from "@angular/core"; +import { RouterModule, Routes } from "@angular/router"; +import { UserWarningComponent } from "./components/user-warning/user-warning.component"; + +const routes: Routes = [ + + { path: "", component: UserWarningComponent } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class UserWarningRoutingModule { + +} diff --git a/web/src/app/modules/view/server/user-warning/user-warning.module.ts b/web/src/app/modules/view/server/user-warning/user-warning.module.ts new file mode 100644 index 00000000..df78033a --- /dev/null +++ b/web/src/app/modules/view/server/user-warning/user-warning.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { UserWarningRoutingModule } from "./user-warning-routing.module"; +import { SharedModule } from "../../../shared/shared.module"; +import { UserWarningComponent } from "./components/user-warning/user-warning.component"; + + +@NgModule({ + declarations: [ + UserWarningComponent + ], + imports: [ + CommonModule, + UserWarningRoutingModule, + SharedModule + ] +}) +export class UserWarningModule { +} diff --git a/web/src/app/services/sidebar/sidebar.service.ts b/web/src/app/services/sidebar/sidebar.service.ts index b5c887f6..623a9118 100644 --- a/web/src/app/services/sidebar/sidebar.service.ts +++ b/web/src/app/services/sidebar/sidebar.service.ts @@ -27,6 +27,7 @@ export class SidebarService { serverDashboard: MenuItem = {}; serverProfile: MenuItem = {}; serverMembers: MenuItem = {}; + serverUserWarnings: MenuItem = {}; serverAutoRoles: MenuItem = {}; serverLevels: MenuItem = {}; serverAchievements: MenuItem = {}; @@ -89,6 +90,12 @@ export class SidebarService { visible: true, routerLink: `server/${this.server?.id}/members` }; + this.serverUserWarnings = { + label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.user_warnings") : "", + icon: "pi pi-exclamation-triangle", + visible: true, + routerLink: `server/${this.server?.id}/user-warnings` + }; this.serverAutoRoles = { label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.auto_roles") : "", @@ -137,7 +144,7 @@ export class SidebarService { icon: "pi pi-server", visible: false, expanded: true, - items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles, this.serverLevels, this.serverAchievements, this.serverScheduledEvents, this.serverShortRoleNames, this.serverConfig] + items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverUserWarnings, this.serverAutoRoles, this.serverLevels, this.serverAchievements, this.serverScheduledEvents, this.serverShortRoleNames, this.serverConfig] }; this.adminConfig = { label: this.isSidebarOpen ? this.translateService.instant("sidebar.config") : "", @@ -194,6 +201,7 @@ export class SidebarService { if (this.server) { this.serverMenu.visible = true; this.serverMembers.visible = isTechnicianAndFullAccessActive || user?.isModerator; + this.serverUserWarnings.visible = isTechnicianAndFullAccessActive || user?.isModerator; this.serverAutoRoles.visible = isTechnicianAndFullAccessActive || this.hasFeature("AutoRoleModule") && user?.isModerator; this.serverLevels.visible = isTechnicianAndFullAccessActive || this.hasFeature("LevelModule") && user?.isModerator; this.serverAchievements.visible = isTechnicianAndFullAccessActive || this.hasFeature("AchievementsModule") && user?.isModerator; diff --git a/web/src/assets/i18n/de.json b/web/src/assets/i18n/de.json index 008d1ab5..6461cd4c 100644 --- a/web/src/assets/i18n/de.json +++ b/web/src/assets/i18n/de.json @@ -190,6 +190,7 @@ "left_server": "Aktiv", "level": "Level", "location": "Ort", + "member": "Mitglied", "message_id": "Nachricht Id", "min_xp": "Min. XP", "modified_at": "Bearbeitet am", @@ -218,7 +219,8 @@ "user_warnings": "Verwarnungen", "users": "Benutzer", "value": "Wert", - "xp": "XP" + "xp": "XP", + "you": "Du" }, "dialog": { "abort": "Abbrechen", @@ -348,7 +350,8 @@ "members": "Mitglieder", "profile": "Dein Profil", "scheduled_events": "Geplante Events", - "short_role_names": "Rollen Kürzel" + "short_role_names": "Rollen Kürzel", + "user_warnings": "Verwarnungen" }, "server_empty": "Kein Server ausgewählt", "settings": "Einstellungen", @@ -622,6 +625,11 @@ "short_role_names_update_failed_d": "Die Bearbeitung des Levels ist fehlgeschlagen!" }, "short_role_names": "Rollen Kürzel" + }, + "user_warning": { + "message": { + "user_warning_updated": "Verwarnungen wurde bearbeitet" + } } }, "user_settings": { diff --git a/web/src/assets/i18n/en.json b/web/src/assets/i18n/en.json index 2a5b0632..18cd2b0e 100644 --- a/web/src/assets/i18n/en.json +++ b/web/src/assets/i18n/en.json @@ -190,6 +190,7 @@ "left_server": "Active", "level": "Level", "location": "Location", + "member": "Member", "message_id": "Message Id", "min_xp": "Min. XP", "modified_at": "Modified at", @@ -218,7 +219,8 @@ "user_warnings": "User warnings", "users": "User", "value": "Value", - "xp": "XP" + "xp": "XP", + "you": "You" }, "dialog": { "abort": "Abort", @@ -348,7 +350,8 @@ "members": "Members", "profile": "Your profile", "scheduled_events": "Scheduled events", - "short_role_names": "Short role names" + "short_role_names": "Short role names", + "user_warnings": "User warnings" }, "server_empty": "No server selected", "settings": "Settings", @@ -622,6 +625,11 @@ "short_role_names_update_failed_d": "Short role name editing failed!" }, "short_role_names": "Level" + }, + "user_warning": { + "message": { + "user_warning_updated": "Updated user warnings" + } } }, "user_settings": {