This commit is contained in:
Bastian Wagner
2024-10-23 13:58:14 +02:00
parent d4ddcafd2b
commit b453945183
33 changed files with 570 additions and 19 deletions

View File

@@ -1,6 +1,7 @@
import { import {
Column, Column,
CreateDateColumn, CreateDateColumn,
DeleteDateColumn,
Entity, Entity,
ManyToOne, ManyToOne,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
@@ -18,7 +19,7 @@ export class Key implements IKey {
@Column({ nullable: true }) @Column({ nullable: true })
name: string; name: string;
@Column({ name: 'key_number', unique: true }) @Column({ name: 'key_number', unique: false })
nr: number; nr: number;
@Column({ name: 'handed_out', default: false }) @Column({ name: 'handed_out', default: false })
@@ -35,4 +36,7 @@ export class Key implements IKey {
@UpdateDateColumn({ name: 'updatet_at' }) @UpdateDateColumn({ name: 'updatet_at' })
updatedAt: Date; updatedAt: Date;
@DeleteDateColumn()
deletedAt: Date;
} }

View File

@@ -1,7 +1,10 @@
import { import {
Body, Body,
Controller, Controller,
Delete,
Get, Get,
HttpException,
HttpStatus,
Param, Param,
Post, Post,
Put, Put,
@@ -13,7 +16,6 @@ import { AuthenticatedRequest } from 'src/model/interface/authenticated-request.
import { AuthGuard } from 'src/core/guards/auth.guard'; import { AuthGuard } from 'src/core/guards/auth.guard';
import { Key } from 'src/model/entitites'; import { Key } from 'src/model/entitites';
import { CreateKeySystemDto } from 'src/model/dto/create-key-system.dto'; import { CreateKeySystemDto } from 'src/model/dto/create-key-system.dto';
import { HandoverKeyDto } from 'src/model/dto';
@UseGuards(AuthGuard) @UseGuards(AuthGuard)
@Controller('key') @Controller('key')
@@ -25,11 +27,26 @@ export class KeyController {
return this.service.getUsersKeys(req.user); return this.service.getUsersKeys(req.user);
} }
@Post()
postKey(@Req() req: AuthenticatedRequest, @Body() key: Key) {
return this.service.createKey(req.user, key);
}
@Put() @Put()
updateKey(@Req() req: AuthenticatedRequest, @Body() key: Key) { updateKey(@Req() req: AuthenticatedRequest, @Body() key: Key) {
return this.service.updateKey(req.user, 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') @Get('cylinder')
getCylinders(@Req() req: AuthenticatedRequest) { getCylinders(@Req() req: AuthenticatedRequest) {
return this.service.getUsersCylinders(req.user); return this.service.getUsersCylinders(req.user);
@@ -56,4 +73,9 @@ export class KeyController {
getKeyHandouts(@Req() req: AuthenticatedRequest, @Param('id') id: string) { getKeyHandouts(@Req() req: AuthenticatedRequest, @Param('id') id: string) {
return this.service.getKeyHandovers(req.user, id); return this.service.getKeyHandovers(req.user, id);
} }
@Get('Archive')
getArchive(@Req() req: AuthenticatedRequest) {
return this.service.getDeletedKeys(req.user);
}
} }

View File

@@ -9,6 +9,7 @@ import {
KeySystemRepository, KeySystemRepository,
} from 'src/model/repositories'; } from 'src/model/repositories';
import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository'; import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository';
import { IsNull, Not } from 'typeorm';
@Injectable() @Injectable()
export class KeyService { export class KeyService {
@@ -108,4 +109,35 @@ export class KeyService {
relations: ['customer'], 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);
}
} }

View File

@@ -6,4 +6,5 @@ export interface IKey {
handedOut: boolean; handedOut: boolean;
cylinder: any; cylinder: any;
nr: number; nr: number;
deletedAt?: string;
} }

View File

@@ -0,0 +1,12 @@
<h2 mat-dialog-title>Gelöschte Schlüssel</h2>
<mat-dialog-content>
<ag-grid-angular
style="width: 100%; height: 100%;"
(gridReady)="onGridReady($event)"
[gridOptions]="gridOptions!"
/>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button [mat-dialog-close]="dataChanged">Schließen</button>
</mat-dialog-actions>

View File

@@ -0,0 +1,3 @@
mat-dialog-content{
}

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ArchiveComponent } from './archive.component';
describe('ArchiveComponent', () => {
let component: ArchiveComponent;
let fixture: ComponentFixture<ArchiveComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ArchiveComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ArchiveComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -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: () => '<div class="icon-btn-sm restore" ></div>',
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);
}
});
}
}

View File

