From b4e264eda96a57fc071f95ef108a2c099edff684 Mon Sep 17 00:00:00 2001 From: Bastian Wagner Date: Fri, 25 Oct 2024 12:32:26 +0200 Subject: [PATCH] xxx --- api/package-lock.json | 77 +++++++++++++++++++ api/package.json | 3 + api/src/app.module.ts | 14 +++- api/src/model/dto/index.ts | 1 - .../modules/cylinder/cylinder.controller.ts | 32 +++++++- api/src/modules/cylinder/cylinder.service.ts | 20 ++++- api/src/modules/key/key.controller.ts | 9 --- api/src/modules/key/key.service.ts | 12 --- .../system/dto/create-system.dto.ts} | 2 +- .../modules/system/dto/update-system.dto.ts | 4 + api/src/modules/system/system.controller.ts | 50 ++++++++++++ api/src/modules/system/system.module.ts | 12 +++ api/src/modules/system/system.service.spec.ts | 18 +++++ api/src/modules/system/system.service.ts | 42 ++++++++++ client/src/app/app.routes.ts | 4 +- .../src/app/core/layout/layout.component.html | 1 + .../app/model/interface/cylinder.interface.ts | 12 +++ .../src/app/model/interface/key.interface.ts | 4 +- .../admin/all-users/all-users.component.ts | 2 - .../delete-cylinder.component.html | 9 +++ .../delete-cylinder.component.scss} | 0 .../delete-cylinder.component.spec.ts | 23 ++++++ .../delete-cylinder.component.ts | 18 +++++ .../modules/cylinder/cylinder.component.ts | 7 ++ client/src/app/modules/keys/keys.component.ts | 4 +- .../app/modules/start/start.component.html | 1 - .../src/app/modules/start/start.component.ts | 19 ----- .../app/modules/system/system.component.html | 5 ++ .../app/modules/system/system.component.scss | 0 .../system.component.spec.ts} | 12 +-- .../app/modules/system/system.component.ts | 47 +++++++++++ .../ag-base-component.component.html | 1 + .../ag-base-component.component.scss | 0 .../ag-base-component.component.spec.ts | 23 ++++++ .../ag-base-component.component.ts | 28 +++++++ .../ag-delete-cylinder.component.html | 1 + .../ag-delete-cylinder.component.scss | 0 .../ag-delete-cylinder.component.spec.ts | 23 ++++++ .../ag-delete-cylinder.component.ts | 47 +++++++++++ client/src/app/shared/api.service.ts | 17 +++- 40 files changed, 538 insertions(+), 66 deletions(-) rename api/src/{model/dto/create-key-system.dto.ts => modules/system/dto/create-system.dto.ts} (80%) create mode 100644 api/src/modules/system/dto/update-system.dto.ts create mode 100644 api/src/modules/system/system.controller.ts create mode 100644 api/src/modules/system/system.module.ts create mode 100644 api/src/modules/system/system.service.spec.ts create mode 100644 api/src/modules/system/system.service.ts create mode 100644 client/src/app/model/interface/cylinder.interface.ts create mode 100644 client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.html rename client/src/app/modules/{start/start.component.scss => cylinder/components/delete-cylinder/delete-cylinder.component.scss} (100%) create mode 100644 client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.spec.ts create mode 100644 client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.ts delete mode 100644 client/src/app/modules/start/start.component.html delete mode 100644 client/src/app/modules/start/start.component.ts create mode 100644 client/src/app/modules/system/system.component.html create mode 100644 client/src/app/modules/system/system.component.scss rename client/src/app/modules/{start/start.component.spec.ts => system/system.component.spec.ts} (55%) create mode 100644 client/src/app/modules/system/system.component.ts create mode 100644 client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.html create mode 100644 client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.scss create mode 100644 client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.spec.ts create mode 100644 client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.ts create mode 100644 client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.html create mode 100644 client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.scss create mode 100644 client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.spec.ts create mode 100644 client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.ts diff --git a/api/package-lock.json b/api/package-lock.json index dea2ba3..3306e47 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -10,13 +10,16 @@ "license": "UNLICENSED", "dependencies": { "@nestjs/axios": "^3.0.3", + "@nestjs/cache-manager": "^2.3.0", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", "@nestjs/jwt": "^10.2.0", + "@nestjs/mapped-types": "^2.0.5", "@nestjs/platform-express": "^10.0.0", "@nestjs/typeorm": "^10.0.2", "axios": "^1.7.7", + "cache-manager": "^5.7.6", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "mysql2": "^3.11.2", @@ -1583,6 +1586,18 @@ "rxjs": "^6.0.0 || ^7.0.0" } }, + "node_modules/@nestjs/cache-manager": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/cache-manager/-/cache-manager-2.3.0.tgz", + "integrity": "sha512-pxeBp9w/s99HaW2+pezM1P3fLiWmUEnTUoUMLa9UYViCtjj0E0A19W/vaT5JFACCzFIeNrwH4/16jkpAhQ25Vw==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "cache-manager": "<=5", + "rxjs": "^7.0.0" + } + }, "node_modules/@nestjs/cli": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.5.tgz", @@ -1732,6 +1747,26 @@ "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" } }, + "node_modules/@nestjs/mapped-types": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/platform-express": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.1.tgz", @@ -3159,6 +3194,27 @@ "node": ">= 0.8" } }, + "node_modules/cache-manager": { + "version": "5.7.6", + "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-5.7.6.tgz", + "integrity": "sha512-wBxnBHjDxF1RXpHCBD6HGvKER003Ts7IIm0CHpggliHzN1RZditb7rXoduE1rplc2DEFYKxhLKgFuchXMJje9w==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "lodash.clonedeep": "^4.5.0", + "lru-cache": "^10.2.2", + "promise-coalesce": "^1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/cache-manager/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -4288,6 +4344,12 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -6365,6 +6427,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -7262,6 +7330,15 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "node_modules/promise-coalesce": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/promise-coalesce/-/promise-coalesce-1.1.2.tgz", + "integrity": "sha512-zLaJ9b8hnC564fnJH6NFSOGZYYdzrAJn2JUUIwzoQb32fG2QAakpDNM+CZo1km6keXkRXRM+hml1BFAPVnPkxg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", diff --git a/api/package.json b/api/package.json index b8c1f65..955912b 100644 --- a/api/package.json +++ b/api/package.json @@ -21,13 +21,16 @@ }, "dependencies": { "@nestjs/axios": "^3.0.3", + "@nestjs/cache-manager": "^2.3.0", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.3", "@nestjs/core": "^10.0.0", "@nestjs/jwt": "^10.2.0", + "@nestjs/mapped-types": "^2.0.5", "@nestjs/platform-express": "^10.0.0", "@nestjs/typeorm": "^10.0.2", "axios": "^1.7.7", + "cache-manager": "^5.7.6", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "mysql2": "^3.11.2", diff --git a/api/src/app.module.ts b/api/src/app.module.ts index 1588b13..facd2a9 100644 --- a/api/src/app.module.ts +++ b/api/src/app.module.ts @@ -10,6 +10,9 @@ import { RoleModule } from './modules/role/role.module'; import { KeyModule } from './modules/key/key.module'; import { CustomerModule } from './modules/customer/customer.module'; import { CylinderModule } from './modules/cylinder/cylinder.module'; +import { SystemModule } from './modules/system/system.module'; +import { CacheInterceptor, CacheModule } from '@nestjs/cache-manager'; +import { APP_INTERCEPTOR } from '@nestjs/core'; @Module({ imports: [ @@ -17,6 +20,7 @@ import { CylinderModule } from './modules/cylinder/cylinder.module'; envFilePath: ['.env'], isGlobal: true, }), + CacheModule.register({ ttl: 5000, isGlobal: true }), DatabaseModule, AuthModule, UserModule, @@ -24,8 +28,16 @@ import { CylinderModule } from './modules/cylinder/cylinder.module'; KeyModule, CustomerModule, CylinderModule, + SystemModule, ], controllers: [AppController], - providers: [AppService, AuthGuard], + providers: [ + AppService, + AuthGuard, + { + provide: APP_INTERCEPTOR, + useClass: CacheInterceptor, + }, + ], }) export class AppModule {} diff --git a/api/src/model/dto/index.ts b/api/src/model/dto/index.ts index 8de6036..c10427e 100644 --- a/api/src/model/dto/index.ts +++ b/api/src/model/dto/index.ts @@ -1,4 +1,3 @@ export * from './login.dto'; export * from './auth-code.dto'; -export * from './create-key-system.dto'; export * from './handover-key.dto'; diff --git a/api/src/modules/cylinder/cylinder.controller.ts b/api/src/modules/cylinder/cylinder.controller.ts index d4ae41c..e281290 100644 --- a/api/src/modules/cylinder/cylinder.controller.ts +++ b/api/src/modules/cylinder/cylinder.controller.ts @@ -1,4 +1,14 @@ -import { Controller, Delete, Get, Param, Req, UseGuards } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + Param, + Post, + Put, + Req, + UseGuards, +} from '@nestjs/common'; import { AuthGuard } from 'src/core/guards/auth.guard'; import { CylinderService } from './cylinder.service'; import { AuthenticatedRequest } from 'src/model/interface/authenticated-request.interface'; @@ -15,7 +25,23 @@ export class CylinderController { } @Delete(':id') - deleteKey(@Req() req: AuthenticatedRequest, @Param() id: string) { - return this.service.deleteKey(req.user, id); + deleteKey(@Req() req: AuthenticatedRequest, @Param('id') id: string) { + return this.service.deleteCylinder(req.user, id); + } + + @Put() + updateCylinder( + @Req() req: AuthenticatedRequest, + @Body() b: Partial, + ) { + return this.service.updateCylinder(req.user, b); + } + + @Post() + createCylinder( + @Req() req: AuthenticatedRequest, + @Body() b: Partial, + ) { + return this.service.createCylinder(req.user, b); } } diff --git a/api/src/modules/cylinder/cylinder.service.ts b/api/src/modules/cylinder/cylinder.service.ts index 20903f8..3d37299 100644 --- a/api/src/modules/cylinder/cylinder.service.ts +++ b/api/src/modules/cylinder/cylinder.service.ts @@ -19,12 +19,28 @@ export class CylinderService { return c; } - async deleteKey(user: User, id: string) { + async deleteCylinder(user: User, cylinderid: string) { const cylinder = await this.cylinderRepo.findOneOrFail({ - where: { id: id, system: { managers: { id: user.id } } }, + where: { id: cylinderid, system: { managers: { id: user.id } } }, relations: ['keys'], }); await this.keyRepo.softRemove(cylinder.keys); return this.cylinderRepo.softRemove(cylinder); } + + async updateCylinder(user: User, cylinder: Partial) { + const original = await this.cylinderRepo.findOneOrFail({ + where: { id: cylinder.id, system: { managers: { id: user.id } } }, + relations: ['keys', 'system'], + }); + + Object.keys(cylinder).forEach((k: string) => { + original[k] = cylinder[k]; + }); + return this.cylinderRepo.save(original); + } + + createCylinder(user: User, cylinder: Partial) { + return this.cylinderRepo.save(this.cylinderRepo.create(cylinder)); + } } diff --git a/api/src/modules/key/key.controller.ts b/api/src/modules/key/key.controller.ts index 3da25ec..6656e85 100644 --- a/api/src/modules/key/key.controller.ts +++ b/api/src/modules/key/key.controller.ts @@ -13,7 +13,6 @@ import { KeyService } from './key.service'; import { AuthenticatedRequest } from 'src/model/interface/authenticated-request.interface'; import { AuthGuard } from 'src/core/guards/auth.guard'; import { Key } from 'src/model/entitites'; -import { CreateKeySystemDto } from 'src/model/dto/create-key-system.dto'; @UseGuards(AuthGuard) @Controller('key') @@ -45,14 +44,6 @@ export class KeyController { return this.service.deleteKey(req.user, id); } - @Post('system') - createKeySystem( - @Req() req: AuthenticatedRequest, - @Body() body: CreateKeySystemDto, - ) { - return this.service.createKeySystem(req.user, body); - } - @Post(':id/handover') handoutKey( @Req() req: AuthenticatedRequest, diff --git a/api/src/modules/key/key.service.ts b/api/src/modules/key/key.service.ts index d3678ac..caa0c83 100644 --- a/api/src/modules/key/key.service.ts +++ b/api/src/modules/key/key.service.ts @@ -1,5 +1,4 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateKeySystemDto } from 'src/model/dto'; import { Cylinder, Key, User } from 'src/model/entitites'; import { IUser } from 'src/model/interface'; import { @@ -68,17 +67,6 @@ export class KeyService { }); } - async createKeySystem(user: User, systemDTO: CreateKeySystemDto) { - const sys = this.systemRepo.create(systemDTO); - sys.managers = [user]; - try { - const res = await this.systemRepo.save(sys); - return res; - } catch (e) { - throw new HttpException(e.code, HttpStatus.UNPROCESSABLE_ENTITY); - } - } - async handoverKey(user: IUser, data: any, keyID: string) { const key: Key = await this.keyrepository.findOneOrFail({ where: { id: keyID, cylinder: { system: { managers: { id: user.id } } } }, diff --git a/api/src/model/dto/create-key-system.dto.ts b/api/src/modules/system/dto/create-system.dto.ts similarity index 80% rename from api/src/model/dto/create-key-system.dto.ts rename to api/src/modules/system/dto/create-system.dto.ts index bb6afd6..91f009e 100644 --- a/api/src/model/dto/create-key-system.dto.ts +++ b/api/src/modules/system/dto/create-system.dto.ts @@ -1,6 +1,6 @@ import { IsNotEmpty, MaxLength, MinLength } from 'class-validator'; -export class CreateKeySystemDto { +export class CreateSystemDto { @IsNotEmpty() @MinLength(2) @MaxLength(255) diff --git a/api/src/modules/system/dto/update-system.dto.ts b/api/src/modules/system/dto/update-system.dto.ts new file mode 100644 index 0000000..34a50a1 --- /dev/null +++ b/api/src/modules/system/dto/update-system.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateSystemDto } from './create-system.dto'; + +export class UpdateSystemDto extends PartialType(CreateSystemDto) {} diff --git a/api/src/modules/system/system.controller.ts b/api/src/modules/system/system.controller.ts new file mode 100644 index 0000000..2b05524 --- /dev/null +++ b/api/src/modules/system/system.controller.ts @@ -0,0 +1,50 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, + Req, + UseGuards, +} from '@nestjs/common'; +import { SystemService } from './system.service'; +import { CreateSystemDto } from './dto/create-system.dto'; +import { UpdateSystemDto } from './dto/update-system.dto'; +import { AuthenticatedRequest } from 'src/model/interface/authenticated-request.interface'; +import { AuthGuard } from 'src/core/guards/auth.guard'; + +@UseGuards(AuthGuard) +@Controller('system') +export class SystemController { + constructor(private readonly systemService: SystemService) {} + + @Post() + create( + @Req() req: AuthenticatedRequest, + @Body() createSystemDto: CreateSystemDto, + ) { + return this.systemService.create(req.user, createSystemDto); + } + + @Get() + findAll(@Req() req: AuthenticatedRequest) { + return this.systemService.findAll(req.user); + } + + @Get(':id') + findOne(@Param('id') id: string) { + return this.systemService.findOne(id); + } + + @Patch(':id') + update(@Param('id') id: string, @Body() updateSystemDto: UpdateSystemDto) { + return this.systemService.update(id, updateSystemDto); + } + + @Delete(':id') + remove(@Param('id') id: string) { + return this.systemService.remove(id); + } +} diff --git a/api/src/modules/system/system.module.ts b/api/src/modules/system/system.module.ts new file mode 100644 index 0000000..4088a08 --- /dev/null +++ b/api/src/modules/system/system.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { SystemService } from './system.service'; +import { SystemController } from './system.controller'; +import { AuthModule } from '../auth/auth.module'; +import { DatabaseModule } from 'src/shared/database/database.module'; + +@Module({ + controllers: [SystemController], + providers: [SystemService], + imports: [AuthModule, DatabaseModule], +}) +export class SystemModule {} diff --git a/api/src/modules/system/system.service.spec.ts b/api/src/modules/system/system.service.spec.ts new file mode 100644 index 0000000..f78ad0b --- /dev/null +++ b/api/src/modules/system/system.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SystemService } from './system.service'; + +describe('SystemService', () => { + let service: SystemService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SystemService], + }).compile(); + + service = module.get(SystemService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/api/src/modules/system/system.service.ts b/api/src/modules/system/system.service.ts new file mode 100644 index 0000000..df894b8 --- /dev/null +++ b/api/src/modules/system/system.service.ts @@ -0,0 +1,42 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { CreateSystemDto } from './dto/create-system.dto'; +import { UpdateSystemDto } from './dto/update-system.dto'; +import { KeySystemRepository } from 'src/model/repositories'; +import { User } from 'src/model/entitites'; + +@Injectable() +export class SystemService { + constructor(private systemRepo: KeySystemRepository) {} + + async create(user: User, createSystemDto: CreateSystemDto) { + const sys = this.systemRepo.create(createSystemDto); + sys.managers = [user]; + try { + const res = await this.systemRepo.save(sys); + return res; + } catch (e) { + throw new HttpException(e.code, HttpStatus.UNPROCESSABLE_ENTITY); + } + } + + findAll(user: User) { + return this.systemRepo.find({ + where: { managers: { id: user.id } }, + order: { name: { direction: 'ASC' } }, + }); + } + + findOne(id: string) { + return `This action returns a #${id} system`; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + update(id: string, updateSystemDto: UpdateSystemDto) { + return `This action updates a #${id} system`; + } + + async remove(id: string) { + const system = await this.systemRepo.findOne({ where: { id } }); + return this.systemRepo.softRemove(system); + } +} diff --git a/client/src/app/app.routes.ts b/client/src/app/app.routes.ts index 2eef6e2..0ee2da3 100644 --- a/client/src/app/app.routes.ts +++ b/client/src/app/app.routes.ts @@ -8,13 +8,15 @@ import { DashboardComponent } from './modules/dashboard/dashboard.component'; import { AllUsersComponent } from './modules/admin/all-users/all-users.component'; import { KeysComponent } from './modules/keys/keys.component'; import { CylinderComponent } from './modules/cylinder/cylinder.component'; +import { SystemComponent } from './modules/system/system.component'; export const routes: Routes = [ { path: '', component: LayoutComponent, canActivate: [AuthenticatedGuard], children: [ { path: '', component: DashboardComponent }, { path: 'users', component: AllUsersComponent }, { path: 'keys', component: KeysComponent }, - { path: 'cylinders', component: CylinderComponent } + { path: 'cylinders', component: CylinderComponent }, + { path: 'systems', component: SystemComponent } ]}, { path: 'login', component: LoginComponent}, ]; diff --git a/client/src/app/core/layout/layout.component.html b/client/src/app/core/layout/layout.component.html index c53b9fe..523af4a 100644 --- a/client/src/app/core/layout/layout.component.html +++ b/client/src/app/core/layout/layout.component.html @@ -15,6 +15,7 @@ + diff --git a/client/src/app/model/interface/cylinder.interface.ts b/client/src/app/model/interface/cylinder.interface.ts new file mode 100644 index 0000000..845f69f --- /dev/null +++ b/client/src/app/model/interface/cylinder.interface.ts @@ -0,0 +1,12 @@ +import { IKey } from "./key.interface"; + +export interface ICylinder { + id: string; + name: string; + createdAt: string; + updatedAt: string; + deletedAt: string; + system: any; + keys: IKey[]; + keyCount: number; +} \ No newline at end of file diff --git a/client/src/app/model/interface/key.interface.ts b/client/src/app/model/interface/key.interface.ts index f881d4b..679c980 100644 --- a/client/src/app/model/interface/key.interface.ts +++ b/client/src/app/model/interface/key.interface.ts @@ -1,10 +1,12 @@ +import { ICylinder } from "./cylinder.interface"; + export interface IKey { id: string; name: string; createdAt: string; updatedAt: string; handedOut: boolean; - cylinder: any; + cylinder: ICylinder; nr: number; deletedAt?: string; } \ No newline at end of file diff --git a/client/src/app/modules/admin/all-users/all-users.component.ts b/client/src/app/modules/admin/all-users/all-users.component.ts index 5a7a8ca..43b3ef0 100644 --- a/client/src/app/modules/admin/all-users/all-users.component.ts +++ b/client/src/app/modules/admin/all-users/all-users.component.ts @@ -70,7 +70,6 @@ export class AllUsersComponent { next: n => { this.gridApi.setGridOption("rowData", n) this.gridApi.setGridOption("loading", false); - n.filter(u => u.username == 'mail@bastian-wagner.de').map((u: any) => { console.log(u['lastLogin'])}) } }) } @@ -98,7 +97,6 @@ export class AllUsersComponent { const self = this; this.gridApi.addEventListener("cellEditingStopped", evt => this.cellEditEnd(evt, self)) this.loadUsers(); - console.log(params) } } diff --git a/client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.html b/client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.html new file mode 100644 index 0000000..f55412d --- /dev/null +++ b/client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.html @@ -0,0 +1,9 @@ +

