diff --git a/api/src/model/entitites/cylinder.entity.ts b/api/src/model/entitites/cylinder.entity.ts index 2b8792e..69679d9 100644 --- a/api/src/model/entitites/cylinder.entity.ts +++ b/api/src/model/entitites/cylinder.entity.ts @@ -6,7 +6,6 @@ import { Entity, ManyToMany, ManyToOne, - OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @@ -18,9 +17,12 @@ export class Cylinder { @PrimaryGeneratedColumn('uuid') id: string; - @Column({ nullable: false, unique: true }) + @Column({ nullable: false }) name: string; + @Column({ name:'description', type: 'text', nullable: true }) + description: string; + @ManyToMany(() => Key, (key) => key.cylinder, { onDelete: 'NO ACTION'}) keys: Key[]; diff --git a/api/src/modules/cylinder/cylinder.service.ts b/api/src/modules/cylinder/cylinder.service.ts index 1e67748..c8caf05 100644 --- a/api/src/modules/cylinder/cylinder.service.ts +++ b/api/src/modules/cylinder/cylinder.service.ts @@ -60,7 +60,8 @@ export class CylinderService { } async createCylinder(user: User, cylinder: Partial) { - const c = await this.cylinderRepo.save(this.cylinderRepo.create(cylinder)); + try { + const c = await this.cylinderRepo.save(this.cylinderRepo.create(cylinder)); this.systemActivityRepo.save({ message: `Zylinder ${(c as any).name} angelegt`, @@ -68,6 +69,10 @@ export class CylinderService { system: (c as any).system }); return c + } catch (e) { + // this.log.log() + throw new HttpException('Zylinder konnte nicht angelegt werden', HttpStatus.BAD_REQUEST) + } } getDeletedCylinders(user: User) { diff --git a/api/src/modules/system/system.controller.ts b/api/src/modules/system/system.controller.ts index d61b723..8798906 100644 --- a/api/src/modules/system/system.controller.ts +++ b/api/src/modules/system/system.controller.ts @@ -8,6 +8,7 @@ import { Delete, Req, UseGuards, + Put, } from '@nestjs/common'; import { SystemService } from './system.service'; import { CreateSystemDto } from './dto/create-system.dto'; @@ -47,7 +48,7 @@ export class SystemController { return this.systemService.findOne(id); } - @Patch(':id') + @Put(':id') update(@Param('id') id: string, @Body() updateSystemDto: UpdateSystemDto) { return this.systemService.update(id, updateSystemDto); } diff --git a/client/src/app/model/interface/cylinder.interface.ts b/client/src/app/model/interface/cylinder.interface.ts index 845f69f..565bce9 100644 --- a/client/src/app/model/interface/cylinder.interface.ts +++ b/client/src/app/model/interface/cylinder.interface.ts @@ -3,6 +3,7 @@ import { IKey } from "./key.interface"; export interface ICylinder { id: string; name: string; + description: string; createdAt: string; updatedAt: string; deletedAt: string; diff --git a/client/src/app/modules/cylinder/components/create-cylinder/create-cylinder.component.html b/client/src/app/modules/cylinder/components/create-cylinder/create-cylinder.component.html index e033519..6f30dc8 100644 --- a/client/src/app/modules/cylinder/components/create-cylinder/create-cylinder.component.html +++ b/client/src/app/modules/cylinder/components/create-cylinder/create-cylinder.component.html @@ -12,6 +12,12 @@ } + + Beschreibung + + Zylinderlänge und co. + + Schließanlage diff --git a/client/src/app/modules/cylinder/components/create-cylinder/create-cylinder.component.ts b/client/src/app/modules/cylinder/components/create-cylinder/create-cylinder.component.ts index 4f3ec03..189a32c 100644 --- a/client/src/app/modules/cylinder/components/create-cylinder/create-cylinder.component.ts +++ b/client/src/app/modules/cylinder/components/create-cylinder/create-cylinder.component.ts @@ -25,11 +25,12 @@ export class CreateCylinderComponent { createForm = new FormGroup({ name: new FormControl(null, Validators.required), - system: new FormControl(null, Validators.required) + system: new FormControl(null, Validators.required), + description: new FormControl(null) }); ngOnInit() { - this.api.getSystems().subscribe({ + this.api.systems.asObservable().subscribe({ next: systems => { this.systems = systems; } diff --git a/client/src/app/modules/cylinder/cylinder.component.ts b/client/src/app/modules/cylinder/cylinder.component.ts index 2981d60..e4c1d50 100644 --- a/client/src/app/modules/cylinder/cylinder.component.ts +++ b/client/src/app/modules/cylinder/cylinder.component.ts @@ -1,6 +1,6 @@ import { Component, inject } from '@angular/core'; import { HELPER } from '../../shared/helper.service'; -import { GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community'; +import { CellEditingStoppedEvent, GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community'; import { AgGridAngular } from 'ag-grid-angular'; import { ApiService } from '../../shared/api.service'; import { DatePipe } from '@angular/common'; @@ -11,6 +11,7 @@ import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; import { AgGridContainerComponent } from '../../shared/ag-grid/components/ag-grid-container/ag-grid-container.component'; import { CylinderArchiveComponent } from './components/cylinder-archive/cylinder-archive.component'; +import { ICylinder } from '../../model/interface/cylinder.interface'; @Component({ selector: 'app-cylinder', @@ -33,7 +34,8 @@ export class CylinderComponent extends AgGridContainerComponent { super(); this.gridOptions.columnDefs = [ - { field: 'name', headerName: 'Name', sort: 'asc', flex: 1, filter: true }, + { field: 'name', headerName: 'Name', sort: 'asc', flex: 1, filter: true, editable: true }, + { field: 'description', headerName: 'Beschreibung', flex: 1, filter: true, editable: true }, { field: 'system.name', headerName: 'System', flex: 1, filter: true }, { 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)) : '-' }, @@ -54,6 +56,7 @@ export class CylinderComponent extends AgGridContainerComponent { onGridReady(params: GridReadyEvent) { this.gridApi = params.api; + this.gridApi.addEventListener("cellEditingStopped", evt => this.cellEditEnd(evt)); this.loadCylinders(); this.api.cylinders.asObservable().subscribe({ next: (data) => { @@ -63,6 +66,15 @@ export class CylinderComponent extends AgGridContainerComponent { }) } + private async cellEditEnd(event: CellEditingStoppedEvent) { + const cylinder: ICylinder = event.data; + + if (!event.valueChanged || event.newValue == event.oldValue) { return; } + + await this.api.updateCylinder(cylinder) + + } + openCreateCylinder() { this.dialog.open(CreateCylinderComponent, { maxWidth: "calc(100vw - 24px)", diff --git a/client/src/app/modules/keys/components/handover-dialog/handover-dialog.component.html b/client/src/app/modules/keys/components/handover-dialog/handover-dialog.component.html index d5765d4..ddb2db7 100644 --- a/client/src/app/modules/keys/components/handover-dialog/handover-dialog.component.html +++ b/client/src/app/modules/keys/components/handover-dialog/handover-dialog.component.html @@ -58,6 +58,7 @@ style="width: 100%; height: 100%;" (gridReady)="onGridReady($event)" [gridOptions]="gridOptions!" + [theme]="myTheme" /> 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 4ce5416..af1e642 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 @@ -25,6 +25,7 @@ import { GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community'; import { AG_GRID_LOCALE_DE } from '@ag-grid-community/locale'; import { AgGridAngular } from 'ag-grid-angular'; import { MatIconModule } from '@angular/material/icon'; +import { AgGridContainerComponent } from '../../../../shared/ag-grid/components/ag-grid-container/ag-grid-container.component'; @Component({ selector: 'app-handover-dialog', @@ -38,7 +39,7 @@ import { MatIconModule } from '@angular/material/icon'; templateUrl: './handover-dialog.component.html', styleUrl: './handover-dialog.component.scss' }) -export class HandoverDialogComponent { +export class HandoverDialogComponent extends AgGridContainerComponent { private api: ApiService = inject(ApiService); readonly dialogRef = inject(MatDialogRef); diff --git a/client/src/app/modules/keys/create/select-key-cylinder/select-key-cylinder.component.html b/client/src/app/modules/keys/create/select-key-cylinder/select-key-cylinder.component.html index 7234b04..e3bf2e8 100644 --- a/client/src/app/modules/keys/create/select-key-cylinder/select-key-cylinder.component.html +++ b/client/src/app/modules/keys/create/select-key-cylinder/select-key-cylinder.component.html @@ -6,6 +6,7 @@ style="width: 100%; height: 100%;" (gridReady)="onGridReady($event)" [gridOptions]="gridOptions!" + [theme]="myTheme" /> diff --git a/client/src/app/modules/keys/create/select-key-cylinder/select-key-cylinder.component.ts b/client/src/app/modules/keys/create/select-key-cylinder/select-key-cylinder.component.ts index 59c7ada..95bf00d 100644 --- a/client/src/app/modules/keys/create/select-key-cylinder/select-key-cylinder.component.ts +++ b/client/src/app/modules/keys/create/select-key-cylinder/select-key-cylinder.component.ts @@ -7,6 +7,7 @@ import { GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community'; import { AG_GRID_LOCALE_DE } from '@ag-grid-community/locale'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button'; +import { AgGridContainerComponent } from '../../../../shared/ag-grid/components/ag-grid-container/ag-grid-container.component'; @Component({ selector: 'app-select-key-cylinder', @@ -14,7 +15,7 @@ import { MatButtonModule } from '@angular/material/button'; templateUrl: './select-key-cylinder.component.html', styleUrl: './select-key-cylinder.component.scss' }) -export class SelectKeyCylinderComponent { +export class SelectKeyCylinderComponent extends AgGridContainerComponent { private toast: HotToastService = inject(HotToastService); readonly dialogRef = inject(MatDialogRef); readonly cylinders = inject(MAT_DIALOG_DATA); diff --git a/client/src/app/modules/keys/keys.component.ts b/client/src/app/modules/keys/keys.component.ts index 390af43..d09b26a 100644 --- a/client/src/app/modules/keys/keys.component.ts +++ b/client/src/app/modules/keys/keys.component.ts @@ -149,7 +149,6 @@ export class KeysComponent extends AgGridContainerComponent { this.gridApi.addEventListener("cellEditingStopped", evt => this.cellEditEnd(evt)); this.api.keys.asObservable().subscribe({ next: keys => { - console.log(keys) this.gridApi.setGridOption("rowData", keys); this.gridApi.setGridOption("loading", false); } diff --git a/client/src/app/modules/system/system.component.ts b/client/src/app/modules/system/system.component.ts index 869f2fd..f7ff89d 100644 --- a/client/src/app/modules/system/system.component.ts +++ b/client/src/app/modules/system/system.component.ts @@ -30,7 +30,7 @@ export class SystemComponent extends AgGridContainerComponent { super(); this.gridOptions.columnDefs = [ { colId: 'name', field: 'name', headerName: 'Name', sort: 'asc', flex: 1}, - { colId: 'cylinderCount', field: 'cylinders', headerName: 'Zylinderanzahl', flex: 0, cellRenderer: (data: any) => data.value.length}, + { colId: 'cylinderCount', field: 'cylinders', headerName: 'Zylinderanzahl', flex: 0, cellRenderer: (data: any) => data.value?.length || 0}, { 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)) : '-' }, { @@ -43,17 +43,20 @@ export class SystemComponent extends AgGridContainerComponent { ]; } - loadSystems() { - this.api.getSystems().subscribe({ - next: n => { - this.gridApi.setGridOption("rowData", n); - this.gridApi.setGridOption("loading", false); - } - }) + async loadSystems() { + this.gridApi.setGridOption("loading", true); + await this.api.refreshSystems(); + this.gridApi.setGridOption("loading", false); } onGridReady(params: GridReadyEvent) { this.gridApi = params.api; + this.api.systems.asObservable().subscribe({ + next: systems => { + this.gridApi.setGridOption("rowData", systems); + this.gridApi.setGridOption("loading", false); + } + }) this.loadSystems(); } diff --git a/client/src/app/shared/api.service.ts b/client/src/app/shared/api.service.ts index bdaa8e7..f5c35f4 100644 --- a/client/src/app/shared/api.service.ts +++ b/client/src/app/shared/api.service.ts @@ -15,6 +15,7 @@ export class ApiService { public keys: BehaviorSubject = new BehaviorSubject([]); public cylinders: BehaviorSubject = new BehaviorSubject([]); + public systems: BehaviorSubject = new BehaviorSubject([]); public user: BehaviorSubject = new BehaviorSubject(null!); @@ -73,6 +74,24 @@ export class ApiService { return this.http.put('api/key', key); } + updateCylinder(cylinder: ICylinder): Promise { + return new Promise(resolve => { + this.http.put('api/cylinder', cylinder).pipe( + this.toast.observe({ + loading: `Zylinder ${cylinder} wird gespeichert...`, + success: `Zylinder ${cylinder.name} gespeichert.`, + error: 'Es ist ein Fehler aufgetreten' + }) + ).subscribe({ + next: () => resolve(true), + error: () => resolve(false), + complete: () => { + this.refreshCylinders() + } + }) + }) + } + createKey(key: any) { return this.http.post('api/key', key); } @@ -81,7 +100,7 @@ export class ApiService { return this.http.post('api/system', keySystem); } - getSystems(): Observable { + private getSystems(): Observable { return this.http.get('api/system'); } @@ -139,6 +158,24 @@ export class ApiService { }) } + deleteSystem(system: any): Promise { + return new Promise(resolve => { + this.http.delete(`api/system${system.id}`).pipe( + this.toast.observe({ + loading: `Lösche Schließsystem ${system.name}...`, + success: `Schließsystem ${system.name} wurde gelöscht.`, + error: 'Es ist ein Fehler aufgetreten' + }) + ).subscribe({ + next: () => resolve(true), + error: () => resolve(false), + complete: () => { + this.refreshSystems(); + } + }) + }) + } + getKeyArchive(): Observable { return this.http.get('api/key/archive'); } @@ -184,6 +221,21 @@ export class ApiService { }) } + refreshSystems(): Promise { + return new Promise(resolve => { + this.getSystems().subscribe({ + next: data => { + this.systems.next(data); + resolve() + }, + error: () => { + this.toast.error('Fehler beim Laden der Schließsysteme') + }, + complete: () => resolve() + }) + }) + } + deleteCylinder(cylinder: ICylinder): Promise { return new Promise(resolve => { this.http.delete(`api/cylinder/${cylinder.id}`).pipe( diff --git a/client/src/styles.scss b/client/src/styles.scss index 2413c32..ff9709c 100644 --- a/client/src/styles.scss +++ b/client/src/styles.scss @@ -216,6 +216,6 @@ div.ag-row { } .ag-filter-body-wrapper { - padding: 8px; + padding: 8px 12px; gap: 6px; } \ No newline at end of file