From b4539451833d4cf1970b22632472144736ef4772 Mon Sep 17 00:00:00 2001 From: Bastian Wagner Date: Wed, 23 Oct 2024 13:58:14 +0200 Subject: [PATCH] Archive --- api/src/model/entitites/key.entity.ts | 6 +- api/src/modules/key/key.controller.ts | 24 ++++- api/src/modules/key/key.service.ts | 32 +++++++ .../src/app/model/interface/key.interface.ts | 1 + .../components/archive/archive.component.html | 12 +++ .../components/archive/archive.component.scss | 3 + .../archive/archive.component.spec.ts | 23 +++++ .../components/archive/archive.component.ts | 91 +++++++++++++++++++ .../delete-key/delete-key.component.html | 9 ++ .../delete-key/delete-key.component.scss | 0 .../delete-key/delete-key.component.spec.ts | 23 +++++ .../delete-key/delete-key.component.ts | 17 ++++ .../handover-dialog.component.ts | 1 + .../modules/keys/create/create.component.html | 13 ++- .../modules/keys/create/create.component.scss | 1 + .../modules/keys/create/create.component.ts | 21 ++++- .../src/app/modules/keys/keys.component.html | 17 ++-- .../src/app/modules/keys/keys.component.scss | 4 +- client/src/app/modules/keys/keys.component.ts | 48 +++++++++- .../ag-delete-key.component.html | 1 + .../ag-delete-key.component.scss | 8 ++ .../ag-delete-key.component.spec.ts | 23 +++++ .../ag-delete-key/ag-delete-key.component.ts | 69 ++++++++++++++ .../ag-loading/ag-loading.component.html | 1 + .../ag-loading/ag-loading.component.scss | 0 .../ag-loading/ag-loading.component.spec.ts | 23 +++++ .../ag-loading/ag-loading.component.ts | 21 +++++ client/src/app/shared/api.service.ts | 12 +++ client/src/app/shared/helper.service.ts | 16 ++++ client/src/assets/img/delete.svg | 1 + client/src/assets/img/inbox.svg | 60 ++++++++++++ client/src/assets/img/restore.svg | 1 + client/src/styles.scss | 7 ++ 33 files changed, 570 insertions(+), 19 deletions(-) create mode 100644 client/src/app/modules/keys/components/archive/archive.component.html create mode 100644 client/src/app/modules/keys/components/archive/archive.component.scss create mode 100644 client/src/app/modules/keys/components/archive/archive.component.spec.ts create mode 100644 client/src/app/modules/keys/components/archive/archive.component.ts create mode 100644 client/src/app/modules/keys/components/delete-key/delete-key.component.html create mode 100644 client/src/app/modules/keys/components/delete-key/delete-key.component.scss create mode 100644 client/src/app/modules/keys/components/delete-key/delete-key.component.spec.ts create mode 100644 client/src/app/modules/keys/components/delete-key/delete-key.component.ts create mode 100644 client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.html create mode 100644 client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.scss create mode 100644 client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.spec.ts create mode 100644 client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.ts create mode 100644 client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.html create mode 100644 client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.scss create mode 100644 client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.spec.ts create mode 100644 client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.ts create mode 100644 client/src/app/shared/helper.service.ts create mode 100644 client/src/assets/img/delete.svg create mode 100644 client/src/assets/img/inbox.svg create mode 100644 client/src/assets/img/restore.svg diff --git a/api/src/model/entitites/key.entity.ts b/api/src/model/entitites/key.entity.ts index 5e26cf2..ca7c9a3 100644 --- a/api/src/model/entitites/key.entity.ts +++ b/api/src/model/entitites/key.entity.ts @@ -1,6 +1,7 @@ import { Column, CreateDateColumn, + DeleteDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, @@ -18,7 +19,7 @@ export class Key implements IKey { @Column({ nullable: true }) name: string; - @Column({ name: 'key_number', unique: true }) + @Column({ name: 'key_number', unique: false }) nr: number; @Column({ name: 'handed_out', default: false }) @@ -35,4 +36,7 @@ export class Key implements IKey { @UpdateDateColumn({ name: 'updatet_at' }) updatedAt: Date; + + @DeleteDateColumn() + deletedAt: Date; } diff --git a/api/src/modules/key/key.controller.ts b/api/src/modules/key/key.controller.ts index 1962aaa..0f14213 100644 --- a/api/src/modules/key/key.controller.ts +++ b/api/src/modules/key/key.controller.ts @@ -1,7 +1,10 @@ import { Body, Controller, + Delete, Get, + HttpException, + HttpStatus, Param, Post, Put, @@ -13,7 +16,6 @@ import { AuthenticatedRequest } from 'src/model/interface/authenticated-request. import { AuthGuard } from 'src/core/guards/auth.guard'; import { Key } from 'src/model/entitites'; import { CreateKeySystemDto } from 'src/model/dto/create-key-system.dto'; -import { HandoverKeyDto } from 'src/model/dto'; @UseGuards(AuthGuard) @Controller('key') @@ -25,11 +27,26 @@ export class KeyController { return this.service.getUsersKeys(req.user); } + @Post() + postKey(@Req() req: AuthenticatedRequest, @Body() key: Key) { + return this.service.createKey(req.user, key); + } + @Put() updateKey(@Req() req: AuthenticatedRequest, @Body() key: Key) { return this.service.updateKey(req.user, key); } + @Put(':id/restore') + restoreKey(@Req() req: AuthenticatedRequest, @Param('id') id: string) { + return this.service.restoreKey(req.user, id); + } + + @Delete(':id') + deleteKey(@Req() req: AuthenticatedRequest, @Param('id') id: string) { + return this.service.deleteKey(req.user, id); + } + @Get('cylinder') getCylinders(@Req() req: AuthenticatedRequest) { return this.service.getUsersCylinders(req.user); @@ -56,4 +73,9 @@ export class KeyController { getKeyHandouts(@Req() req: AuthenticatedRequest, @Param('id') id: string) { return this.service.getKeyHandovers(req.user, id); } + + @Get('Archive') + getArchive(@Req() req: AuthenticatedRequest) { + return this.service.getDeletedKeys(req.user); + } } diff --git a/api/src/modules/key/key.service.ts b/api/src/modules/key/key.service.ts index f9607cf..d3678ac 100644 --- a/api/src/modules/key/key.service.ts +++ b/api/src/modules/key/key.service.ts @@ -9,6 +9,7 @@ import { KeySystemRepository, } from 'src/model/repositories'; import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository'; +import { IsNull, Not } from 'typeorm'; @Injectable() export class KeyService { @@ -108,4 +109,35 @@ export class KeyService { relations: ['customer'], }); } + + createKey(user: User, key: any) { + return this.keyrepository.save(this.keyrepository.create(key)); + } + + async deleteKey(user: User, id: string) { + const key = await this.keyrepository.findOneOrFail({ + where: { id, cylinder: { system: { managers: { id: user.id } } } }, + }); + return this.keyrepository.softRemove(key); + } + + getDeletedKeys(user: User) { + return this.keyrepository.find({ + where: { + cylinder: { system: { managers: { id: user.id } } }, + deletedAt: Not(IsNull()), + }, + withDeleted: true, + order: { deletedAt: { direction: 'DESC' } }, + }); + } + + async restoreKey(user: User, keyID: string) { + const key = await this.keyrepository.findOneOrFail({ + where: { cylinder: { system: { managers: { id: user.id } } }, id: keyID }, + withDeleted: true, + }); + key.deletedAt = null; + return this.keyrepository.save(key); + } } diff --git a/client/src/app/model/interface/key.interface.ts b/client/src/app/model/interface/key.interface.ts index ad9dae6..f881d4b 100644 --- a/client/src/app/model/interface/key.interface.ts +++ b/client/src/app/model/interface/key.interface.ts @@ -6,4 +6,5 @@ export interface IKey { handedOut: boolean; cylinder: any; nr: number; + deletedAt?: string; } \ No newline at end of file diff --git a/client/src/app/modules/keys/components/archive/archive.component.html b/client/src/app/modules/keys/components/archive/archive.component.html new file mode 100644 index 0000000..c27ca19 --- /dev/null +++ b/client/src/app/modules/keys/components/archive/archive.component.html @@ -0,0 +1,12 @@ +

Gelöschte Schlüssel

+ + + + + + + \ No newline at end of file diff --git a/client/src/app/modules/keys/components/archive/archive.component.scss b/client/src/app/modules/keys/components/archive/archive.component.scss new file mode 100644 index 0000000..0c10519 --- /dev/null +++ b/client/src/app/modules/keys/components/archive/archive.component.scss @@ -0,0 +1,3 @@ +mat-dialog-content{ + +} \ No newline at end of file diff --git a/client/src/app/modules/keys/components/archive/archive.component.spec.ts b/client/src/app/modules/keys/components/archive/archive.component.spec.ts new file mode 100644 index 0000000..642dd39 --- /dev/null +++ b/client/src/app/modules/keys/components/archive/archive.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ArchiveComponent } from './archive.component'; + +describe('ArchiveComponent', () => { + let component: ArchiveComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ArchiveComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ArchiveComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/modules/keys/components/archive/archive.component.ts b/client/src/app/modules/keys/components/archive/archive.component.ts new file mode 100644 index 0000000..088ae36 --- /dev/null +++ b/client/src/app/modules/keys/components/archive/archive.component.ts @@ -0,0 +1,91 @@ +import { AG_GRID_LOCALE_DE } from '@ag-grid-community/locale'; +import { Component, inject, LOCALE_ID } from '@angular/core'; +import { MatDialogModule } from '@angular/material/dialog'; +import { AgGridAngular } from 'ag-grid-angular'; +import { GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community'; +import { ApiService } from '../../../../shared/api.service'; +import { DatePipe } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { IKey } from '../../../../model/interface/key.interface'; +import { HotToastService } from '@ngxpert/hot-toast'; +import { AgLoadingComponent } from '../../../../shared/ag-grid/components/ag-loading/ag-loading.component'; +import { HELPER } from '../../../../shared/helper.service'; + +@Component({ + selector: 'app-archive', + standalone: true, + imports: [MatDialogModule, AgGridAngular, MatButtonModule, MatIconModule], + providers: [DatePipe, { provide: LOCALE_ID, useValue: 'de-DE' }], + templateUrl: './archive.component.html', + styleUrl: './archive.component.scss' +}) +export class ArchiveComponent { + private api: ApiService = inject(ApiService); + private datePipe = inject(DatePipe); + private toast = inject(HotToastService); + + public dataChanged = false; + + gridApi!: GridApi; + + gridOptions: GridOptions = HELPER.getGridOptions(); + + constructor() { + this.gridOptions.columnDefs = [ + { colId: 'name', field: 'name' , headerName: 'Name', flex: 1, editable: true, sort: 'asc', filter: true }, + { colId: 'nr', field: 'nr' , headerName: 'Schlüsselnummer', flex: 1, editable: true, filter: true }, + { + field: 'deletedAt' + , headerName: 'Gelöscht' + , width: 160 + , type: 'date' + , cellRenderer: (data: any) => this.datePipe.transform(new Date(data.value), 'short') + }, + { + width: 40, + cellRenderer: () => '
', + onCellClicked: (event) => { this.restoreKey(event.data);} + } + ] + } + + onGridReady(params: GridReadyEvent) { + this.gridApi = params.api; + this.loadKeys(); + } + + + loadKeys() { + this.gridApi.setGridOption("loading", true); + this.api.getKeyArchive().subscribe({ + next: n => { + this.gridApi.setGridOption("rowData", n); + this.gridApi.setGridOption("loading", false); + } + }) + } + + restoreKey(key: IKey) { + this.gridApi.setGridOption("loading", true); + key.deletedAt = undefined; + this.api.restoreKey(key.id) + .pipe( + this.toast.observe({ + loading: 'Stelle wiederher...', + success: 'Schlüssel wiederhergestellt', + error: 'Es ist ein Fehler aufgetreten' + }) + ) + .subscribe({ + next: () => { + this.dataChanged = true; + this.loadKeys(); + }, + error: () => { + this.gridApi.setGridOption("loading", false); + } + }); + } + +} diff --git a/client/src/app/modules/keys/components/delete-key/delete-key.component.html b/client/src/app/modules/keys/components/delete-key/delete-key.component.html new file mode 100644 index 0000000..de34798 --- /dev/null +++ b/client/src/app/modules/keys/components/delete-key/delete-key.component.html @@ -0,0 +1,9 @@ +

Schlüssel {{key.name}} löschen?

+ +

Soll der Schlüssel wirklich gelöscht werden?

+ +
+ + + + \ No newline at end of file diff --git a/client/src/app/modules/keys/components/delete-key/delete-key.component.scss b/client/src/app/modules/keys/components/delete-key/delete-key.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/client/src/app/modules/keys/components/delete-key/delete-key.component.spec.ts b/client/src/app/modules/keys/components/delete-key/delete-key.component.spec.ts new file mode 100644 index 0000000..421d870 --- /dev/null +++ b/client/src/app/modules/keys/components/delete-key/delete-key.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DeleteKeyComponent } from './delete-key.component'; + +describe('DeleteKeyComponent', () => { + let component: DeleteKeyComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DeleteKeyComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DeleteKeyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/modules/keys/components/delete-key/delete-key.component.ts b/client/src/app/modules/keys/components/delete-key/delete-key.component.ts new file mode 100644 index 0000000..95af08e --- /dev/null +++ b/client/src/app/modules/keys/components/delete-key/delete-key.component.ts @@ -0,0 +1,17 @@ +import { Component, inject } from '@angular/core'; +import { IKey } from '../../../../model/interface/key.interface'; +import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; +import { MatButtonModule } from '@angular/material/button'; + +@Component({ + selector: 'app-delete-key', + standalone: true, + imports: [MatDialogModule, MatButtonModule], + templateUrl: './delete-key.component.html', + styleUrl: './delete-key.component.scss' +}) +export class DeleteKeyComponent { + readonly dialogRef = inject(MatDialogRef); + readonly key = inject(MAT_DIALOG_DATA); + +} diff --git a/client/src/app/modules/keys/components/handover-dialog/handover-dialog.component.ts b/client/src/app/modules/keys/components/handover-dialog/handover-dialog.component.ts index dabde7d..5d46749 100644 --- a/client/src/app/modules/keys/components/handover-dialog/handover-dialog.component.ts +++ b/client/src/app/modules/keys/components/handover-dialog/handover-dialog.component.ts @@ -51,6 +51,7 @@ export class HandoverDialogComponent { gridOptions: GridOptions = { localeText: AG_GRID_LOCALE_DE, rowData: [], + isRowSelectable: () => false, columnDefs: [ { colId: 'customer', field: 'customer.name' , headerName: 'Kunde', flex: 1, editable: false, filter: false}, { diff --git a/client/src/app/modules/keys/create/create.component.html b/client/src/app/modules/keys/create/create.component.html index 7910add..ddd11f7 100644 --- a/client/src/app/modules/keys/create/create.component.html +++ b/client/src/app/modules/keys/create/create.component.html @@ -1,15 +1,21 @@ -

Schlüssel anlegen

+

Neuen Schlüssel anlegen

Name - + + @if ((createForm.controls.name.value || '').length > 20) { + {{ (createForm.controls.name.value || '').length }} / 100 Zeichen + } @else { + Wie soll der Schlüssel heißen? + } Schlüsselnummer - + + Nummer auf dem Schlüssel @@ -19,6 +25,7 @@ {{ item.name }} } + Wo sperrt der Schlüssel?
diff --git a/client/src/app/modules/keys/create/create.component.scss b/client/src/app/modules/keys/create/create.component.scss index df8ee7a..1057c49 100644 --- a/client/src/app/modules/keys/create/create.component.scss +++ b/client/src/app/modules/keys/create/create.component.scss @@ -1,4 +1,5 @@ form { display: flex; flex-direction: column; + gap: 12px; } \ No newline at end of file diff --git a/client/src/app/modules/keys/create/create.component.ts b/client/src/app/modules/keys/create/create.component.ts index a3b3073..cabe021 100644 --- a/client/src/app/modules/keys/create/create.component.ts +++ b/client/src/app/modules/keys/create/create.component.ts @@ -1,24 +1,27 @@ import { Component, inject } from '@angular/core'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; +import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { ApiService } from '../../../shared/api.service'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { map, Observable, startWith } from 'rxjs'; import { MatSelectModule } from '@angular/material/select'; +import { HotToastService } from '@ngxpert/hot-toast'; @Component({ selector: 'app-create', standalone: true, - imports: [MatDialogModule, MatButtonModule, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatInputModule, MatSelectModule], + imports: [MatDialogModule, MatButtonModule, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatDialogModule], templateUrl: './create.component.html', styleUrl: './create.component.scss' }) export class CreateKeyComponent { private api: ApiService = inject(ApiService); + private toast: HotToastService = inject(HotToastService); + readonly dialogRef = inject(MatDialogRef); createForm = new FormGroup({ name: new FormControl(null, Validators.required), @@ -54,7 +57,19 @@ export class CreateKeyComponent { } save() { - console.log(this.createForm.value) this.api.createKey(this.createForm.value as any) + .pipe( + this.toast.observe({ + error: 'Konnte nicht angelegt werden...', + loading: 'Speichern...', + success: 'Gespeichert' + }) + ) + .subscribe({ + next: key => { + this.createForm.reset(); + this.dialogRef.close(key); + } + }) } } diff --git a/client/src/app/modules/keys/keys.component.html b/client/src/app/modules/keys/keys.component.html index a53fd3b..6bcc26b 100644 --- a/client/src/app/modules/keys/keys.component.html +++ b/client/src/app/modules/keys/keys.component.html @@ -1,9 +1,10 @@ -@if (gridOptions || true) { - -} + - \ No newline at end of file +
+ + +
diff --git a/client/src/app/modules/keys/keys.component.scss b/client/src/app/modules/keys/keys.component.scss index 1472c97..495a9a3 100644 --- a/client/src/app/modules/keys/keys.component.scss +++ b/client/src/app/modules/keys/keys.component.scss @@ -1,5 +1,7 @@ -.btn-create { +.floating-btn-container { position: absolute; bottom: 12px; right: 12px; + display: flex; + gap: 12px; } \ No newline at end of file diff --git a/client/src/app/modules/keys/keys.component.ts b/client/src/app/modules/keys/keys.component.ts index f650a6e..80f3fff 100644 --- a/client/src/app/modules/keys/keys.component.ts +++ b/client/src/app/modules/keys/keys.component.ts @@ -10,11 +10,15 @@ import { MatButtonModule } from '@angular/material/button'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { CreateKeyComponent } from './create/create.component'; import { AgOpenHandoutComponent } from '../../shared/ag-grid/components/ag-open-handout/ag-open-handout.component'; +import { AgDeleteKeyComponent } from '../../shared/ag-grid/components/ag-delete-key/ag-delete-key.component'; +import { MatIconModule } from '@angular/material/icon'; +import { ArchiveComponent } from './components/archive/archive.component'; +import { AgLoadingComponent } from '../../shared/ag-grid/components/ag-loading/ag-loading.component'; @Component({ selector: 'app-keys', standalone: true, - imports: [AgGridAngular, MatButtonModule, MatDialogModule], + imports: [AgGridAngular, MatButtonModule, MatDialogModule, MatIconModule], providers: [DatePipe], templateUrl: './keys.component.html', styleUrl: './keys.component.scss' @@ -70,9 +74,24 @@ export class KeysComponent { , cellRenderer: (data: any) => data.value ? this.datePipe.transform(new Date(data.value)) : '-' , tooltipValueGetter: (data: any) => this.datePipe.transform(new Date(data.value), 'medium') } + + ,{ + colId: 'delete' + , headerName: 'Löschen' + , width: 120 + , cellRenderer: AgDeleteKeyComponent + // , onCellClicked: (event) => { this.deleteKey(event.data.id)} + } ], loading: true, rowHeight: 36, + loadingOverlayComponent: AgLoadingComponent + } + + deleteKey(id: string) { + this.api.deleteKey(id).subscribe({ + next: n => console.log(n) + }) } ngOnInit(): void { @@ -118,6 +137,31 @@ export class KeysComponent { } openCreateKey() { - this.dialog.open(CreateKeyComponent) + this.dialog.open(CreateKeyComponent).afterClosed().subscribe({ + next: key => { + if (key) { + let d = [...this.gridApi.getGridOption("rowData") || [], key]; + this.gridApi.setGridOption("rowData", d); + this.loadKeys(); + } + } + }) + } + + openArchive() { + this.dialog.open(ArchiveComponent, { + maxHeight: "calc(100vh - 24px)", + maxWidth: "calc(100vw - 24px)", + width: "50vw", + minWidth: "300px", + height: "70vh", + disableClose: true + }).afterClosed().subscribe({ + next: changed => { + if (changed) { + this.loadKeys(); + } + } + }) } } diff --git a/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.html b/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.html new file mode 100644 index 0000000..322d90b --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.scss b/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.scss new file mode 100644 index 0000000..9584b42 --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.scss @@ -0,0 +1,8 @@ +:host { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.spec.ts b/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.spec.ts new file mode 100644 index 0000000..66a7904 --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AgDeleteKeyComponent } from './ag-delete-key.component'; + +describe('AgDeleteKeyComponent', () => { + let component: AgDeleteKeyComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AgDeleteKeyComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AgDeleteKeyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.ts b/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.ts new file mode 100644 index 0000000..5472e8e --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-delete-key/ag-delete-key.component.ts @@ -0,0 +1,69 @@ +import { Component, inject } from '@angular/core'; +import { ICellRendererAngularComp } from 'ag-grid-angular'; +import { ICellRendererParams } from 'ag-grid-community'; +import { IKey } from '../../../../model/interface/key.interface'; +import { ApiService } from '../../../api.service'; +import { MatDialog, MatDialogModule } from '@angular/material/dialog'; +import { DeleteKeyComponent } from '../../../../modules/keys/components/delete-key/delete-key.component'; +import { HotToastService } from '@ngxpert/hot-toast'; + +@Component({ + selector: 'app-ag-delete-key', + standalone: true, + imports: [MatDialogModule], + templateUrl: './ag-delete-key.component.html', + styleUrl: './ag-delete-key.component.scss' +}) +export class AgDeleteKeyComponent implements ICellRendererAngularComp { + key!: IKey; + params!: ICellRendererParams; + + private api: ApiService = inject(ApiService); + private dialog: MatDialog = inject(MatDialog); + private toast = inject(HotToastService); + + agInit(params: ICellRendererParams): void { + this.params = params; + this.key = params.data; + } + refresh(params: ICellRendererParams): boolean { + return false; + } + + delete() { + const ref = this.dialog.open(DeleteKeyComponent, { + data: this.key, + autoFocus: false + }) + + ref.afterClosed().subscribe({ + next: n => { + if (n) { + this.deleteThisKey(); + } + } + }) + } + + deleteThisKey() { + this.params.api.setGridOption("loading", true); + this.api.deleteKey(this.key.id).pipe( + this.toast.observe({ + loading: 'Lösche Schlüssel ' + this.key.name, + success: 'Schlüssel ' + this.key.name + ' gelöscht', + error: 'Konnte nicht gelöscht werden' + }) + ).subscribe({ + next: () => { + let data = this.params.api.getGridOption("rowData"); + data = data?.filter(d => d.id != this.key.id); + this.params.api.setGridOption("rowData", data); + this.params.api.setGridOption("loading", false); + }, + error: () => { + this.params.api.setGridOption("loading", false); + } + }) + + } +} diff --git a/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.html b/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.html new file mode 100644 index 0000000..7ba13e9 --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.scss b/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.spec.ts b/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.spec.ts new file mode 100644 index 0000000..bb05d19 --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AgLoadingComponent } from './ag-loading.component'; + +describe('AgLoadingComponent', () => { + let component: AgLoadingComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AgLoadingComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AgLoadingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.ts b/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.ts new file mode 100644 index 0000000..c68916a --- /dev/null +++ b/client/src/app/shared/ag-grid/components/ag-loading/ag-loading.component.ts @@ -0,0 +1,21 @@ +import { Component } from '@angular/core'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { ILoadingOverlayAngularComp } from 'ag-grid-angular'; +import { ILoadingOverlayParams } from 'ag-grid-community'; + +@Component({ + selector: 'app-ag-loading', + standalone: true, + imports: [MatProgressSpinnerModule], + templateUrl: './ag-loading.component.html', + styleUrl: './ag-loading.component.scss' +}) +export class AgLoadingComponent implements ILoadingOverlayAngularComp { + agInit(params: ILoadingOverlayParams): void { + + } + refresh?(params: ILoadingOverlayParams): void { + + } + +} diff --git a/client/src/app/shared/api.service.ts b/client/src/app/shared/api.service.ts index a506d72..312a9b6 100644 --- a/client/src/app/shared/api.service.ts +++ b/client/src/app/shared/api.service.ts @@ -38,6 +38,7 @@ export class ApiService { } createKey(key: any) { + return this.http.post('api/key', key); } postKeySystem(keySystem: any) { @@ -60,4 +61,15 @@ export class ApiService { return this.http.get('api/customer') } + deleteKey(id: string) { + return this.http.delete(`api/key/${id}`); + } + + getKeyArchive(): Observable { + return this.http.get('api/key/archive'); + } + + restoreKey(id: string) { + return this.http.put(`api/key/${id}/restore`, null); + } } diff --git a/client/src/app/shared/helper.service.ts b/client/src/app/shared/helper.service.ts new file mode 100644 index 0000000..0e8b2a9 --- /dev/null +++ b/client/src/app/shared/helper.service.ts @@ -0,0 +1,16 @@ +import { AG_GRID_LOCALE_DE } from "@ag-grid-community/locale"; +import { GridOptions } from "ag-grid-community"; +import { AgLoadingComponent } from "./ag-grid/components/ag-loading/ag-loading.component"; + +export class HELPER { + + static getGridOptions(): GridOptions { + return { + localeText: AG_GRID_LOCALE_DE, + rowData: [], + columnDefs: [], + loading: true, + loadingOverlayComponent: AgLoadingComponent + } + } +} \ No newline at end of file diff --git a/client/src/assets/img/delete.svg b/client/src/assets/img/delete.svg new file mode 100644 index 0000000..31daf21 --- /dev/null +++ b/client/src/assets/img/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/img/inbox.svg b/client/src/assets/img/inbox.svg new file mode 100644 index 0000000..10263c2 --- /dev/null +++ b/client/src/assets/img/inbox.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/src/assets/img/restore.svg b/client/src/assets/img/restore.svg new file mode 100644 index 0000000..1cdea84 --- /dev/null +++ b/client/src/assets/img/restore.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/styles.scss b/client/src/styles.scss index 0254bb8..ebd7531 100644 --- a/client/src/styles.scss +++ b/client/src/styles.scss @@ -51,6 +51,13 @@ html, body { justify-content: center; } +.delete { + background-image: url("./assets/img/delete.svg"); +} + +.restore { + background-image: url("./assets/img/restore.svg"); +} /* Core Data Grid CSS */ @import "ag-grid-community/styles/ag-grid.css";