diff --git a/api/src/model/entitites/key.entity.ts b/api/src/model/entitites/key.entity.ts index cb9672d..404eed4 100644 --- a/api/src/model/entitites/key.entity.ts +++ b/api/src/model/entitites/key.entity.ts @@ -29,8 +29,11 @@ export class Key implements IKey { @Column({ name: 'handed_out', default: false }) handedOut: boolean; - @Column({ name: 'lost', default: false }) - keyLost: boolean; + @Column({ name: 'lost', default: null }) + keyLost: Date; + + @Column({ name: 'is_digital', default: false }) + digital: boolean; @ManyToMany(() => Cylinder, (cylinder) => cylinder.keys) @JoinTable() diff --git a/api/src/model/interface/key.interface.ts b/api/src/model/interface/key.interface.ts index dcf3f2f..3040d7d 100644 --- a/api/src/model/interface/key.interface.ts +++ b/api/src/model/interface/key.interface.ts @@ -8,4 +8,6 @@ export interface IKey { cylinder: Cylinder[]; customer: Customer; createdAt: Date; + digital: boolean; + keyLost: Date; } diff --git a/api/src/modules/cylinder/cylinder.service.ts b/api/src/modules/cylinder/cylinder.service.ts index 398f305..3ae2af8 100644 --- a/api/src/modules/cylinder/cylinder.service.ts +++ b/api/src/modules/cylinder/cylinder.service.ts @@ -1,7 +1,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { Cylinder, User } from 'src/model/entitites'; import { ActivityRepository, CylinderRepository, KeyRepository } from 'src/model/repositories'; -import { ManageHelperService } from 'src/shared/service/system.helper.service'; +import { HelperService } from 'src/shared/service/system.helper.service'; @Injectable() export class CylinderService { @@ -9,7 +9,7 @@ export class CylinderService { private readonly cylinderRepo: CylinderRepository, private readonly keyRepo: KeyRepository, private systemActivityRepo: ActivityRepository, - private readonly helper: ManageHelperService + private readonly helper: HelperService ) {} async getCylinders(user: User): Promise { diff --git a/api/src/modules/key/key.controller.ts b/api/src/modules/key/key.controller.ts index 6656e85..f1e04c5 100644 --- a/api/src/modules/key/key.controller.ts +++ b/api/src/modules/key/key.controller.ts @@ -24,6 +24,11 @@ export class KeyController { return this.service.getUsersKeys(req.user); } + @Get('lost') + getLostKeys(@Req() req: AuthenticatedRequest) { + return this.service.getLostKeys(req.user); + } + @Post() postKey(@Req() req: AuthenticatedRequest, @Body() key: Key) { return this.service.createKey(req.user, key); diff --git a/api/src/modules/key/key.service.ts b/api/src/modules/key/key.service.ts index ebe7aaf..14044a2 100644 --- a/api/src/modules/key/key.service.ts +++ b/api/src/modules/key/key.service.ts @@ -1,14 +1,13 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { Customer, Cylinder, Key, User } from 'src/model/entitites'; -import { IUser } from 'src/model/interface'; import { - ActivityRepository, CylinderRepository, KeyRepository, KeySystemRepository, } from 'src/model/repositories'; import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository'; import { ActivityHelperService } from 'src/shared/service/activity.logger.service'; +import { HelperService } from 'src/shared/service/system.helper.service'; import { IsNull, Not } from 'typeorm'; @Injectable() @@ -16,14 +15,25 @@ export class KeyService { constructor( private readonly keyrepository: KeyRepository, private readonly cylinderRepository: CylinderRepository, - private readonly systemRepo: KeySystemRepository, - private handoverRepo: KeyHandoutRepository, - private readonly activityService: ActivityHelperService + private readonly handoverRepo: KeyHandoutRepository, + private readonly activityService: ActivityHelperService, + private readonly helper: HelperService, ) {} async getUsersKeys(user: User): Promise { const keys = await this.keyrepository.find({ - where: { cylinder: { system: { managers: { id: user.id } } } }, + where: { cylinder: { system: { managers: { id: user.id } } }, keyLost: IsNull() }, + relations: ['cylinder', 'cylinder.system', 'customer'], + }); + for (let k of keys) { + k.customer = await this.getCustomerOfLastHandout(user, k.id); + } + return keys; + } + + async getLostKeys(user: User): Promise { + const keys = await this.keyrepository.find({ + where: { cylinder: { system: { managers: { id: user.id } } }, keyLost: Not(IsNull()) }, relations: ['cylinder', 'cylinder.system', 'customer'], }); for (let k of keys) { @@ -36,10 +46,19 @@ export class KeyService { if (!user || !user.id) { throw new HttpException('forbidden', HttpStatus.FORBIDDEN); } - await this.keyrepository.findOneOrFail({ - where: { cylinder: { system: { managers: { id: user.id } } } }, + + const k = await this.keyrepository.findOneOrFail({ + where: { id: key.id, cylinder: { system: { managers: { id: user.id } } } }, + relations: ['cylinder', 'cylinder.system'], + withDeleted: true }); + if (k.name != key.name) { + await this.activityService.logKeyRenamed(user, key, key.cylinder[0].system, k.name); + } + if (k.keyLost != key.keyLost) { + await this.activityService.logKeyLostUpdate(user, key, key.keyLost); + } return this.keyrepository.save(this.keyrepository.create(key)); } @@ -121,6 +140,7 @@ export class KeyService { const key = await this.keyrepository.findOneOrFail({ where: { id, cylinder: { system: { managers: { id: user.id } } } }, }); + await this.activityService.logDeleteKey(user, key); return this.keyrepository.softRemove(key); } @@ -136,11 +156,14 @@ export class KeyService { } 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; + await this.activityService.logKeyRestored(user, key); + await this.helper.deleteKeyArchiveCache(); return this.keyrepository.save(key); } } diff --git a/api/src/modules/user/user.service.ts b/api/src/modules/user/user.service.ts index d60859d..e104a36 100644 --- a/api/src/modules/user/user.service.ts +++ b/api/src/modules/user/user.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { User } from 'src/model/entitites'; import { IUser } from 'src/model/interface'; import { ActivityRepository, KeySystemRepository, RoleRepository, UserRepository } from 'src/model/repositories'; -import { ManageHelperService } from 'src/shared/service/system.helper.service'; +import { HelperService } from 'src/shared/service/system.helper.service'; @Injectable() export class UserService { constructor( @@ -10,7 +10,7 @@ export class UserService { private readonly roleRepo: RoleRepository, private readonly systemRepo: KeySystemRepository, private readonly systemActivityRepo: ActivityRepository, - private readonly helper: ManageHelperService, + private readonly helper: HelperService, ) {} getAllUsers(): Promise { diff --git a/api/src/shared/service/activity.logger.service.ts b/api/src/shared/service/activity.logger.service.ts index 2aa0c19..bb3eda4 100644 --- a/api/src/shared/service/activity.logger.service.ts +++ b/api/src/shared/service/activity.logger.service.ts @@ -2,11 +2,13 @@ import { Injectable } from "@nestjs/common"; import { Key, KeyHandout, User } from "src/model/entitites"; import { KeySystem } from "src/model/entitites/system.entity"; import { ActivityRepository, CylinderRepository, KeyRepository } from "src/model/repositories"; +import { HelperService } from "./system.helper.service"; @Injectable() export class ActivityHelperService { constructor( + private readonly helper: HelperService, private readonly activityRepo: ActivityRepository, private readonly keyRepository: KeyRepository, private readonly cylinderRepo: CylinderRepository, @@ -54,8 +56,8 @@ export class ActivityHelperService { })) } - async logKeyRenamed(user: User, key: Key, system: KeySystem) { - let msg = `Schlüssel ${key.nr} in ${key.name} umbenannt`; + async logKeyRenamed(user: User, key: Key, system: KeySystem, oldName?: string) { + let msg = `Schlüssel ${key.nr} ${oldName ? 'von ' + oldName + ' in ' : ''}in ${key.name} umbenannt`; this.activityRepo.save( this.activityRepo.create({ system, @@ -64,6 +66,31 @@ export class ActivityHelperService { })) } + /** + * Logs when a key is reported as lost or misplaced in the system. + * + * @param user - The user who reported the key as lost + * @param key - The key that was reported as lost + * @param system - The key system the lost key belongs to + * @param lostDate - The date when the key was reported as lost + */ + async logKeyLostUpdate(user: User, key: Key,lostDate: Date | string) { + const sys = await this.helper.getSystemOfKey(key); + let msg; + if (lostDate == null) { + msg = `Schlüssel ${key.nr} als gefunden gemeldet`; + } else { + msg = `Schlüssel ${key.nr} als verloren gemeldet` + } + this.activityRepo.save( + this.activityRepo.create({ + system: sys, + user, + message: msg, + }) + ) + } + logKeyHandover(user: User, key: Key, system: KeySystem, handover: KeyHandout) { const msg = `Schlüssel ${key.nr} ${handover.direction == 'out' ? 'ausgegeben an ' : 'zurückgegeben von '}${handover.customer.name}` @@ -75,4 +102,17 @@ export class ActivityHelperService { })) } + + async logKeyRestored(user: User, key: Key) { + let msg = `Schlüssel ${key.nr} wiederhergestellt`; + + const system: KeySystem = await this.helper.getSystemOfKey(key); + + this.activityRepo.save( + this.activityRepo.create({ + system, + user, + message: msg, + })) + } } \ No newline at end of file diff --git a/api/src/shared/service/shared.service.module.ts b/api/src/shared/service/shared.service.module.ts index dac53c3..b6875c7 100644 --- a/api/src/shared/service/shared.service.module.ts +++ b/api/src/shared/service/shared.service.module.ts @@ -1,14 +1,15 @@ import { Module } from "@nestjs/common"; import { DatabaseModule } from "../database/database.module"; -import { ManageHelperService } from "./system.helper.service"; +import { HelperService } from "./system.helper.service"; import { CylinderRepository, KeySystemRepository } from "src/model/repositories"; import { Cylinder, User } from "src/model/entitites"; import { ActivityHelperService } from "./activity.logger.service"; +import { CacheModule } from "@nestjs/cache-manager"; @Module({ imports: [ DatabaseModule ], - providers: [ ManageHelperService, ActivityHelperService ], - exports: [ ManageHelperService, ActivityHelperService ], + providers: [ HelperService, ActivityHelperService ], + exports: [ HelperService, ActivityHelperService ], }) export class SharedServiceModule { diff --git a/api/src/shared/service/system.helper.service.ts b/api/src/shared/service/system.helper.service.ts index b1b8785..02418ed 100644 --- a/api/src/shared/service/system.helper.service.ts +++ b/api/src/shared/service/system.helper.service.ts @@ -1,14 +1,17 @@ -import { Injectable } from "@nestjs/common"; +import { Inject, Injectable } from "@nestjs/common"; import { User, Cylinder, Key } from "src/model/entitites"; +import { KeySystem } from "src/model/entitites/system.entity"; import { KeySystemRepository, CylinderRepository, KeyRepository } from "src/model/repositories"; +import { Cache } from "@nestjs/cache-manager"; @Injectable() -export class ManageHelperService { +export class HelperService { constructor( private readonly systemRepository: KeySystemRepository, private readonly cylinderRepository: CylinderRepository, private readonly keyRepo: KeyRepository, + private cacheManager: Cache ) {} @@ -35,4 +38,24 @@ export class ManageHelperService { }); return keys; } + + async getSystemOfKey(key: Key): Promise { + const k = await this.keyRepo.findOne({ + where: { id: key.id }, + relations: ['cylinder', 'cylinder.system'], + withDeleted: true, + }); + this.cache() + const found = k.cylinder.find(c => c.system != null); + return found.system; + } + + async cache() { + const value = await this.cacheManager.store.keys() + console.log(value) + } + + async deleteKeyArchiveCache() { + await this.cacheManager.del('/key/archive'); + } } \ 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 b3a9a4c..bfa5780 100644 --- a/client/src/app/model/interface/key.interface.ts +++ b/client/src/app/model/interface/key.interface.ts @@ -9,4 +9,5 @@ export interface IKey { cylinder: ICylinder[]; nr: number; deletedAt?: string; + keyLost: Date | null; } \ No newline at end of file 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 0a76ad0..bf3ff14 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 @@ -25,6 +25,9 @@ - - + + \ No newline at end of file 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 07f8c5c..5eef64c 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 @@ -8,11 +8,12 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; @Component({ selector: 'app-create-cylinder', standalone: true, - imports: [CommonModule, MatFormFieldModule, MatInputModule, MatDialogModule, ReactiveFormsModule, FormsModule, MatSelectModule, MatButtonModule], + imports: [CommonModule, MatFormFieldModule, MatInputModule, MatDialogModule, ReactiveFormsModule, FormsModule, MatSelectModule, MatButtonModule, MatIconModule], templateUrl: './create-cylinder.component.html', styleUrl: './create-cylinder.component.scss' }) diff --git a/client/src/app/modules/dashboard/dashboard.component.ts b/client/src/app/modules/dashboard/dashboard.component.ts index 61f428d..202025a 100644 --- a/client/src/app/modules/dashboard/dashboard.component.ts +++ b/client/src/app/modules/dashboard/dashboard.component.ts @@ -31,7 +31,7 @@ export class DashboardComponent { cellRenderer: (data: any) => `${data.value?.firstName} ${data.value?.lastName}` } ], pagination: true, - paginationPageSize: 10, + paginationPageSize: 50, loading: true, localeText: AG_GRID_LOCALE_DE, loadingOverlayComponent: AgLoadingComponent diff --git a/client/src/app/modules/keys/components/archive/archive.component.ts b/client/src/app/modules/keys/components/archive/archive.component.ts index 250cf6d..3b21ed4 100644 --- a/client/src/app/modules/keys/components/archive/archive.component.ts +++ b/client/src/app/modules/keys/components/archive/archive.component.ts @@ -46,10 +46,12 @@ export class ArchiveComponent { width: 40, cellRenderer: () => '
', onCellClicked: (event) => { this.restoreKey(event.data);}, + tooltipValueGetter: () => 'Wiederherstellen', sortable: false } ]; this.gridOptions.rowHeight = 36; + this.gridOptions.overlayNoRowsTemplate = 'Bisher wurden keine Schlüssel gelöscht. Sobald dies der Fall ist, werden sie hier angezeigt.'; } onGridReady(params: GridReadyEvent) { diff --git a/client/src/app/modules/keys/components/lost-key/lost-key.component.html b/client/src/app/modules/keys/components/lost-key/lost-key.component.html new file mode 100644 index 0000000..0fe69da --- /dev/null +++ b/client/src/app/modules/keys/components/lost-key/lost-key.component.html @@ -0,0 +1,41 @@ +@if(key.keyLost != null) { +

Schlüssel als gefunden markieren

+} @else { +

Schlüssel als verloren markieren

+} + +
+ @if(key.keyLost != null) { + report +

+ {{key.name}} wirklich als gefunden markieren? +

+

+ Die Information, dass er am {{ key.keyLost| date:'shortDate' }} verloren wurde, wird gelöscht! +

+ } @else { + warning +

+ {{key.name}} wirklich als verloren markieren? +

+

+ Verlorene Schlüssel müssen gesperrt und neu angefertigt werden. +

+ } +
+
+ + + + @if(key.keyLost != null) { + + } @else { + + } + \ No newline at end of file diff --git a/client/src/app/modules/keys/components/lost-key/lost-key.component.scss b/client/src/app/modules/keys/components/lost-key/lost-key.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/client/src/app/modules/keys/components/lost-key/lost-key.component.spec.ts b/client/src/app/modules/keys/components/lost-key/lost-key.component.spec.ts new file mode 100644 index 0000000..c241bd2 --- /dev/null +++ b/client/src/app/modules/keys/components/lost-key/lost-key.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LostKeyComponent } from './lost-key.component'; + +describe('LostKeyComponent', () => { + let component: LostKeyComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LostKeyComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LostKeyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/modules/keys/components/lost-key/lost-key.component.ts b/client/src/app/modules/keys/components/lost-key/lost-key.component.ts new file mode 100644 index 0000000..ed88c79 --- /dev/null +++ b/client/src/app/modules/keys/components/lost-key/lost-key.component.ts @@ -0,0 +1,33 @@ +import { Component, inject, LOCALE_ID } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { IKey } from '../../../../model/interface/key.interface'; +import { CommonModule, DatePipe } from '@angular/common'; + +@Component({ + selector: 'app-lost-key', + standalone: true, + imports: [MatDialogModule, MatButtonModule, MatIconModule, CommonModule], + providers: [{ provide: LOCALE_ID, useValue: 'de-DE' }], + templateUrl: './lost-key.component.html', + styleUrl: './lost-key.component.scss' +}) +export class LostKeyComponent { + readonly dialogRef = inject(MatDialogRef); + readonly key = inject(MAT_DIALOG_DATA); + + closeWithData() { + this.dialogRef.close(new Date()); + } + + closeFound() { + this.dialogRef.close(""); + } + + get loss(): Date | null { + if (!this.key.keyLost) { return null } + + return new Date(this.key.keyLost); + } +} diff --git a/client/src/app/modules/keys/components/lost-keys/lost-keys.component.html b/client/src/app/modules/keys/components/lost-keys/lost-keys.component.html new file mode 100644 index 0000000..8d7ca97 --- /dev/null +++ b/client/src/app/modules/keys/components/lost-keys/lost-keys.component.html @@ -0,0 +1,11 @@ +

Verlorene Schlüssel

+ + + + + + \ No newline at end of file diff --git a/client/src/app/modules/keys/components/lost-keys/lost-keys.component.scss b/client/src/app/modules/keys/components/lost-keys/lost-keys.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/client/src/app/modules/keys/components/lost-keys/lost-keys.component.spec.ts b/client/src/app/modules/keys/components/lost-keys/lost-keys.component.spec.ts new file mode 100644 index 0000000..6cee472 --- /dev/null +++ b/client/src/app/modules/keys/components/lost-keys/lost-keys.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LostKeysComponent } from './lost-keys.component'; + +describe('LostKeysComponent', () => { + let component: LostKeysComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LostKeysComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LostKeysComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/modules/keys/components/lost-keys/lost-keys.component.ts b/client/src/app/modules/keys/components/lost-keys/lost-keys.component.ts new file mode 100644 index 0000000..4ca6cf5 --- /dev/null +++ b/client/src/app/modules/keys/components/lost-keys/lost-keys.component.ts @@ -0,0 +1,95 @@ +import { CommonModule, DatePipe } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { MatDialog, MatDialogModule } from '@angular/material/dialog'; +import { HotToastService } from '@ngxpert/hot-toast'; +import { GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community'; +import { ICylinder } from '../../../../model/interface/cylinder.interface'; +import { IKey } from '../../../../model/interface/key.interface'; +import { ApiService } from '../../../../shared/api.service'; +import { HELPER } from '../../../../shared/helper.service'; +import { AgGridAngular } from 'ag-grid-angular'; +import { LostKeyComponent } from '../lost-key/lost-key.component'; +import { MatButtonModule } from '@angular/material/button'; + +@Component({ + selector: 'app-lost-keys', + standalone: true, + imports: [MatDialogModule, AgGridAngular, CommonModule, MatButtonModule], + providers: [DatePipe], + templateUrl: './lost-keys.component.html', + styleUrl: './lost-keys.component.scss' +}) +export class LostKeysComponent { + private api: ApiService = inject(ApiService); + private datePipe = inject(DatePipe); + private dialog: MatDialog = inject(MatDialog); + private toast = inject(HotToastService); + + gridApi!: GridApi; + + dataChanged = false; + + gridOptions: GridOptions = HELPER.getGridOptions(); + + constructor() { + this.gridOptions.columnDefs = [ + { colId: 'name', field: 'name', headerName: 'Name', sort: 'asc', flex: 1, filter: true }, + { colId: 'nr', field: 'nr', headerName: 'Schlüsselnummer', flex: 1, filter: true }, + { colId: 'cylinder', field: 'cylinder', headerName: 'Zylinder', flex: 1, filter: true, + cellRenderer: (data: any) => data.value?.map((m: ICylinder) => m.name).join(', ') + }, + { + colId: 'customer', field: 'customer.name', headerName: 'Kunde', flex: 1, filter: true, + }, + { colId: 'keyLost', field: 'keyLost', headerName: 'Verloren seit', width: 100, + cellRenderer: (data: any) => this.datePipe.transform(new Date(data.value), 'dd.MM.yyyy'), + }, + { + colId: 'actions', + headerName: 'Aktionen', + width: 100, + tooltipValueGetter: () => 'Als gefunden markieren', + cellRenderer: (params: any) => `
`, + onCellClicked: (event: any) => { + if (event.colDef.colId === 'actions') { + this.markAsFound(event.data); + } + } + } + ]; + this.gridOptions.overlayNoRowsTemplate = 'Bisher wurden keine Schlüssel als verloren gemeldet. Sobald dies der Fall ist, werden sie hier angezeigt.'; + } + + onGridReady(params: GridReadyEvent) { + this.gridApi = params.api; + this.loadLostKeys(); + } + + loadLostKeys() { + this.gridApi?.setGridOption("loading", true); + this.api.getLostKeys().subscribe({ + next: keys => { + this.gridApi.setGridOption("rowData", keys); + this.gridApi.setGridOption("loading", false); + } + }); + } + + markAsFound(key: IKey) { + this.dialog.open(LostKeyComponent, { data: key, autoFocus: false }).afterClosed().subscribe({ + next: (result) => { + if (result == "") { + key.keyLost = null; + this.api.updateKey(key).subscribe({ + next: () => { + this.toast.success('Schlüssel als gefunden markiert'); + this.loadLostKeys(); + } + }); + this.dataChanged = true; + } + } + }) + + } +} diff --git a/client/src/app/modules/keys/create/create.component.html b/client/src/app/modules/keys/create/create.component.html index bac86b0..532d667 100644 --- a/client/src/app/modules/keys/create/create.component.html +++ b/client/src/app/modules/keys/create/create.component.html @@ -12,11 +12,17 @@ } - - Schlüsselnummer - - Nummer auf dem Schlüssel - +
+ + Schlüsselnummer + + Nummer auf dem Schlüssel + + +
+ Schlüsselkarte +
+
@@ -28,13 +34,16 @@ Wo sperrt der Schlüssel? -
- - + + \ 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 76b7a22..7f6e63c 100644 --- a/client/src/app/modules/keys/create/create.component.ts +++ b/client/src/app/modules/keys/create/create.component.ts @@ -11,11 +11,12 @@ import { MatSelectModule } from '@angular/material/select'; import { HotToastService } from '@ngxpert/hot-toast'; import { SelectKeyCylinderComponent } from './select-key-cylinder/select-key-cylinder.component'; import { MatIconModule } from '@angular/material/icon'; +import {MatCheckboxModule} from '@angular/material/checkbox'; @Component({ selector: 'app-create', standalone: true, - imports: [MatDialogModule, MatButtonModule, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatDialogModule, MatIconModule], + imports: [MatDialogModule, MatButtonModule, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatDialogModule, MatIconModule, MatCheckboxModule], templateUrl: './create.component.html', styleUrl: './create.component.scss' }) @@ -29,6 +30,7 @@ export class CreateKeyComponent { createForm = new FormGroup({ name: new FormControl(null, Validators.required), nr: new FormControl(null, Validators.required), + digital: new FormControl(false, Validators.required), cylinder: new FormControl(null, Validators.required), }) diff --git a/client/src/app/modules/keys/keys.component.html b/client/src/app/modules/keys/keys.component.html index b21620b..e01d744 100644 --- a/client/src/app/modules/keys/keys.component.html +++ b/client/src/app/modules/keys/keys.component.html @@ -6,5 +6,6 @@
- + +
diff --git a/client/src/app/modules/keys/keys.component.ts b/client/src/app/modules/keys/keys.component.ts index 89d09ca..bd588d3 100644 --- a/client/src/app/modules/keys/keys.component.ts +++ b/client/src/app/modules/keys/keys.component.ts @@ -15,11 +15,13 @@ import { ArchiveComponent } from './components/archive/archive.component'; import { AgLoadingComponent } from '../../shared/ag-grid/components/ag-loading/ag-loading.component'; import { map, of } from 'rxjs'; import { ICylinder } from '../../model/interface/cylinder.interface'; +import { LostKeysComponent } from './components/lost-keys/lost-keys.component'; +import { MatTooltipModule } from '@angular/material/tooltip'; @Component({ selector: 'app-keys', standalone: true, - imports: [AgGridAngular, MatButtonModule, MatDialogModule, MatIconModule], + imports: [AgGridAngular, MatButtonModule, MatDialogModule, MatIconModule, MatTooltipModule], providers: [DatePipe], templateUrl: './keys.component.html', styleUrl: './keys.component.scss' @@ -37,6 +39,9 @@ export class KeysComponent { gridOptions: GridOptions = { localeText: AG_GRID_LOCALE_DE, rowData: [], + rowClassRules: { + 'key-lost': (params) => {console.log(params.data); return params.data.keyLost != null}, + }, columnDefs: [ { colId: 'name', field: 'name' , headerName: 'Name', flex: 1, editable: true, sort: 'asc', filter: true }, @@ -178,4 +183,22 @@ export class KeysComponent { } }) } + + openLostKeys() { + this.dialog.open(LostKeysComponent, { + maxHeight: "calc(100vh - 24px)", + maxWidth: "calc(100vw - 24px)", + width: "50vw", + minWidth: "min(700px,calc(100vw - 24px))", + height: "70vh", + disableClose: true + }).afterClosed().subscribe({ + next: changed => { + console.log(changed) + if (changed) { + this.loadKeys(); + } + } + }) + } } diff --git a/client/src/app/modules/system/create/create.component.html b/client/src/app/modules/system/create/create.component.html index bb979b4..266e5b7 100644 --- a/client/src/app/modules/system/create/create.component.html +++ b/client/src/app/modules/system/create/create.component.html @@ -14,6 +14,9 @@ - - + + \ No newline at end of file diff --git a/client/src/app/modules/system/create/create.component.ts b/client/src/app/modules/system/create/create.component.ts index 196501d..3105cae 100644 --- a/client/src/app/modules/system/create/create.component.ts +++ b/client/src/app/modules/system/create/create.component.ts @@ -6,11 +6,12 @@ import { FormGroup, FormControl, Validators, FormsModule, ReactiveFormsModule } import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { MatIconModule } from '@angular/material/icon'; @Component({ selector: 'app-create', standalone: true, - imports: [MatDialogModule, MatButtonModule, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatInputModule, MatDialogModule], + imports: [MatDialogModule, MatButtonModule, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatInputModule, MatDialogModule, MatIconModule], templateUrl: './create.component.html', styleUrl: './create.component.scss' }) 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 index caa34ff..d9219fc 100644 --- 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 @@ -1,3 +1,5 @@ -
+
-
\ No newline at end of file +
+ +
\ 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 index f2521a8..f5ad8a3 100644 --- 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 @@ -5,7 +5,7 @@ justify-content: center; width: 100%; height: 100%; - gap: 12px; + gap: 6px; } .handover { 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 index c34502b..33f9f90 100644 --- 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 @@ -8,6 +8,7 @@ import { DeleteKeyComponent } from '../../../../modules/keys/components/delete-k import { HotToastService } from '@ngxpert/hot-toast'; import { HandoverDialogComponent } from '../../../../modules/keys/components/handover-dialog/handover-dialog.component'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { LostKeyComponent } from '../../../../modules/keys/components/lost-key/lost-key.component'; @Component({ selector: 'app-ag-delete-key', @@ -86,4 +87,24 @@ export class AgDeleteKeyComponent implements ICellRendererAngularComp { } }) } + + openLostKeyDialog() { + this.dialog.open(LostKeyComponent, { + data: this.key, + autoFocus: false, + maxWidth: '100vw', + maxHeight: '100vh' + }).afterClosed().subscribe({ + next: n => { + if (n != null) { + if (n == "") { + n = null; + } + this.key.keyLost = n; + this.params.api.refreshCells(); + this.api.updateKey(this.key).subscribe(); + } + } + }) + } } diff --git a/client/src/app/shared/api.service.ts b/client/src/app/shared/api.service.ts index b76e2d4..9326c26 100644 --- a/client/src/app/shared/api.service.ts +++ b/client/src/app/shared/api.service.ts @@ -30,6 +30,10 @@ export class ApiService { return this.http.get('api/key') } + getLostKeys(): Observable { + return this.http.get('api/key/lost') + } + updateKey(key: IKey): Observable { return this.http.put('api/key', key); } diff --git a/client/src/assets/img/lost.svg b/client/src/assets/img/lost.svg new file mode 100644 index 0000000..a5c573d --- /dev/null +++ b/client/src/assets/img/lost.svg @@ -0,0 +1,62 @@ + + + + diff --git a/client/src/assets/img/magnifying-glass.svg b/client/src/assets/img/magnifying-glass.svg new file mode 100644 index 0000000..83c91db --- /dev/null +++ b/client/src/assets/img/magnifying-glass.svg @@ -0,0 +1,52 @@ + + + + diff --git a/client/src/styles.scss b/client/src/styles.scss index 3877e25..a0e24b3 100644 --- a/client/src/styles.scss +++ b/client/src/styles.scss @@ -66,6 +66,8 @@ html, body { width: 28px; height: 28px; background-size: 20px; + background-position: center; + background-repeat: no-repeat; } .loading-spinner { @@ -90,6 +92,14 @@ html, body { background-image: url("./assets/img/restore.svg"); } +.magnifying-glass { + background-image: url("./assets/img/magnifying-glass.svg"); +} + +.lost { + background-image: url("./assets/img/lost.svg"); +} + .ag-row { transition: background-color 0.2s ease-in-out; } @@ -131,4 +141,11 @@ div.ag-row { margin-top: 1rem; color: rgba(0, 0, 0, 0.6); } +} + +.key-lost{ + span, div { + + text-decoration: line-through !important; + } } \ No newline at end of file