add lazy loaded userlist to userpanel
This commit is contained in:
parent
509dda78ea
commit
26c3918bcc
|
@ -19,19 +19,19 @@
|
||||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix"
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^8.4.0",
|
"@nestjs/common": "^8.4.1",
|
||||||
"@nestjs/config": "^1.2.0",
|
"@nestjs/config": "^2.0.0",
|
||||||
"@nestjs/core": "^8.4.0",
|
"@nestjs/core": "^8.4.1",
|
||||||
"@nestjs/jwt": "^8.0.0",
|
"@nestjs/jwt": "^8.0.0",
|
||||||
"@nestjs/passport": "^8.2.1",
|
"@nestjs/passport": "^8.2.1",
|
||||||
"@nestjs/platform-fastify": "^8.4.0",
|
"@nestjs/platform-fastify": "^8.4.1",
|
||||||
"@nestjs/serve-static": "^2.2.2",
|
"@nestjs/serve-static": "^2.2.2",
|
||||||
"@nestjs/typeorm": "^8.0.3",
|
"@nestjs/typeorm": "^8.0.3",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.13.2",
|
"class-validator": "^0.13.2",
|
||||||
"fastify-multipart": "^5.3.1",
|
"fastify-multipart": "^5.3.1",
|
||||||
"fastify-static": "^4.5.0",
|
"fastify-static": "^4.6.1",
|
||||||
"file-type": "^17.1.1",
|
"file-type": "^17.1.1",
|
||||||
"passport": "^0.5.2",
|
"passport": "^0.5.2",
|
||||||
"passport-jwt": "^4.0.0",
|
"passport-jwt": "^4.0.0",
|
||||||
|
@ -42,12 +42,12 @@
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rxjs": "^7.5.5",
|
"rxjs": "^7.5.5",
|
||||||
"typeorm": "^0.2.45"
|
"typeorm": "0.2.45"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^8.2.2",
|
"@nestjs/cli": "^8.2.3",
|
||||||
"@nestjs/schematics": "^8.0.8",
|
"@nestjs/schematics": "^8.0.8",
|
||||||
"@nestjs/testing": "^8.4.0",
|
"@nestjs/testing": "^8.4.1",
|
||||||
"@types/bcrypt": "^5.0.0",
|
"@types/bcrypt": "^5.0.0",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"@types/node": "^17.0.21",
|
"@types/node": "^17.0.21",
|
||||||
|
@ -55,16 +55,16 @@
|
||||||
"@types/passport-local": "^1.0.34",
|
"@types/passport-local": "^1.0.34",
|
||||||
"@types/passport-strategy": "^0.2.35",
|
"@types/passport-strategy": "^0.2.35",
|
||||||
"@types/supertest": "^2.0.11",
|
"@types/supertest": "^2.0.11",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
"@typescript-eslint/eslint-plugin": "^5.15.0",
|
||||||
"@typescript-eslint/parser": "^5.14.0",
|
"@typescript-eslint/parser": "^5.15.0",
|
||||||
"eslint": "^8.11.0",
|
"eslint": "^8.11.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.6.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"ts-loader": "^9.2.8",
|
"ts-loader": "^9.2.8",
|
||||||
"ts-node": "^10.7.0",
|
"ts-node": "^10.7.0",
|
||||||
"tsconfig-paths": "^3.13.0",
|
"tsconfig-paths": "^3.14.0",
|
||||||
"typescript": "4.5.5",
|
"typescript": "4.5.5",
|
||||||
"webpack": "^5.70.0"
|
"webpack": "^5.70.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { plainToClass } from 'class-transformer';
|
||||||
import Crypto from 'crypto';
|
import Crypto from 'crypto';
|
||||||
|
import { SupportedMime } from 'picsur-shared/dist/dto/mimes.dto';
|
||||||
import {
|
import {
|
||||||
AsyncFailable,
|
AsyncFailable,
|
||||||
Fail,
|
Fail,
|
||||||
HasFailed,
|
HasFailed,
|
||||||
HasSuccess,
|
HasSuccess
|
||||||
} from 'picsur-shared/dist/types';
|
} from 'picsur-shared/dist/types';
|
||||||
import { SupportedMime } from 'picsur-shared/dist/dto/mimes.dto';
|
import { Repository } from 'typeorm';
|
||||||
import { GetCols } from '../collectionutils';
|
|
||||||
import { plainToClass } from 'class-transformer';
|
|
||||||
import { EImageBackend } from '../../models/entities/image.entity';
|
import { EImageBackend } from '../../models/entities/image.entity';
|
||||||
|
import { GetCols } from '../collectionutils';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ImageDBService {
|
export class ImageDBService {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
{
|
{
|
||||||
"$schema": "../node_modules/@angular/cli/lib/config/schema.json",
|
"$schema": "../node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"cli": {
|
||||||
|
"analytics": false
|
||||||
|
},
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"newProjectRoot": "projects",
|
"newProjectRoot": "projects",
|
||||||
"projects": {
|
"projects": {
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
"@angular-devkit/build-angular": "14.0.0-next.4",
|
"@angular-devkit/build-angular": "14.0.0-next.4",
|
||||||
"@angular/cli": "^14.0.0-next.4",
|
"@angular/cli": "^14.0.0-next.4",
|
||||||
"@angular/compiler-cli": "^14.0.0-next.5",
|
"@angular/compiler-cli": "^14.0.0-next.5",
|
||||||
"@types/jasmine": "~3.10.3",
|
"@types/jasmine": "~4.0.0",
|
||||||
"@types/node": "^17.0.21",
|
"@types/node": "^17.0.21",
|
||||||
"@types/validator": "^13.7.1",
|
"@types/validator": "^13.7.1",
|
||||||
"jasmine-core": "~4.0.1",
|
"jasmine-core": "~4.0.1",
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
SysPrefValueType
|
SysPrefValueType
|
||||||
} from 'picsur-shared/dist/dto/syspreferences.dto';
|
} from 'picsur-shared/dist/dto/syspreferences.dto';
|
||||||
import { HasFailed } from 'picsur-shared/dist/types';
|
import { HasFailed } from 'picsur-shared/dist/types';
|
||||||
import { debounceTime, Subject } from 'rxjs';
|
import { Subject, throttleTime } from 'rxjs';
|
||||||
import { SnackBarType } from 'src/app/models/snack-bar-type';
|
import { SnackBarType } from 'src/app/models/snack-bar-type';
|
||||||
import { SysprefService } from 'src/app/services/api/syspref.service';
|
import { SysprefService } from 'src/app/services/api/syspref.service';
|
||||||
import { UtilService } from 'src/app/util/util.service';
|
import { UtilService } from 'src/app/util/util.service';
|
||||||
|
@ -70,7 +70,7 @@ export class SettingsSysprefOptionComponent implements OnInit {
|
||||||
@AutoUnsubscribe()
|
@AutoUnsubscribe()
|
||||||
subscribeUpdate() {
|
subscribeUpdate() {
|
||||||
return this.updateSubject
|
return this.updateSubject
|
||||||
.pipe(debounceTime(300))
|
.pipe(throttleTime(300, undefined, { leading: true, trailing: true }))
|
||||||
.subscribe(async (value) => {
|
.subscribe(async (value) => {
|
||||||
const result = await this.sysprefService.setPreference(
|
const result = await this.sysprefService.setPreference(
|
||||||
this.pref.key,
|
this.pref.key,
|
||||||
|
|
|
@ -1,2 +1,26 @@
|
||||||
<h1>Users</h1>
|
<h1>Users</h1>
|
||||||
|
|
||||||
|
<table mat-table [dataSource]="dataSubject">
|
||||||
|
<ng-container matColumnDef="id">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>ID</th>
|
||||||
|
<td mat-cell *matCellDef="let user">{{ user.id }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="username">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Username</th>
|
||||||
|
<td mat-cell *matCellDef="let user">{{ user.username }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<mat-paginator
|
||||||
|
color="accent"
|
||||||
|
[pageSizeOptions]="pageSizeOptions"
|
||||||
|
[pageSize]="startingPageSize"
|
||||||
|
length="Infinity"
|
||||||
|
aria-label="Select page of periodic elements"
|
||||||
|
(page)="updateSubject.next($event)"
|
||||||
|
>
|
||||||
|
</mat-paginator>
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
|
@ -1,12 +1,60 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||||
|
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||||
|
import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||||
|
import { HasFailed } from 'picsur-shared/dist/types';
|
||||||
|
import { BehaviorSubject, Subject, throttleTime } from 'rxjs';
|
||||||
|
import { UserManageService } from 'src/app/services/api/usermanage.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './settings-users.component.html',
|
templateUrl: './settings-users.component.html',
|
||||||
|
styleUrls: ['./settings-users.component.scss'],
|
||||||
})
|
})
|
||||||
export class SettingsUsersComponent implements OnInit {
|
export class SettingsUsersComponent implements OnInit {
|
||||||
constructor() {}
|
public readonly displayedColumns: string[] = ['id', 'username'];
|
||||||
|
public readonly pageSizeOptions: number[] = [5, 10, 25, 100];
|
||||||
|
public readonly startingPageSize = this.pageSizeOptions[2];
|
||||||
|
|
||||||
ngOnInit(): void {
|
public dataSubject = new BehaviorSubject<EUser[]>([]);
|
||||||
|
public updateSubject = new Subject<PageEvent>();
|
||||||
|
|
||||||
|
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||||
|
|
||||||
|
constructor(private userManageService: UserManageService) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.subscribeToUpdate();
|
||||||
|
this.fetchUsers(this.startingPageSize, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AutoUnsubscribe()
|
||||||
|
private subscribeToUpdate() {
|
||||||
|
return this.updateSubject
|
||||||
|
.pipe(throttleTime(500, undefined, { leading: true, trailing: true }))
|
||||||
|
.subscribe(async (pageEvent: PageEvent) => {
|
||||||
|
let amount = await this.fetchUsers(
|
||||||
|
pageEvent.pageSize,
|
||||||
|
pageEvent.pageIndex
|
||||||
|
);
|
||||||
|
if (amount === 0) {
|
||||||
|
if ( pageEvent.previousPageIndex === pageEvent.pageIndex - 1){
|
||||||
|
this.paginator.previousPage();
|
||||||
|
} else {
|
||||||
|
this.paginator.firstPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchUsers(
|
||||||
|
pageSize: number,
|
||||||
|
pageIndex: number
|
||||||
|
): Promise<number> {
|
||||||
|
const result = await this.userManageService.getUsers(pageSize, pageIndex);
|
||||||
|
if (HasFailed(result)) return 0;
|
||||||
|
|
||||||
|
this.dataSubject.next(result);
|
||||||
|
|
||||||
|
return result.length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||||
|
import { MatTableModule } from '@angular/material/table';
|
||||||
import { SettingsUsersComponent } from './settings-users.component';
|
import { SettingsUsersComponent } from './settings-users.component';
|
||||||
import { SettingsUsersRoutingModule } from './settings-users.routing.module';
|
import { SettingsUsersRoutingModule } from './settings-users.routing.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [SettingsUsersComponent],
|
declarations: [SettingsUsersComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
SettingsUsersRoutingModule,
|
SettingsUsersRoutingModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SettingsUsersRouteModule {}
|
export class SettingsUsersRouteModule {}
|
||||||
|
|
35
frontend/src/app/services/api/usermanage.service.ts
Normal file
35
frontend/src/app/services/api/usermanage.service.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
UserListRequest,
|
||||||
|
UserListResponse
|
||||||
|
} from 'picsur-shared/dist/dto/api/usermanage.dto';
|
||||||
|
import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||||
|
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
|
||||||
|
import { ApiService } from './api.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class UserManageService {
|
||||||
|
constructor(private apiService: ApiService) {}
|
||||||
|
|
||||||
|
public async getUsers(count: number, page: number): AsyncFailable<EUser[]> {
|
||||||
|
const body = {
|
||||||
|
count,
|
||||||
|
page,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await this.apiService.post(
|
||||||
|
UserListRequest,
|
||||||
|
UserListResponse,
|
||||||
|
'/api/user/list',
|
||||||
|
body
|
||||||
|
);
|
||||||
|
|
||||||
|
if (HasFailed(result)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.users;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue