Handout management
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
@if (isLoading) {
|
||||
<div class="loading-spinner">
|
||||
<mat-spinner></mat-spinner>
|
||||
</div>
|
||||
}
|
||||
|
||||
<h2 mat-dialog-title>Übergaben {{ data.name }}</h2>
|
||||
<mat-dialog-content>
|
||||
|
||||
<h6>Historie:</h6>
|
||||
<table class="handouts">
|
||||
<tr>
|
||||
<th>Kunde</th>
|
||||
<th >Datum</th>
|
||||
<th>
|
||||
Übergabe
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
@if (handovers.length == 0) {
|
||||
<tr>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
}
|
||||
@for (item of handovers; track $index) {
|
||||
<tr>
|
||||
<td>{{ item.customer.name }}</td>
|
||||
<td>{{ item.timestamp | date}}</td>
|
||||
<td>{{ item.direction == 'out' ? 'Ausgabe' : 'Rückgabe'}}</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
|
||||
</table>
|
||||
|
||||
<form [formGroup]="handoverForm" class="flex-column" style="margin-top: 48px;">
|
||||
<h6>Neue Übergabe anlegen:</h6>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Kunde</mat-label>
|
||||
<input type="text"
|
||||
matInput
|
||||
formControlName="customer"
|
||||
[matAutocomplete]="auto">
|
||||
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
|
||||
@for (option of filteredCustomers | async; track option) {
|
||||
<mat-option [value]="option.name">{{option.name}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
<mat-hint>Wähle den Empfänger oder tippe einen neuen Namen ein</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<div style="margin: 24px 0;">
|
||||
Der Schlüssel wurde
|
||||
<mat-radio-group formControlName="direction" class="flex-column" style="align-items: flex-start; justify-content: flex-start;">
|
||||
<mat-radio-button [value]="'out'">Ausgegeben</mat-radio-button>
|
||||
<mat-radio-button [value]="'return'">Zurückgegeben</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Datum der Übergabe</mat-label>
|
||||
<input matInput [matDatepicker]="picker" formControlName="timestamp">
|
||||
<mat-hint>TT/MM/JJJJ</mat-hint>
|
||||
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
|
||||
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close color="warn" >Schließen</button>
|
||||
<button mat-button (click)="save()" [disabled]="handoverForm.invalid">Speichern</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,24 @@
|
||||
:host {
|
||||
width: min(calc(100vw - 24px), 700px);
|
||||
max-height: calc(100vh - 24px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
form {
|
||||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
:host {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.handouts{
|
||||
//margin-top: 32px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align: start;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HandoverDialogComponent } from './handover-dialog.component';
|
||||
|
||||
describe('HandoverDialogComponent', () => {
|
||||
let component: HandoverDialogComponent;
|
||||
let fixture: ComponentFixture<HandoverDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [HandoverDialogComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(HandoverDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,193 @@
|
||||
import { Component, inject, LOCALE_ID } from '@angular/core';
|
||||
import { ApiService } from '../../../../shared/api.service';
|
||||
import { IKey } from '../../../../model/interface/key.interface';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MAT_DATE_LOCALE, provideNativeDateAdapter } from '@angular/material/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { map, Observable, startWith, timestamp } from 'rxjs';
|
||||
import {
|
||||
MatBottomSheet,
|
||||
MatBottomSheetModule,
|
||||
MatBottomSheetRef,
|
||||
} from '@angular/material/bottom-sheet';
|
||||
import {MatListModule} from '@angular/material/list';
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
import {MatRadioModule} from '@angular/material/radio';
|
||||
import { HotToastService } from '@ngxpert/hot-toast';
|
||||
|
||||
@Component({
|
||||
selector: 'app-handover-dialog',
|
||||
standalone: true,
|
||||
imports: [FormsModule, ReactiveFormsModule, MatDatepickerModule, MatFormFieldModule, MatInputModule, MatButtonModule, MatDialogModule, CommonModule, MatAutocompleteModule, MatProgressSpinnerModule, MatRadioModule],
|
||||
providers: [
|
||||
provideNativeDateAdapter(),
|
||||
{ provide: LOCALE_ID, useValue: 'de-DE' },
|
||||
{ provide: MAT_DATE_LOCALE, useValue: 'de-DE' },
|
||||
],
|
||||
templateUrl: './handover-dialog.component.html',
|
||||
styleUrl: './handover-dialog.component.scss'
|
||||
})
|
||||
export class HandoverDialogComponent {
|
||||
|
||||
private api: ApiService = inject(ApiService);
|
||||
readonly dialogRef = inject(MatDialogRef<HandoverDialogComponent>);
|
||||
readonly data = inject<IKey>(MAT_DIALOG_DATA);
|
||||
private _bottomSheet = inject(MatBottomSheet);
|
||||
private toast: HotToastService = inject(HotToastService);
|
||||
|
||||
isLoading: boolean = false;
|
||||
|
||||
customers: { name: string, id: string }[] = [];
|
||||
filteredCustomers: Observable<any[]> = new Observable();
|
||||
|
||||
handovers: any[] = [];
|
||||
|
||||
|
||||
handoverForm = new FormGroup({
|
||||
customer: new FormControl<any>(null, Validators.required),
|
||||
key: new FormControl(this.data),
|
||||
direction: new FormControl('out', Validators.required),
|
||||
timestamp: new FormControl(new Date(), Validators.required)
|
||||
});
|
||||
|
||||
ngOnInit() {
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
loadData() {
|
||||
this.isLoading = true;
|
||||
const promises: Observable<any>[] = [
|
||||
this.getHandovers(),
|
||||
this.loadCustomers()
|
||||
];
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
this.isLoading = false;
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
getHandovers() {
|
||||
const promise = this.api.getHandovers(this.data.id)
|
||||
|
||||
promise.subscribe({
|
||||
next: n => {
|
||||
this.handovers = n;
|
||||
if (n && n.length > 0) {
|
||||
this.handoverForm.controls.customer.patchValue(n[0].customer.name);
|
||||
this.handoverForm.controls.direction.patchValue(n[0].direction == 'out' ? 'return' : 'out')
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
loadCustomers() {
|
||||
const promise = this.api.getCustomers()
|
||||
|
||||
promise.subscribe({
|
||||
next: customers => {
|
||||
this.customers = customers;
|
||||
this.filteredCustomers = this.handoverForm.controls.customer.valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => this._filter(value || '')),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return promise;
|
||||
|
||||
}
|
||||
|
||||
private _filter(value: string): any[] {
|
||||
const filterValue = value.toLowerCase();
|
||||
|
||||
return this.customers.filter(option => option.name.toLowerCase().includes(filterValue) || option.id.toLowerCase().includes(filterValue));
|
||||
}
|
||||
|
||||
save() {
|
||||
const val = this.handoverForm.value;
|
||||
const dto = {
|
||||
key: this.data,
|
||||
customer: this.customers.find(c => c.name == val.customer || c.id == val.customer),
|
||||
timestamp: val.timestamp,
|
||||
direction: val.direction
|
||||
}
|
||||
|
||||
if (dto.customer == null) {
|
||||
this._bottomSheet.open(BottomSheetCreateCustomer).afterDismissed().subscribe({
|
||||
next: async n => {
|
||||
if (!n) { return; }
|
||||
await this.createCustomer(val.customer);
|
||||
this.saveIt(dto);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.saveIt(dto);
|
||||
}
|
||||
}
|
||||
|
||||
saveIt(data: any) {
|
||||
this.api.handoverKey(data)
|
||||
.pipe(
|
||||
this.toast.observe({
|
||||
loading: 'Speichern...',
|
||||
error: 'Konnte nicht gespeichert werden. Bitte versuche es später erneut',
|
||||
success: 'Gespeichert'
|
||||
})
|
||||
)
|
||||
.subscribe({
|
||||
next: n => {
|
||||
this.dialogRef.close(data.direction == 'out')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
createCustomer(name: string) {
|
||||
this.isLoading = true;
|
||||
this.api.createCustomer({ name, system: this.data.cylinder.system}).subscribe({
|
||||
next: n => {
|
||||
this.isLoading = false;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-nav-list>
|
||||
<a mat-list-item (click)="openLink($event, true)">
|
||||
<span matListItemTitle>Anlegen</span>
|
||||
<span matLine>Neuen Empfänger anlegen</span>
|
||||
</a>
|
||||
|
||||
<a mat-list-item (click)="openLink($event)">
|
||||
<span matListItemTitle>Abbrechen</span>
|
||||
<span matLine>Zurück zur Auswahl</span>
|
||||
</a>
|
||||
|
||||
</mat-nav-list>
|
||||
`,
|
||||
standalone: true,
|
||||
imports: [MatInputModule, MatListModule],
|
||||
})
|
||||
export class BottomSheetCreateCustomer {
|
||||
private _bottomSheetRef =
|
||||
inject<MatBottomSheetRef<BottomSheetCreateCustomer>>(MatBottomSheetRef);
|
||||
|
||||
openLink(event: MouseEvent, data?: boolean): void {
|
||||
this._bottomSheetRef.dismiss(data);
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { HotToastService } from '@ngxpert/hot-toast';
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'app-keys',
|
||||
@@ -32,7 +33,12 @@ export class KeysComponent {
|
||||
localeText: AG_GRID_LOCALE_DE,
|
||||
rowData: [],
|
||||
columnDefs: [
|
||||
{ field: 'handedOut' , headerName: 'Ausgegeben', width: 100,editable: true, filter: true, headerTooltip: 'Ausgegeben' },
|
||||
{
|
||||
cellRenderer: AgOpenHandoutComponent,
|
||||
width: 100,
|
||||
headerName: 'Übergabe'
|
||||
},
|
||||
{ field: 'handedOut' , headerName: 'Ausgegeben', width: 100, editable: false, filter: false, headerTooltip: 'Ausgegeben' },
|
||||
{ field: 'name' , headerName: 'Name', flex: 1, editable: true, sort: 'asc', filter: true },
|
||||
{ field: 'nr' , headerName: 'Schlüsselnummer', flex: 1, editable: true, filter: true },
|
||||
{ field: 'cylinder' , headerName: 'Zylinder', flex: 1, editable: true, filter: true, cellRenderer: (data: any) => {return data.value?.name}, cellEditor: 'agSelectCellEditor',
|
||||
@@ -60,9 +66,10 @@ export class KeysComponent {
|
||||
, type: 'date'
|
||||
, cellRenderer: (data: any) => data.value ? this.datePipe.transform(new Date(data.value)) : '-'
|
||||
, tooltipValueGetter: (data: any) => this.datePipe.transform(new Date(data.value), 'medium')
|
||||
},
|
||||
}
|
||||
],
|
||||
loading: true,
|
||||
rowHeight: 48,
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -71,7 +78,6 @@ export class KeysComponent {
|
||||
this.cylinders = n;
|
||||
}
|
||||
})
|
||||
this.api.postKeySystem({ name: 'Development' }).subscribe()
|
||||
}
|
||||
|
||||
loadKeys() {
|
||||
@@ -79,7 +85,6 @@ export class KeysComponent {
|
||||
this.api.getKeys().subscribe(res => {
|
||||
this.gridApi.setGridOption("rowData", res);
|
||||
this.gridApi.setGridOption("loading", false);
|
||||
res.map((r: any) => console.log(r.updatedAt))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -91,8 +96,6 @@ export class KeysComponent {
|
||||
|
||||
cellEditEnd(event: CellEditingStoppedEvent) {
|
||||
const key: IKey = event.data;
|
||||
console.log(event)
|
||||
|
||||
if (!event.valueChanged || event.newValue == event.oldValue) { return; }
|
||||
|
||||
this.gridApi.setGridOption("loading", true);
|
||||
|
||||
Reference in New Issue
Block a user