@@ -0,0 +1,9 @@
<h2 mat-dialog-title>Schlüssel {{key.name}} löschen?</h2>
<mat-dialog-content>
<p>Soll der Schlüssel wirklich gelöscht werden?</p>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button [mat-dialog-close]="false" >Abbrechen</button>
<button mat-button [mat-dialog-close]="true" color="warn">Ja, löschen</button>
</mat-dialog-actions>

View File

@@ -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<DeleteKeyComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DeleteKeyComponent]
})
.compileComponents();
fixture = TestBed.createComponent(DeleteKeyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -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<DeleteKeyComponent>);
readonly key = inject<IKey>(MAT_DIALOG_DATA);
}

View File

@@ -51,6 +51,7 @@ export class HandoverDialogComponent {
gridOptions: GridOptions = { gridOptions: GridOptions = {
localeText: AG_GRID_LOCALE_DE, localeText: AG_GRID_LOCALE_DE,
rowData: [], rowData: [],
isRowSelectable: () => false,
columnDefs: [ columnDefs: [
{ colId: 'customer', field: 'customer.name' , headerName: 'Kunde', flex: 1, editable: false, filter: false}, { colId: 'customer', field: 'customer.name' , headerName: 'Kunde', flex: 1, editable: false, filter: false},
{ {

View File

@@ -1,15 +1,21 @@
<h2 mat-dialog-title>Schlüssel anlegen</h2> <h2 mat-dialog-title>Neuen Schlüssel anlegen</h2>
<mat-dialog-content> <mat-dialog-content>
<form [formGroup]="createForm" > <form [formGroup]="createForm" >
<mat-form-field> <mat-form-field>
<mat-label>Name</mat-label> <mat-label>Name</mat-label>
<input type="text" matInput formControlName="name"> <input type="text" matInput formControlName="name" maxlength="100">
@if ((createForm.controls.name.value || '').length > 20) {
<mat-hint>{{ (createForm.controls.name.value || '').length }} / 100 Zeichen</mat-hint>
} @else {
<mat-hint>Wie soll der Schlüssel heißen?</mat-hint>
}
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>Schlüsselnummer</mat-label> <mat-label>Schlüsselnummer</mat-label>
<input type="text" matInput formControlName="nr"> <input type="number" matInput formControlName="nr" min="0" max="999999999999">
<mat-hint>Nummer auf dem Schlüssel</mat-hint>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
@@ -19,6 +25,7 @@
<mat-option [value]="item">{{ item.name }}</mat-option> <mat-option [value]="item">{{ item.name }}</mat-option>
} }
</mat-select> </mat-select>
<mat-hint>Wo sperrt der Schlüssel?</mat-hint>
</mat-form-field> </mat-form-field>
</form> </form>
</mat-dialog-content> </mat-dialog-content>

View File

@@ -1,4 +1,5 @@
form { form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px;
} }

View File

@@ -1,24 +1,27 @@
import { Component, inject } from '@angular/core'; import { Component, inject } from '@angular/core';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; 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 { ApiService } from '../../../shared/api.service';
import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { map, Observable, startWith } from 'rxjs'; import { map, Observable, startWith } from 'rxjs';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { HotToastService } from '@ngxpert/hot-toast';
@Component({ @Component({
selector: 'app-create', selector: 'app-create',
standalone: true, standalone: true,
imports: [MatDialogModule, MatButtonModule, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatInputModule, MatSelectModule], imports: [MatDialogModule, MatButtonModule, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatDialogModule],
templateUrl: './create.component.html', templateUrl: './create.component.html',
styleUrl: './create.component.scss' styleUrl: './create.component.scss'
}) })
export class CreateKeyComponent { export class CreateKeyComponent {
private api: ApiService = inject(ApiService); private api: ApiService = inject(ApiService);
private toast: HotToastService = inject(HotToastService);
readonly dialogRef = inject(MatDialogRef<CreateKeyComponent>);
createForm = new FormGroup({ createForm = new FormGroup({
name: new FormControl(null, Validators.required), name: new FormControl(null, Validators.required),
@@ -54,7 +57,19 @@ export class CreateKeyComponent {
} }
save() { save() {
console.log(this.createForm.value)
this.api.createKey(this.createForm.value as any) 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);
}
})
} }
} }

View File

@@ -1,9 +1,10 @@
@if (gridOptions || true) { <ag-grid-angular
<ag-grid-angular
style="width: 100%; height: 100%;" style="width: 100%; height: 100%;"
(gridReady)="onGridReady($event)" (gridReady)="onGridReady($event)"
[gridOptions]="gridOptions!" [gridOptions]="gridOptions!"
/> />
}
<button mat-flat-button class="btn-create" (click)="openCreateKey()" >Schlüssel anlegen</button> <div class="floating-btn-container">
<button mat-raised-button class="btn-create" (click)="openCreateKey()" >Schlüssel anlegen</button>
<button mat-mini-fab (click)="openArchive()"><mat-icon>inventory_2</mat-icon></button>
</div>

View File

@@ -1,5 +1,7 @@
.btn-create { .floating-btn-container {
position: absolute; position: absolute;
bottom: 12px; bottom: 12px;
right: 12px; right: 12px;
display: flex;
gap: 12px;
} }

View File

@@ -10,11 +10,15 @@ import { MatButtonModule } from '@angular/material/button';
import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { CreateKeyComponent } from './create/create.component'; import { CreateKeyComponent } from './create/create.component';
import { AgOpenHandoutComponent } from '../../shared/ag-grid/components/ag-open-handout/ag-open-handout.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({ @Component({
selector: 'app-keys', selector: 'app-keys',
standalone: true, standalone: true,
imports: [AgGridAngular, MatButtonModule, MatDialogModule], imports: [AgGridAngular, MatButtonModule, MatDialogModule, MatIconModule],
providers: [DatePipe], providers: [DatePipe],
templateUrl: './keys.component.html', templateUrl: './keys.component.html',
styleUrl: './keys.component.scss' styleUrl: './keys.component.scss'
@@ -70,9 +74,24 @@ export class KeysComponent {
, cellRenderer: (data: any) => data.value ? this.datePipe.transform(new Date(data.value)) : '-' , cellRenderer: (data: any) => data.value ? this.datePipe.transform(new Date(data.value)) : '-'
, tooltipValueGetter: (data: any) => this.datePipe.transform(new Date(data.value), 'medium') , 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, loading: true,
rowHeight: 36, rowHeight: 36,
loadingOverlayComponent: AgLoadingComponent
}
deleteKey(id: string) {
this.api.deleteKey(id).subscribe({
next: n => console.log(n)
})
} }
ngOnInit(): void { ngOnInit(): void {
@@ -118,6 +137,31 @@ export class KeysComponent {
} }
openCreateKey() { 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();
}
}
})
} }
} }

View File

@@ -0,0 +1 @@
<div class="delete icon-btn-sm" (click)="delete()" ></div>

View File

@@ -0,0 +1,8 @@
:host {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}

View File

@@ -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<AgDeleteKeyComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AgDeleteKeyComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AgDeleteKeyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -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<any, any, any>;
private api: ApiService = inject(ApiService);
private dialog: MatDialog = inject(MatDialog);
private toast = inject(HotToastService);
agInit(params: ICellRendererParams<any, any, any>): void {
this.params = params;
this.key = params.data;
}
refresh(params: ICellRendererParams<any, any, any>): 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);
}
})
}
}

View File

@@ -0,0 +1 @@
<mat-spinner></mat-spinner>

View File

@@ -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<AgLoadingComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AgLoadingComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AgLoadingComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -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<any, any>): void {
}
refresh?(params: ILoadingOverlayParams<any, any>): void {
}
}

View File

@@ -38,6 +38,7 @@ export class ApiService {
} }
createKey(key: any) { createKey(key: any) {
return this.http.post<IKey>('api/key', key);
} }
postKeySystem(keySystem: any) { postKeySystem(keySystem: any) {
@@ -60,4 +61,15 @@ export class ApiService {
return this.http.get<any[]>('api/customer') return this.http.get<any[]>('api/customer')
} }
deleteKey(id: string) {
return this.http.delete(`api/key/${id}`);
}
getKeyArchive(): Observable<IKey[]> {
return this.http.get<IKey[]>('api/key/archive');
}
restoreKey(id: string) {
return this.http.put(`api/key/${id}/restore`, null);
}
} }

View File

@@ -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
}
}
}

View File

