Added members view #130
This commit is contained in:
@@ -32,7 +32,7 @@
|
||||
<th pSortableColumn="firstName">
|
||||
<div class="table-header-label">
|
||||
<div class="table-header-text">{{'admin.auth_users.headers.first_name' | translate}}</div>
|
||||
<p-sortIcon icon="sort" field="firstName" class="table-header-icon"></p-sortIcon>
|
||||
<p-sortIcon field="firstName" class="table-header-icon"></p-sortIcon>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
@@ -70,24 +70,11 @@
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th class="table-header-small-dropdown">
|
||||
<div class="table-header-label">
|
||||
<div class="table-header-text">{{'admin.auth_users.headers.created_at' | translate}}</div>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th class="table-header-small-dropdown">
|
||||
<div class="table-header-label">
|
||||
<div class="table-header-text">{{'admin.auth_users.headers.modified_at' | translate}}</div>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th class="table-header-actions">
|
||||
<div class="table-header-label">
|
||||
<div class="table-header-text">{{'admin.auth_users.headers.actions' | translate}}</div>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
@@ -108,7 +95,7 @@
|
||||
<th></th>
|
||||
<th>
|
||||
<form [formGroup]="filterForm">
|
||||
<p-dropdown formControlName="authRole" [options]="authRoles"></p-dropdown>
|
||||
<p-dropdown formControlName="authRole" [options]="authRoles" placeholder="{{'admin.auth_users.headers.auth_role' | translate}}"></p-dropdown>
|
||||
</form>
|
||||
</th>
|
||||
<th></th>
|
||||
@@ -223,7 +210,7 @@
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="Invalidmessage">
|
||||
<ng-template pTemplate="emptymessage">
|
||||
<tr>
|
||||
<td colspan="7">{{'admin.auth_users.no_entries_found' | translate}}</td>
|
||||
</tr>
|
||||
@@ -234,3 +221,4 @@
|
||||
</p-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -0,0 +1,192 @@
|
||||
<h1>
|
||||
{{'view.server.members.header' | translate}}
|
||||
</h1>
|
||||
<div class="content-wrapper">
|
||||
<div class="content">
|
||||
<p-table #dt [value]="members" 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">{{members.length}} {{'view.server.members.of' | translate}}
|
||||
{{dt.totalRecords}}
|
||||
</ng-container>
|
||||
{{'view.server.members.members' | translate}}
|
||||
</div>
|
||||
|
||||
<div class="table-caption-btn-wrapper btn-wrapper">
|
||||
<button pButton label="{{'view.server.members.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.members.headers.id' | translate}}</div>
|
||||
<p-sortIcon field="id" class="table-header-icon"></p-sortIcon>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th pSortableColumn="discordId">
|
||||
<div class="table-header-label">
|
||||
<div class="table-header-text">{{'view.server.members.headers.discord_id' | translate}}</div>
|
||||
<p-sortIcon field="discordId" class="table-header-icon"></p-sortIcon>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th pSortableColumn="name">
|
||||
<div class="table-header-label">
|
||||
<div class="table-header-text">{{'view.server.members.headers.name' | translate}}</div>
|
||||
<p-sortIcon field="name" class="table-header-icon"></p-sortIcon>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th pSortableColumn="xp">
|
||||
<div class="table-header-label">
|
||||
<div class="table-header-text">{{'view.server.members.headers.xp' | translate}}</div>
|
||||
<p-sortIcon field="xp" class="table-header-icon"></p-sortIcon>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th pSortableColumn="ontime">
|
||||
<div class="table-header-label">
|
||||
<div class="table-header-text">{{'view.server.members.headers.ontime' | translate}}</div>
|
||||
<p-sortIcon field="ontime" class="table-header-icon"></p-sortIcon>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th class="table-header-small-dropdown" pSortableColumn="level">
|
||||
<div class="table-header-label">
|
||||
<div class="table-header-text">{{'view.server.members.headers.level' | translate}}</div>
|
||||
<p-sortIcon field="level" class="table-header-icon"></p-sortIcon>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th class="table-header-actions">
|
||||
<div class="table-header-label">
|
||||
<div class="table-header-text">{{'view.server.members.headers.actions' | translate}}</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<form [formGroup]="filterForm">
|
||||
<input type="text" pInputText formControlName="id" placeholder="{{'view.server.members.headers.id' | translate}}">
|
||||
</form>
|
||||
</th>
|
||||
<th>
|
||||
<form [formGroup]="filterForm">
|
||||
<input type="text" pInputText formControlName="discordId" placeholder="{{'view.server.members.headers.discord_id' | translate}}">
|
||||
</form>
|
||||
</th>
|
||||
<th>
|
||||
<form [formGroup]="filterForm">
|
||||
<input type="text" pInputText formControlName="name" placeholder="{{'view.server.members.headers.name' | translate}}">
|
||||
</form>
|
||||
</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th>
|
||||
<form [formGroup]="filterForm">
|
||||
<p-dropdown formControlName="level" [options]="levels" placeholder="{{'view.server.members.headers.level' | translate}}"></p-dropdown>
|
||||
</form>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="body" let-member let-editing="editing" let-ri="rowIndex">
|
||||
<tr [pEditableRow]="member">
|
||||
<td>
|
||||
<p-cellEditor>
|
||||
<ng-template pTemplate="input">
|
||||
{{member.id}}
|
||||
</ng-template>
|
||||
<ng-template pTemplate="output">
|
||||
{{member.id}}
|
||||
</ng-template>
|
||||
</p-cellEditor>
|
||||
</td>
|
||||
<td>
|
||||
<p-cellEditor>
|
||||
<ng-template pTemplate="input">
|
||||
{{member.discordId}}
|
||||
</ng-template>
|
||||
<ng-template pTemplate="output">
|
||||
{{member.discordId}}
|
||||
</ng-template>
|
||||
</p-cellEditor>
|
||||
</td>
|
||||
<td>
|
||||
<p-cellEditor>
|
||||
<ng-template pTemplate="input">
|
||||
{{member.name}}
|
||||
</ng-template>
|
||||
<ng-template pTemplate="output">
|
||||
{{member.name}}
|
||||
</ng-template>
|
||||
</p-cellEditor>
|
||||
</td>
|
||||
<td>
|
||||
<p-cellEditor>
|
||||
<ng-template pTemplate="input">
|
||||
<input class="table-edit-input" pInputText type="number" [(ngModel)]="member.xp">
|
||||
</ng-template>
|
||||
<ng-template pTemplate="output">
|
||||
{{member.xp}}
|
||||
</ng-template>
|
||||
</p-cellEditor>
|
||||
</td>
|
||||
<td>
|
||||
<p-cellEditor>
|
||||
<ng-template pTemplate="input">
|
||||
{{member.ontime}}
|
||||
</ng-template>
|
||||
<ng-template pTemplate="output">
|
||||
{{member.ontime}}
|
||||
</ng-template>
|
||||
</p-cellEditor>
|
||||
</td>
|
||||
<td>
|
||||
<p-cellEditor>
|
||||
<ng-template pTemplate="input">
|
||||
<p-dropdown [options]="levels" [(ngModel)]="member.level" placeholder="{{'view.server.members.headers.level' | translate}}"></p-dropdown>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="output">
|
||||
{{member.level.name}}
|
||||
</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, member, ri)"></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>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="emptymessage">
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<td colspan="7">{{'view.server.members.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 { MembersComponent } from './members.component';
|
||||
|
||||
describe('MembersComponent', () => {
|
||||
let component: MembersComponent;
|
||||
let fixture: ComponentFixture<MembersComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MembersComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(MembersComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
235
kdb-web/src/app/modules/view/server/members/members.component.ts
Normal file
235
kdb-web/src/app/modules/view/server/members/members.component.ts
Normal file
@@ -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<number | null>,
|
||||
discordId: FormControl<number | null>,
|
||||
name: FormControl<string | null>,
|
||||
level: FormControl<number | null>
|
||||
}>;
|
||||
|
||||
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<LevelListQuery>(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<number | null>(null),
|
||||
discordId: new FormControl<number | null>(null),
|
||||
name: [""],
|
||||
level: new FormControl<number | null>(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<UserListQuery>(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<UpdateUserMutationResult>(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];
|
||||
}
|
||||
}
|
@@ -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({
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user