Zylinder {{key.name}} löschen?

+ +

Soll der Zylinder wirklich gelöscht werden? Alle {{ key.keyCount | number:'0.0-0'}} enthaltenen Schlüssel werden mit entfernt!

+ +
+ + + + \ No newline at end of file diff --git a/client/src/app/modules/start/start.component.scss b/client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.scss similarity index 100% rename from client/src/app/modules/start/start.component.scss rename to client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.scss diff --git a/client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.spec.ts b/client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.spec.ts new file mode 100644 index 0000000..440fe56 --- /dev/null +++ b/client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DeleteCylinderComponent } from './delete-cylinder.component'; + +describe('DeleteCylinderComponent', () => { + let component: DeleteCylinderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DeleteCylinderComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DeleteCylinderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.ts b/client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.ts new file mode 100644 index 0000000..d997ffe --- /dev/null +++ b/client/src/app/modules/cylinder/components/delete-cylinder/delete-cylinder.component.ts @@ -0,0 +1,18 @@ +import { CommonModule } from '@angular/common'; +import { Component, inject, LOCALE_ID } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; + +@Component({ + selector: 'app-delete-cylinder', + standalone: true, + imports: [MatDialogModule, MatButtonModule, CommonModule], + providers: [{ provide: LOCALE_ID, useValue: 'de-DE' },], + templateUrl: './delete-cylinder.component.html', + styleUrl: './delete-cylinder.component.scss' +}) +export class DeleteCylinderComponent { + readonly dialogRef = inject(MatDialogRef); + readonly key = inject(MAT_DIALOG_DATA); + +} diff --git a/client/src/app/modules/cylinder/cylinder.component.ts b/client/src/app/modules/cylinder/cylinder.component.ts index 5ba398d..0b404f5 100644 --- a/client/src/app/modules/cylinder/cylinder.component.ts +++ b/client/src/app/modules/cylinder/cylinder.component.ts @@ -4,6 +4,7 @@ import { GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community'; import { AgGridAngular } from 'ag-grid-angular'; import { ApiService } from '../../shared/api.service'; import { DatePipe } from '@angular/common'; +import { AgDeleteCylinderComponent } from '../../shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component'; @Component({ selector: 'app-cylinder', @@ -30,6 +31,12 @@ export class CylinderComponent { { field: 'keyCount', headerName: 'Anzahl Schlüssel', flex: 0, type: 'number' }, { field: 'createdAt', headerName: 'Angelegt', cellRenderer: (data: any) => data.value ? this.datePipe.transform(new Date(data.value)) : '-' }, { field: 'updatedAt', headerName: 'Upgedated', cellRenderer: (data: any) => data.value ? this.datePipe.transform(new Date(data.value)) : '-' }, + { + colId: 'actions' + , headerName: 'Aktionen' + , width: 120 + , cellRenderer: AgDeleteCylinderComponent + } ] } diff --git a/client/src/app/modules/keys/keys.component.ts b/client/src/app/modules/keys/keys.component.ts index b401cb0..c2b6b57 100644 --- a/client/src/app/modules/keys/keys.component.ts +++ b/client/src/app/modules/keys/keys.component.ts @@ -83,9 +83,7 @@ export class KeysComponent { } deleteKey(id: string) { - this.api.deleteKey(id).subscribe({ - next: n => console.log(n) - }) + this.api.deleteKey(id).subscribe() } ngOnInit(): void { diff --git a/client/src/app/modules/start/start.component.html b/client/src/app/modules/start/start.component.html deleted file mode 100644 index 9f40752..0000000 --- a/client/src/app/modules/start/start.component.html +++ /dev/null @@ -1 +0,0 @@ -

start works!

diff --git a/client/src/app/modules/start/start.component.ts b/client/src/app/modules/start/start.component.ts deleted file mode 100644 index 683e197..0000000 --- a/client/src/app/modules/start/start.component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { HttpClient } from '@angular/common/http'; -import { Component, inject } from '@angular/core'; - -@Component({ - selector: 'app-start', - standalone: true, - imports: [], - templateUrl: './start.component.html', - styleUrl: './start.component.scss' -}) -export class StartComponent { - private http: HttpClient = inject(HttpClient); - - ngOnInit(): void { - this.http.get('/api/').subscribe(res => { - console.log(res) - }) - } -} diff --git a/client/src/app/modules/system/system.component.html b/client/src/app/modules/system/system.component.html new file mode 100644 index 0000000..112977c --- /dev/null +++ b/client/src/app/modules/system/system.component.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/client/src/app/modules/system/system.component.scss b/client/src/app/modules/system/system.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/client/src/app/modules/start/start.component.spec.ts b/client/src/app/modules/system/system.component.spec.ts similarity index 55% rename from client/src/app/modules/start/start.component.spec.ts rename to client/src/app/modules/system/system.component.spec.ts index 03e2127..bae84c7 100644 --- a/client/src/app/modules/start/start.component.spec.ts +++ b/client/src/app/modules/system/system.component.spec.ts @@ -1,18 +1,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { StartComponent } from './start.component'; +import { SystemComponent } from './system.component'; -describe('StartComponent', () => { - let component: StartComponent; - let fixture: ComponentFixture; +describe('SystemComponent', () => { + let component: SystemComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [StartComponent] + imports: [SystemComponent] }) .compileComponents(); - fixture = TestBed.createComponent(StartComponent); + fixture = TestBed.createComponent(SystemComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/client/src/app/modules/system/system.component.ts b/client/src/app/modules/system/system.component.ts new file mode 100644 index 0000000..e0a58e6 --- /dev/null +++ b/client/src/app/modules/system/system.component.ts @@ -0,0 +1,47 @@ +import { DatePipe } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { AgGridAngular } from 'ag-grid-angular'; +import { GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community'; +import { ApiService } from '../../shared/api.service'; +import { HELPER } from '../../shared/helper.service'; + +@Component({ + selector: 'app-system', + standalone: true, + imports: [AgGridAngular], + providers: [DatePipe], + templateUrl: './system.component.html', + styleUrl: './system.component.scss' +}) +export class SystemComponent { + private api: ApiService = inject(ApiService); + private datePipe = inject(DatePipe); + + gridApi!: GridApi; + + gridOptions: GridOptions = HELPER.getGridOptions(); + + constructor() { + this.gridOptions.columnDefs = [ + { colId: 'name', field: 'name', headerName: 'Name', sort: 'asc', flex: 1}, + { field: 'createdAt', headerName: 'Angelegt', cellRenderer: (data: any) => data.value ? this.datePipe.transform(new Date(data.value)) : '-' }, + { field: 'updatedAt', headerName: 'Upgedated', cellRenderer: (data: any) => data.value ? this.datePipe.transform(new Date(data.value)) : '-' }, + ] + } + + + loadSystems() { + this.api.getSystems().subscribe({ + next: n => { + this.gridApi.setGridOption("rowData", n); + this.gridApi.setGridOption("loading", false); + } + }) + } + + onGridReady(params: GridReadyEvent) { + this.gridApi = params.api; + this.loadSystems(); + } + +} diff --git a/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.html b/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.html new file mode 100644 index 0000000..4ee713e --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.html @@ -0,0 +1 @@ +

ag-base-component works!

diff --git a/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.scss b/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.spec.ts b/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.spec.ts new file mode 100644 index 0000000..60c7dc1 --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AgBaseComponentComponent } from './ag-base-component.component'; + +describe('AgBaseComponentComponent', () => { + let component: AgBaseComponentComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AgBaseComponentComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AgBaseComponentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.ts b/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.ts new file mode 100644 index 0000000..23394da --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-base-component/ag-base-component.component.ts @@ -0,0 +1,28 @@ +import { Component, inject } from '@angular/core'; +import { MatDialog, MatDialogModule } from '@angular/material/dialog'; +import { HotToastService } from '@ngxpert/hot-toast'; +import { ApiService } from '../../../api.service'; +import { ICellRendererAngularComp } from 'ag-grid-angular'; +import { ICellRendererParams } from 'ag-grid-community'; +import { MatTooltipModule } from '@angular/material/tooltip'; + +@Component({ + standalone: true, + imports: [MatDialogModule, MatTooltipModule], + templateUrl: './ag-base-component.component.html', + styleUrl: './ag-base-component.component.scss' +}) +export class AgBaseComponentComponent implements ICellRendererAngularComp { + protected api: ApiService = inject(ApiService); + protected dialog: MatDialog = inject(MatDialog); + protected toast = inject(HotToastService); + params!: ICellRendererParams; + + agInit(params: ICellRendererParams): void { + this.params = params; + } + refresh(params: ICellRendererParams): boolean { + return false; + } + +} diff --git a/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.html b/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.html new file mode 100644 index 0000000..b8a2d10 --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.scss b/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.spec.ts b/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.spec.ts new file mode 100644 index 0000000..d9781be --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AgDeleteCylinderComponent } from './ag-delete-cylinder.component'; + +describe('AgDeleteCylinderComponent', () => { + let component: AgDeleteCylinderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AgDeleteCylinderComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AgDeleteCylinderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.ts b/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.ts new file mode 100644 index 0000000..21d5f86 --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-delete-cylinder/ag-delete-cylinder.component.ts @@ -0,0 +1,47 @@ +import { Component } from '@angular/core'; +import { AgBaseComponentComponent } from '../ag-base-component/ag-base-component.component'; +import { DeleteCylinderComponent } from '../../../../modules/cylinder/components/delete-cylinder/delete-cylinder.component'; +import { MatTooltipModule } from '@angular/material/tooltip'; + +@Component({ + selector: 'app-ag-delete-cylinder', + standalone: true, + imports: [MatTooltipModule], + templateUrl: './ag-delete-cylinder.component.html', + styleUrl: './ag-delete-cylinder.component.scss' +}) +export class AgDeleteCylinderComponent extends AgBaseComponentComponent { + + + delete() { + const ref = this.dialog.open(DeleteCylinderComponent, { + data: this.params.data, + autoFocus: false + }) + + ref.afterClosed().subscribe({ + next: n => { + if (n) { + this.deleteThisCylinder(); + } + } + }) + } + + deleteThisCylinder() { + this.api.deleteCylinder(this.params.data) + .pipe( + this.toast.observe({ + loading: 'Löschen...', + success: 'Gelöscht!', + error: 'Konnte nicht gelöscht werden' + }) + ).subscribe({ + next: () => { + const rows = this.params.api.getGridOption("rowData")?.filter(r => r.id != this.params.data.id); + this.params.api.setGridOption("rowData", rows); + } + }) + } + +} diff --git a/client/src/app/shared/api.service.ts b/client/src/app/shared/api.service.ts index 4868d1d..9e0cf2e 100644 --- a/client/src/app/shared/api.service.ts +++ b/client/src/app/shared/api.service.ts @@ -3,6 +3,7 @@ import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { IUser } from '../model/interface/user.interface'; import { IKey } from '../model/interface/key.interface'; +import { ICylinder } from '../model/interface/cylinder.interface'; @Injectable({ providedIn: 'root' @@ -37,8 +38,12 @@ export class ApiService { return this.http.post('api/key', key); } - postKeySystem(keySystem: any) { - return this.http.post('api/key/system', keySystem); + createSystem(keySystem: any) { + return this.http.post('api/system', keySystem); + } + + getSystems(): Observable { + return this.http.get('api/system'); } handoverKey(data: any) { @@ -69,7 +74,11 @@ export class ApiService { return this.http.put(`api/key/${id}/restore`, null); } - getCylinders(): Observable { - return this.http.get('api/cylinder'); + getCylinders(): Observable { + return this.http.get('api/cylinder'); + } + + deleteCylinder(cylinder: ICylinder): Observable { + return this.http.delete(`api/cylinder/${cylinder.id}`) } }