@@ -0,0 +1 @@
<svg height="427pt" viewBox="-40 0 427 427.00131" width="427pt" xmlns="http://www.w3.org/2000/svg"><path d="m232.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0"/><path d="m114.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0"/><path d="m28.398438 127.121094v246.378906c0 14.5625 5.339843 28.238281 14.667968 38.050781 9.285156 9.839844 22.207032 15.425781 35.730469 15.449219h189.203125c13.527344-.023438 26.449219-5.609375 35.730469-15.449219 9.328125-9.8125 14.667969-23.488281 14.667969-38.050781v-246.378906c18.542968-4.921875 30.558593-22.835938 28.078124-41.863282-2.484374-19.023437-18.691406-33.253906-37.878906-33.257812h-51.199218v-12.5c.058593-10.511719-4.097657-20.605469-11.539063-28.03125-7.441406-7.421875-17.550781-11.5546875-28.0625-11.46875h-88.796875c-10.511719-.0859375-20.621094 4.046875-28.0625 11.46875-7.441406 7.425781-11.597656 17.519531-11.539062 28.03125v12.5h-51.199219c-19.1875.003906-35.394531 14.234375-37.878907 33.257812-2.480468 19.027344 9.535157 36.941407 28.078126 41.863282zm239.601562 279.878906h-189.203125c-17.097656 0-30.398437-14.6875-30.398437-33.5v-245.5h250v245.5c0 18.8125-13.300782 33.5-30.398438 33.5zm-158.601562-367.5c-.066407-5.207031 1.980468-10.21875 5.675781-13.894531 3.691406-3.675781 8.714843-5.695313 13.925781-5.605469h88.796875c5.210937-.089844 10.234375 1.929688 13.925781 5.605469 3.695313 3.671875 5.742188 8.6875 5.675782 13.894531v12.5h-128zm-71.199219 32.5h270.398437c9.941406 0 18 8.058594 18 18s-8.058594 18-18 18h-270.398437c-9.941407 0-18-8.058594-18-18s8.058593-18 18-18zm0 0"/><path d="m173.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<g>
<path d="M312.461,332.734H199.539c-8.511,0-15.434,6.923-15.434,15.434v34.634c0,8.511,6.923,15.435,15.434,15.435h112.923
c8.511,0,15.435-6.923,15.435-15.435v-34.634C327.895,339.658,320.972,332.734,312.461,332.734z M308.051,378.393H203.948v-25.814
h104.103V378.393z"/>
</g>
</g>
<g>
<g>
<path d="M506.976,246.958l0.159-0.08L432.73,99.774c-6.015-11.89-18.025-19.275-31.346-19.275h-14.141V66.824
c0-5.48-4.442-9.922-9.922-9.922H134.68c-5.48,0-9.922,4.442-9.922,9.922v13.675h-14.141c-13.321,0-25.331,7.385-31.346,19.275
L4.865,246.878l0.159,0.08C1.837,252.207,0,258.363,0,264.939v155.409c0,19.162,15.59,34.751,34.752,34.751h442.497
c19.162,0,34.751-15.59,34.751-34.751V264.939C512,258.363,510.163,252.207,506.976,246.958z M387.242,102.548h14.141
c4.959,0,9.43,2.751,11.671,7.179l60.93,120.462h-41.431v-37.066c0-5.48-4.442-9.922-9.922-9.922h-12.275v-53.227
c0-5.48-4.442-9.922-9.922-9.922h-13.192V102.548z M412.71,203.044v27.144h-52.359c-8.984,0-17.174,5.293-20.865,13.482
l-14.296,31.71c-0.136,0.299-0.435,0.493-0.764,0.493H187.575c-0.329,0-0.628-0.194-0.764-0.494l-14.295-31.708
c-3.692-8.19-11.882-13.483-20.866-13.483H99.291v-27.144H412.71z M144.602,76.746h222.796v43.305H144.602V76.746z
M390.512,139.895v43.305H121.488v-43.305H390.512z M98.946,109.727c2.24-4.429,6.712-7.179,11.671-7.179h14.141v17.503h-13.192
c-5.48,0-9.922,4.442-9.922,9.922v53.227H89.369c-5.48,0-9.922,4.442-9.922,9.922v37.066H38.016L98.946,109.727z M477.249,433.049
H34.752c-7.004,0-12.703-5.699-12.703-12.701V264.939c0-7.003,5.698-12.701,12.703-12.701H151.65c0.328,0,0.629,0.194,0.765,0.495
l14.295,31.708c3.692,8.19,11.881,13.481,20.865,13.481h136.85c8.984,0,17.174-5.292,20.865-13.48l14.296-31.709v-0.001
c0.136-0.3,0.435-0.494,0.764-0.494h116.898c7.004,0,12.701,5.699,12.701,12.701v155.409h0.001
C489.951,427.352,484.253,433.049,477.249,433.049z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M440-320h80v-166l64 62 56-56-160-160-160 160 56 56 64-62v166ZM280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520Zm-400 0v520-520Z"/></svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@@ -51,6 +51,13 @@ html, body {
justify-content: center; justify-content: center;
} }
.delete {
background-image: url("./assets/img/delete.svg");
}
.restore {
background-image: url("./assets/img/restore.svg");
}
/* Core Data Grid CSS */ /* Core Data Grid CSS */
@import "ag-grid-community/styles/ag-grid.css"; @import "ag-grid-community/styles/ag-grid.css";