(web): use angulars built-in control flow everywhere
This commit is contained in:
parent
08e3b3247c
commit
3302dd7e32
|
|
@ -1,5 +1,5 @@
|
|||
import { ClipboardModule } from '@angular/cdk/clipboard';
|
||||
import { CurrencyPipe, NgClass, NgIf } from '@angular/common';
|
||||
import { CurrencyPipe, NgClass } from '@angular/common';
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
|
|
@ -23,32 +23,33 @@ import {
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'dks-accounting-item',
|
||||
template: ` <div class="flex justify-between">
|
||||
<div class="flex items-center">
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="notifyCopy($event)"
|
||||
[cdkCopyToClipboard]="tour.operationId"
|
||||
>
|
||||
<mat-icon class="text-sm">content_copy</mat-icon>
|
||||
</button>
|
||||
{{ tour.operationId }}
|
||||
<div class="flex items-center">
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="notifyCopy($event)"
|
||||
[cdkCopyToClipboard]="tour.operationId"
|
||||
>
|
||||
<mat-icon class="text-sm">content_copy</mat-icon>
|
||||
</button>
|
||||
{{ tour.operationId }}
|
||||
</div>
|
||||
<span>{{ tour.ordinanceType }}</span>
|
||||
</div>
|
||||
<span>{{ tour.ordinanceType }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
[title]="!tour.healthInsurance ? 'Keine Krankenkasse hinterlegt' : ''"
|
||||
[ngClass]="{ 'bg-yellow-200': !tour.healthInsurance }"
|
||||
*ngIf="accountingState !== AccountingState.Billed; else diffTmpl"
|
||||
>
|
||||
{{ tour.revenue | currency : 'EUR' : 'symbol' : '1.2-2' : 'de-DE' }}
|
||||
</span>
|
||||
</div>
|
||||
<ng-template #diffTmpl>
|
||||
{{
|
||||
tour.revenueDeviation | currency : 'EUR' : 'symbol' : '1.2-2' : 'de-DE'
|
||||
}}
|
||||
</ng-template>`,
|
||||
<div>
|
||||
@if (accountingState !== AccountingState.Billed) {
|
||||
<span
|
||||
[title]="!tour.healthInsurance ? 'Keine Krankenkasse hinterlegt' : ''"
|
||||
[ngClass]="{ 'bg-yellow-200': !tour.healthInsurance }"
|
||||
>
|
||||
{{ tour.revenue | currency : 'EUR' : 'symbol' : '1.2-2' : 'de-DE' }}
|
||||
</span>
|
||||
} @else {
|
||||
{{
|
||||
tour.revenueDeviation | currency : 'EUR' : 'symbol' : '1.2-2' : 'de-DE'
|
||||
}}
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
host: {
|
||||
class: 'flex flex-col rounded border p-2 m-1 bg-white cursor-pointer',
|
||||
},
|
||||
|
|
@ -69,10 +70,9 @@ import {
|
|||
MatIconModule,
|
||||
MatBadgeModule,
|
||||
ClipboardModule,
|
||||
NgIf,
|
||||
NgClass,
|
||||
CurrencyPipe,
|
||||
],
|
||||
CurrencyPipe
|
||||
],
|
||||
})
|
||||
export class AccountingItemComponent implements AfterViewInit {
|
||||
toast = inject(HotToastService);
|
||||
|
|
|
|||
|
|
@ -1,24 +1,28 @@
|
|||
import { AsyncPipe, CurrencyPipe, NgIf } from '@angular/common';
|
||||
import { AsyncPipe, CurrencyPipe } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { TourStateMeta } from '../../../core/data-access/graphql/generated/generated';
|
||||
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'dks-accounting-lane-footer',
|
||||
template: `<span *ngIf="withTourCount" class="text-left text-xs">
|
||||
{{ tourStateMeta.count }} Touren
|
||||
</span>
|
||||
<span *ngIf="withRevenueSum" class="text-left text-xs">
|
||||
{{
|
||||
tourStateMeta.revenueSum
|
||||
| currency : 'EUR' : 'symbol' : '1.2-2' : 'de-DE'
|
||||
}}
|
||||
</span>`,
|
||||
template: `@if (withTourCount) {
|
||||
<span class="text-left text-xs">
|
||||
{{ tourStateMeta.count }} Touren
|
||||
</span>
|
||||
}
|
||||
@if (withRevenueSum) {
|
||||
<span class="text-left text-xs">
|
||||
{{
|
||||
tourStateMeta.revenueSum
|
||||
| currency : 'EUR' : 'symbol' : '1.2-2' : 'de-DE'
|
||||
}}
|
||||
</span>
|
||||
}`,
|
||||
host: {
|
||||
class: 'flex flex-col items-center justify-center p-2',
|
||||
},
|
||||
styles: [``],
|
||||
imports: [NgIf, CurrencyPipe],
|
||||
imports: [CurrencyPipe],
|
||||
})
|
||||
export class AccountingLaneFooterComponent {
|
||||
@Input() tourStateMeta!: TourStateMeta;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { NgFor, NgSwitch, NgSwitchCase } from '@angular/common';
|
||||
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
|
|
@ -23,68 +23,70 @@ import {
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'dks-accounting-lane-header-filter',
|
||||
template: `
|
||||
<ng-container [ngSwitch]="state">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="beforeBilledMenu"
|
||||
aria-label="Abrechenbar Filter button"
|
||||
*ngSwitchCase="AccountingState.Billable"
|
||||
@switch (state) {
|
||||
@case (AccountingState.Billable) {
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="beforeBilledMenu"
|
||||
aria-label="Abrechenbar Filter button"
|
||||
>
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="beforeBilledMenu"
|
||||
aria-label="Abrechnung vorbereitet Filter button"
|
||||
*ngSwitchCase="AccountingState.PreparedForBilling"
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
}
|
||||
@case (AccountingState.PreparedForBilling) {
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="beforeBilledMenu"
|
||||
aria-label="Abrechnung vorbereitet Filter button"
|
||||
>
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="afterBilledMenu"
|
||||
aria-label="Abgerechnet Filter button"
|
||||
*ngSwitchCase="AccountingState.Billed"
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
}
|
||||
@case (AccountingState.Billed) {
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="afterBilledMenu"
|
||||
aria-label="Abgerechnet Filter button"
|
||||
>
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
</ng-container>
|
||||
<form #form="ngForm">
|
||||
<mat-menu #beforeBilledMenu="matMenu">
|
||||
<section
|
||||
mat-menu-item
|
||||
(click)="$event.stopPropagation()"
|
||||
*ngFor="let item of filter.beforeBilled"
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
<form #form="ngForm">
|
||||
<mat-menu #beforeBilledMenu="matMenu">
|
||||
@for (item of filter.beforeBilled; track item) {
|
||||
<section
|
||||
mat-menu-item
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
<mat-checkbox [(ngModel)]="item.checked" [name]="item.value">{{
|
||||
item.text
|
||||
}}</mat-checkbox>
|
||||
</section>
|
||||
</mat-menu>
|
||||
<mat-menu #afterBilledMenu="matMenu">
|
||||
<section
|
||||
mat-menu-item
|
||||
(click)="$event.stopPropagation()"
|
||||
*ngFor="let item of filter.afterBilled"
|
||||
<mat-checkbox [(ngModel)]="item.checked" [name]="item.value">{{
|
||||
item.text
|
||||
}}</mat-checkbox>
|
||||
</section>
|
||||
}
|
||||
</mat-menu>
|
||||
<mat-menu #afterBilledMenu="matMenu">
|
||||
@for (item of filter.afterBilled; track item) {
|
||||
<section
|
||||
mat-menu-item
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
<mat-checkbox [(ngModel)]="item.checked" [name]="item.value">{{
|
||||
item.text
|
||||
}}</mat-checkbox>
|
||||
</section>
|
||||
</mat-menu>
|
||||
</form>
|
||||
`,
|
||||
<mat-checkbox [(ngModel)]="item.checked" [name]="item.value">{{
|
||||
item.text
|
||||
}}</mat-checkbox>
|
||||
</section>
|
||||
}
|
||||
</mat-menu>
|
||||
</form>
|
||||
`,
|
||||
styles: [],
|
||||
imports: [
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgFor,
|
||||
FormsModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
]
|
||||
FormsModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule
|
||||
]
|
||||
})
|
||||
export class AccountingLaneHeaderFilterComponent implements AfterViewInit {
|
||||
@Input() state!: AccountingState;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { NgSwitch, NgSwitchCase } from '@angular/common';
|
||||
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
|
|
@ -16,19 +16,25 @@ import { AccountingLaneHeaderFilterComponent } from './accounting-lane-header-fi
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'dks-accounting-lane-header',
|
||||
template: `<div></div>
|
||||
<ng-container [ngSwitch]="state">
|
||||
<div *ngSwitchCase="AccountingState.Billable">Abrechenbar</div>
|
||||
<div *ngSwitchCase="AccountingState.PreparedForBilling">
|
||||
für Abrechnung vorbereitet
|
||||
</div>
|
||||
<div *ngSwitchCase="AccountingState.Billed">
|
||||
Abgerechnet (mit Abweichung)
|
||||
</div>
|
||||
</ng-container>
|
||||
<dks-accounting-lane-header-filter
|
||||
[state]="state"
|
||||
(filterChange)="filterChanged($event)"
|
||||
></dks-accounting-lane-header-filter>`,
|
||||
@switch (state) {
|
||||
@case (AccountingState.Billable) {
|
||||
<div>Abrechenbar</div>
|
||||
}
|
||||
@case (AccountingState.PreparedForBilling) {
|
||||
<div>
|
||||
für Abrechnung vorbereitet
|
||||
</div>
|
||||
}
|
||||
@case (AccountingState.Billed) {
|
||||
<div>
|
||||
Abgerechnet (mit Abweichung)
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<dks-accounting-lane-header-filter
|
||||
[state]="state"
|
||||
(filterChange)="filterChanged($event)"
|
||||
></dks-accounting-lane-header-filter>`,
|
||||
host: {
|
||||
class: 'flex items-center justify-between p-2 bg-secondary-container',
|
||||
},
|
||||
|
|
@ -39,7 +45,7 @@ import { AccountingLaneHeaderFilterComponent } from './accounting-lane-header-fi
|
|||
}
|
||||
`,
|
||||
],
|
||||
imports: [NgSwitch, NgSwitchCase, AccountingLaneHeaderFilterComponent],
|
||||
imports: [AccountingLaneHeaderFilterComponent],
|
||||
})
|
||||
export class AccountingLaneHeaderComponent {
|
||||
@Input() state!: AccountingState;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
[state]="accountingState"
|
||||
(filterChange)="filterChanged($event)"
|
||||
></dks-accounting-lane-header>
|
||||
<ng-container *ngIf="tours$ | async as tours">
|
||||
@if (tours$ | async; as tours) {
|
||||
<cdk-virtual-scroll-viewport
|
||||
itemSize="86"
|
||||
(scrolledIndexChange)="fetchNextPage()"
|
||||
>
|
||||
>
|
||||
<dks-accounting-item
|
||||
*cdkVirtualFor="let tour of tours; trackBy: trackByFn"
|
||||
class="animate-fadeIn"
|
||||
|
|
@ -15,14 +15,14 @@
|
|||
[accountingState]="accountingState"
|
||||
[tour]="tour"
|
||||
(click)="openTourView(tour)"
|
||||
>
|
||||
>
|
||||
</dks-accounting-item>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<!-- <ng-container *ngIf="accountingStateMeta$ | async as accountingStateMeta">
|
||||
<dks-accounting-lane-footer
|
||||
[tourStateMeta]="accountingStateMeta"
|
||||
[withTourCount]="true"
|
||||
[withRevenueSum]="accountingState !== AccountingState.Billed"
|
||||
></dks-accounting-lane-footer>
|
||||
</ng-container> -->
|
||||
</ng-container>
|
||||
<dks-accounting-lane-footer
|
||||
[tourStateMeta]="accountingStateMeta"
|
||||
[withTourCount]="true"
|
||||
[withRevenueSum]="accountingState !== AccountingState.Billed"
|
||||
></dks-accounting-lane-footer>
|
||||
</ng-container> -->
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import {
|
|||
CdkVirtualScrollViewport,
|
||||
ScrollingModule,
|
||||
} from '@angular/cdk/scrolling';
|
||||
import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
|
|
@ -53,10 +53,9 @@ import { AccountingLaneHeaderComponent } from './accounting-lane-header.componen
|
|||
imports: [
|
||||
AccountingLaneHeaderComponent,
|
||||
AccountingItemComponent,
|
||||
NgIf,
|
||||
AsyncPipe,
|
||||
ScrollingModule,
|
||||
],
|
||||
ScrollingModule
|
||||
],
|
||||
})
|
||||
export class AccountingLaneComponent {
|
||||
private dialog = inject(DialogService);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { DatePipe, NgIf } from '@angular/common';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
|
@ -18,37 +18,46 @@ import { Tour } from '../../../core/data-access/graphql/generated/generated';
|
|||
</span>
|
||||
</div>
|
||||
<div class="flex flex-col text-gray-600 text-sm">
|
||||
<span class="font-medium" *ngIf="tour.createdAt">Erstellt am</span>
|
||||
<span *ngIf="tour.createdAt">
|
||||
{{ tour.createdAt | date : 'dd.MM.yyyy' }}
|
||||
{{ tour.createdAt | date : 'HH:mm' }} Uhr
|
||||
</span>
|
||||
@if (tour.createdAt) {
|
||||
<span class="font-medium">Erstellt am</span>
|
||||
}
|
||||
@if (tour.createdAt) {
|
||||
<span>
|
||||
{{ tour.createdAt | date : 'dd.MM.yyyy' }}
|
||||
{{ tour.createdAt | date : 'HH:mm' }} Uhr
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div
|
||||
class="flex flex-wrap md:flex-nowrap justify-between px-4 py-2"
|
||||
*ngIf="tour.driverName || tour.codriverName || tour.carName"
|
||||
>
|
||||
@if (tour.driverName || tour.codriverName || tour.carName) {
|
||||
<div
|
||||
class="flex flex-col pb-1 md:pb-0"
|
||||
*ngIf="tour.carName"
|
||||
>
|
||||
<span class="font-semibold pb-1">Fahrzeug</span>
|
||||
{{ tour.carName }}
|
||||
class="flex flex-wrap md:flex-nowrap justify-between px-4 py-2"
|
||||
>
|
||||
@if (tour.carName) {
|
||||
<div
|
||||
class="flex flex-col pb-1 md:pb-0"
|
||||
>
|
||||
<span class="font-semibold pb-1">Fahrzeug</span>
|
||||
{{ tour.carName }}
|
||||
</div>
|
||||
}
|
||||
@if (tour.driverName) {
|
||||
<div
|
||||
class="flex flex-col pb-1 md:pb-0"
|
||||
>
|
||||
<span class="font-semibold pb-1">Fahrer</span>
|
||||
<span>{{ tour.driverName }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (tour.codriverName) {
|
||||
<div class="flex flex-col">
|
||||
<span class="font-semibold pb-1">Beifahrer</span>
|
||||
<span>{{ tour.codriverName }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col pb-1 md:pb-0"
|
||||
*ngIf="tour.driverName"
|
||||
>
|
||||
<span class="font-semibold pb-1">Fahrer</span>
|
||||
<span>{{ tour.driverName }}</span>
|
||||
</div>
|
||||
<div class="flex flex-col" *ngIf="tour.codriverName">
|
||||
<span class="font-semibold pb-1">Beifahrer</span>
|
||||
<span>{{ tour.codriverName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="flex flex-col px-4 py-2">
|
||||
<span class="font-semibold pb-1">Patient</span>
|
||||
<span>{{ tour.patientName }} {{ tour.patientSurname }}</span>
|
||||
|
|
@ -69,9 +78,9 @@ import { Tour } from '../../../core/data-access/graphql/generated/generated';
|
|||
<span>{{ tour.targetZip }} {{ tour.targetCity }}</span>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
`,
|
||||
styles: [],
|
||||
imports: [MatTabsModule, MatIconModule, MatDividerModule, NgIf, DatePipe]
|
||||
imports: [MatTabsModule, MatIconModule, MatDividerModule, DatePipe]
|
||||
})
|
||||
export class AccountingTourOverviewComponent {
|
||||
@Input() tour!: Tour;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { DatePipe, NgFor, NgIf, NgSwitch, NgSwitchCase } from '@angular/common';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
|
@ -14,33 +14,38 @@ import { Ticket, TicketValidationState, Tour } from '../../../core/data-access/g
|
|||
<div mat-subheader class="text-sm">Ticket Informationen</div>
|
||||
<mat-list-item role="listitem">
|
||||
Status:
|
||||
<ng-container [ngSwitch]="tour.ticket.currentState">
|
||||
<span
|
||||
class="font-bold"
|
||||
*ngSwitchCase="TicketValidationState.Created"
|
||||
>Touren angenommen
|
||||
</span>
|
||||
<span
|
||||
class="font-bold"
|
||||
*ngSwitchCase="TicketValidationState.UserInteractionRequired"
|
||||
>Genehmigung / Transportschein
|
||||
</span>
|
||||
<span
|
||||
class="font-bold"
|
||||
*ngSwitchCase="TicketValidationState.TdUncertain"
|
||||
>Transportschein unklar
|
||||
</span>
|
||||
<span
|
||||
class="font-bold"
|
||||
*ngSwitchCase="TicketValidationState.DocumentsMissing"
|
||||
>Dokumente fehlen
|
||||
</span>
|
||||
<span
|
||||
class="font-bold"
|
||||
*ngSwitchCase="TicketValidationState.Archived"
|
||||
>Archiviert
|
||||
</span>
|
||||
</ng-container>
|
||||
@switch (tour.ticket.currentState) {
|
||||
@case (TicketValidationState.Created) {
|
||||
<span
|
||||
class="font-bold"
|
||||
>Touren angenommen
|
||||
</span>
|
||||
}
|
||||
@case (TicketValidationState.UserInteractionRequired) {
|
||||
<span
|
||||
class="font-bold"
|
||||
>Genehmigung / Transportschein
|
||||
</span>
|
||||
}
|
||||
@case (TicketValidationState.TdUncertain) {
|
||||
<span
|
||||
class="font-bold"
|
||||
>Transportschein unklar
|
||||
</span>
|
||||
}
|
||||
@case (TicketValidationState.DocumentsMissing) {
|
||||
<span
|
||||
class="font-bold"
|
||||
>Dokumente fehlen
|
||||
</span>
|
||||
}
|
||||
@case (TicketValidationState.Archived) {
|
||||
<span
|
||||
class="font-bold"
|
||||
>Archiviert
|
||||
</span>
|
||||
}
|
||||
}
|
||||
</mat-list-item>
|
||||
<mat-list-item role="listitem">
|
||||
Typ:
|
||||
|
|
@ -55,47 +60,46 @@ import { Ticket, TicketValidationState, Tour } from '../../../core/data-access/g
|
|||
</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
<mat-list
|
||||
class="col-span-2 max-h-96 overflow-y-auto"
|
||||
*ngIf="tour.ticket.errors.length > 0 || tour.ticket.notes.length > 0"
|
||||
role="list"
|
||||
>
|
||||
<div mat-subheader class="text-sm">Ticket Anmerkungen</div>
|
||||
<mat-list-item
|
||||
*ngFor="let error of tour.ticket.errors"
|
||||
lines="2"
|
||||
role="listitem"
|
||||
>
|
||||
<mat-icon matListItemIcon class="!text-red-600">error</mat-icon>
|
||||
<span matListItemTitle>{{ error.split(':').at(0) }}</span>
|
||||
<span [title]="error.split(':').at(1)">{{
|
||||
error.split(':').at(1)
|
||||
}}</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item
|
||||
*ngFor="let note of tour.ticket.notes"
|
||||
lines="2"
|
||||
role="listitem"
|
||||
>
|
||||
<mat-icon matListItemIcon class="!text-yellow-400">warning</mat-icon>
|
||||
<span matListItemTitle>{{ note.split(':').at(0) }}</span>
|
||||
<span [title]="note.split(':').at(1)">{{
|
||||
note.split(':').at(1)
|
||||
}}</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
@if (tour.ticket.errors.length > 0 || tour.ticket.notes.length > 0) {
|
||||
<mat-list
|
||||
class="col-span-2 max-h-96 overflow-y-auto"
|
||||
role="list"
|
||||
>
|
||||
<div mat-subheader class="text-sm">Ticket Anmerkungen</div>
|
||||
@for (error of tour.ticket.errors; track error) {
|
||||
<mat-list-item
|
||||
lines="2"
|
||||
role="listitem"
|
||||
>
|
||||
<mat-icon matListItemIcon class="!text-red-600">error</mat-icon>
|
||||
<span matListItemTitle>{{ error.split(':').at(0) }}</span>
|
||||
<span [title]="error.split(':').at(1)">{{
|
||||
error.split(':').at(1)
|
||||
}}</span>
|
||||
</mat-list-item>
|
||||
}
|
||||
@for (note of tour.ticket.notes; track note) {
|
||||
<mat-list-item
|
||||
lines="2"
|
||||
role="listitem"
|
||||
>
|
||||
<mat-icon matListItemIcon class="!text-yellow-400">warning</mat-icon>
|
||||
<span matListItemTitle>{{ note.split(':').at(0) }}</span>
|
||||
<span [title]="note.split(':').at(1)">{{
|
||||
note.split(':').at(1)
|
||||
}}</span>
|
||||
</mat-list-item>
|
||||
}
|
||||
</mat-list>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
`,
|
||||
styles: [],
|
||||
imports: [
|
||||
MatIconModule,
|
||||
MatListModule,
|
||||
MatDividerModule,
|
||||
NgIf,
|
||||
NgFor,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
]
|
||||
MatIconModule,
|
||||
MatListModule,
|
||||
MatDividerModule
|
||||
]
|
||||
})
|
||||
export class AccountingTourTicketComponent {
|
||||
@Input() tour!: Tour & { ticket: Ticket };
|
||||
|
|
|
|||
|
|
@ -1,29 +1,31 @@
|
|||
<mat-list role="list" *ngIf="anomalies$ | async as anomalies">
|
||||
<ng-container *ngIf="anomalies.length; else emptyListTmpl">
|
||||
<mat-list-item
|
||||
*ngFor="let anomaly of anomalies"
|
||||
class="flex"
|
||||
role="listitem"
|
||||
@if (anomalies$ | async; as anomalies) {
|
||||
<mat-list role="list">
|
||||
@if (anomalies.length) {
|
||||
@for (anomaly of anomalies; track anomaly) {
|
||||
<mat-list-item
|
||||
class="flex"
|
||||
role="listitem"
|
||||
>
|
||||
<dks-anomaly-list-item
|
||||
[anomaly]="anomaly"
|
||||
(anomalySetToSleep)="setAnomalyToSleep($event)"
|
||||
(anomalyAccepted)="acceptAnomaly($event)"
|
||||
></dks-anomaly-list-item>
|
||||
</mat-list-item>
|
||||
}
|
||||
} @else {
|
||||
<mat-list-item
|
||||
>Alle Mitarbeiter sind auf einem Fahrzeug angemeldet!</mat-list-item
|
||||
>
|
||||
}
|
||||
</mat-list>
|
||||
}
|
||||
<mat-paginator
|
||||
class="mt-auto"
|
||||
[length]="totalCount$ | async"
|
||||
[pageSize]="pageSize"
|
||||
[pageSizeOptions]="[]"
|
||||
(page)="changePage($event)"
|
||||
aria-label="Select page"
|
||||
>
|
||||
<dks-anomaly-list-item
|
||||
[anomaly]="anomaly"
|
||||
(anomalySetToSleep)="setAnomalyToSleep($event)"
|
||||
(anomalyAccepted)="acceptAnomaly($event)"
|
||||
></dks-anomaly-list-item>
|
||||
</mat-list-item>
|
||||
</ng-container>
|
||||
<ng-template #emptyListTmpl>
|
||||
<mat-list-item
|
||||
>Alle Mitarbeiter sind auf einem Fahrzeug angemeldet!</mat-list-item
|
||||
>
|
||||
</ng-template>
|
||||
</mat-list>
|
||||
<mat-paginator
|
||||
class="mt-auto"
|
||||
[length]="totalCount$ | async"
|
||||
[pageSize]="pageSize"
|
||||
[pageSizeOptions]="[]"
|
||||
(page)="changePage($event)"
|
||||
aria-label="Select page"
|
||||
>
|
||||
</mat-paginator>
|
||||
</mat-paginator>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
<h2>{{ ref.data.title }}</h2>
|
||||
<mat-divider></mat-divider>
|
||||
<article class="my-4 mx-2">
|
||||
<div class="pb-4" *ngFor="let target of ref.data.solution">
|
||||
<span class="font-bold text-base">{{ target.name }}</span>
|
||||
<ul class="ml-4">
|
||||
<li class="list-disc" *ngFor="let step of target.steps">
|
||||
{{ step.description }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@for (target of ref.data.solution; track target) {
|
||||
<div class="pb-4">
|
||||
<span class="font-bold text-base">{{ target.name }}</span>
|
||||
<ul class="ml-4">
|
||||
@for (step of target.steps; track step) {
|
||||
<li class="list-disc">
|
||||
{{ step.description }}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
</article>
|
||||
<mat-divider></mat-divider>
|
||||
<div class="flex justify-end mt-2">
|
||||
|
|
|
|||
|
|
@ -1,48 +1,47 @@
|
|||
<ng-container *ngIf="anomalies$ | async as anomalies">
|
||||
<mat-tree
|
||||
*ngIf="anomalies.length; else emptyListTmpl"
|
||||
[dataSource]="dataSource"
|
||||
[treeControl]="treeControl"
|
||||
>
|
||||
<mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
|
||||
<dks-anomaly-list-item
|
||||
[anomaly]="node.anomaly"
|
||||
(anomalySetToSleep)="setAnomalyToSleep($event)"
|
||||
(anomalyAccepted)="acceptAnomaly($event)"
|
||||
></dks-anomaly-list-item>
|
||||
</mat-tree-node>
|
||||
|
||||
<mat-tree-node
|
||||
class="flex"
|
||||
*matTreeNodeDef="let node; when: hasChild"
|
||||
matTreeNodePadding
|
||||
>
|
||||
<button
|
||||
mat-icon-button
|
||||
matTreeNodeToggle
|
||||
[attr.aria-label]="'Toggle ' + node.name"
|
||||
@if (anomalies$ | async; as anomalies) {
|
||||
@if (anomalies.length) {
|
||||
<mat-tree
|
||||
[dataSource]="dataSource"
|
||||
[treeControl]="treeControl"
|
||||
>
|
||||
<mat-icon class="mat-icon-rtl-mirror">
|
||||
{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
|
||||
</mat-icon>
|
||||
</button>
|
||||
{{ node.name }}
|
||||
<div class="ml-auto rounded-full w-5 h-5 flex justify-center items-center font-bold" style="background-color: var(--mat-sys-primary-container); color: var(--mat-sys-on-primary-container)">{{ node?.childCount }}</div>
|
||||
</mat-tree-node>
|
||||
</mat-tree>
|
||||
<ng-template #emptyListTmpl>
|
||||
<mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
|
||||
<dks-anomaly-list-item
|
||||
[anomaly]="node.anomaly"
|
||||
(anomalySetToSleep)="setAnomalyToSleep($event)"
|
||||
(anomalyAccepted)="acceptAnomaly($event)"
|
||||
></dks-anomaly-list-item>
|
||||
</mat-tree-node>
|
||||
<mat-tree-node
|
||||
class="flex"
|
||||
*matTreeNodeDef="let node; when: hasChild"
|
||||
matTreeNodePadding
|
||||
>
|
||||
<button
|
||||
mat-icon-button
|
||||
matTreeNodeToggle
|
||||
[attr.aria-label]="'Toggle ' + node.name"
|
||||
>
|
||||
<mat-icon class="mat-icon-rtl-mirror">
|
||||
{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
|
||||
</mat-icon>
|
||||
</button>
|
||||
{{ node.name }}
|
||||
<div class="ml-auto rounded-full w-5 h-5 flex justify-center items-center font-bold" style="background-color: var(--mat-sys-primary-container); color: var(--mat-sys-on-primary-container)">{{ node?.childCount }}</div>
|
||||
</mat-tree-node>
|
||||
</mat-tree>
|
||||
} @else {
|
||||
<mat-list-item
|
||||
>Herzlichen Glückwunsch, es liegen keine Anomalien vor!</mat-list-item
|
||||
>
|
||||
}
|
||||
}
|
||||
<mat-paginator
|
||||
#paginator
|
||||
class="mt-auto"
|
||||
[length]="totalCount$ | async"
|
||||
[pageSize]="pageSize"
|
||||
[pageSizeOptions]="[]"
|
||||
(page)="changePage($event)"
|
||||
aria-label="Select page"
|
||||
>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<mat-paginator
|
||||
#paginator
|
||||
class="mt-auto"
|
||||
[length]="totalCount$ | async"
|
||||
[pageSize]="pageSize"
|
||||
[pageSizeOptions]="[]"
|
||||
(page)="changePage($event)"
|
||||
aria-label="Select page"
|
||||
>
|
||||
</mat-paginator>
|
||||
</mat-paginator>
|
||||
|
|
|
|||
|
|
@ -2,24 +2,27 @@
|
|||
<mat-card-header class="flex justify-center">
|
||||
<mat-card-title>{{ ordinanceType }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content *ngIf="toursPerTimeKPI$ | async as kpis">
|
||||
<div class="flex">
|
||||
<div
|
||||
*ngFor="let kpi of kpis; index as idx"
|
||||
class="w-1/3 flex flex-col justify-center items-center"
|
||||
style="border-color: var(--mat-sys-primary);"
|
||||
@if (toursPerTimeKPI$ | async; as kpis) {
|
||||
<mat-card-content>
|
||||
<div class="flex">
|
||||
@for (kpi of kpis; track kpi; let idx = $index) {
|
||||
<div
|
||||
class="w-1/3 flex flex-col justify-center items-center"
|
||||
style="border-color: var(--mat-sys-primary);"
|
||||
[ngClass]="{
|
||||
'border-b-2': (currentTimeSlot$ | async) === idx,
|
||||
}"
|
||||
>
|
||||
<div class="p-4 text-base">
|
||||
{{ kpi.kpi.value.toString().replace(".", ",") }}
|
||||
</div>
|
||||
<div class="font-bold">
|
||||
{{ kpi.interval.start | date : "HH:mm" }} -
|
||||
{{ kpi.interval.end | date : "HH:mm" }}
|
||||
</div>
|
||||
>
|
||||
<div class="p-4 text-base">
|
||||
{{ kpi.kpi.value.toString().replace(".", ",") }}
|
||||
</div>
|
||||
<div class="font-bold">
|
||||
{{ kpi.interval.start | date : "HH:mm" }} -
|
||||
{{ kpi.interval.end | date : "HH:mm" }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card-content>
|
||||
}
|
||||
</mat-card>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||
</span>
|
||||
</div>
|
||||
<div class="flex flex-col w-1/2 text-gray-600 text-sm">
|
||||
<span class="font-medium" *ngIf="ref.data.createdAt">Erstellt am</span>
|
||||
@if (ref.data.createdAt) {
|
||||
<span class="font-medium">Erstellt am</span>
|
||||
}
|
||||
<span>
|
||||
{{ ref.data.createdAt | date: 'HH:mm' }} Uhr
|
||||
{{ ref.data.createdAt | date: 'dd.MM.yyyy' }}
|
||||
|
|
@ -16,29 +18,34 @@
|
|||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div
|
||||
class="flex flex-wrap md:flex-nowrap p-2"
|
||||
*ngIf="ref.data.driverName || ref.data.codriverName || ref.data.carName"
|
||||
>
|
||||
@if (ref.data.driverName || ref.data.codriverName || ref.data.carName) {
|
||||
<div
|
||||
class="w-full md:w-1/3 flex flex-col pb-1 md:pb-0"
|
||||
*ngIf="ref.data.carName"
|
||||
>
|
||||
<span class="font-semibold pb-1">Fahrzeug</span>
|
||||
{{ ref.data.carName }}
|
||||
class="flex flex-wrap md:flex-nowrap p-2"
|
||||
>
|
||||
@if (ref.data.carName) {
|
||||
<div
|
||||
class="w-full md:w-1/3 flex flex-col pb-1 md:pb-0"
|
||||
>
|
||||
<span class="font-semibold pb-1">Fahrzeug</span>
|
||||
{{ ref.data.carName }}
|
||||
</div>
|
||||
}
|
||||
@if (ref.data.driverName) {
|
||||
<div
|
||||
class="w-full md:w-1/3 flex flex-col pb-1 md:pb-0"
|
||||
>
|
||||
<span class="font-semibold pb-1">Fahrer</span>
|
||||
<span>{{ ref.data.driverName }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (ref.data.codriverName) {
|
||||
<div class="w-full md:w-1/3 flex flex-col">
|
||||
<span class="font-semibold pb-1">Beifahrer</span>
|
||||
<span>{{ ref.data.codriverName }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
class="w-full md:w-1/3 flex flex-col pb-1 md:pb-0"
|
||||
*ngIf="ref.data.driverName"
|
||||
>
|
||||
<span class="font-semibold pb-1">Fahrer</span>
|
||||
<span>{{ ref.data.driverName }}</span>
|
||||
</div>
|
||||
<div class="w-full md:w-1/3 flex flex-col" *ngIf="ref.data.codriverName">
|
||||
<span class="font-semibold pb-1">Beifahrer</span>
|
||||
<span>{{ ref.data.codriverName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="flex flex-col p-2">
|
||||
<span class="font-semibold pb-1">Patient</span>
|
||||
<span>{{ ref.data.patientName }} {{ ref.data.patientSurname }}</span>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
<div
|
||||
class="flex flex-col justify-between flex-wrap m-2 xl:m-4"
|
||||
*ngIf="upcomingTours$ | async as upcomingTours"
|
||||
>
|
||||
<ng-container *ngIf="upcomingTours.length; else emptyTmpl">
|
||||
<div
|
||||
class="py-2 xl:px-2 flex flex-nowrap items-center"
|
||||
*ngFor="let tour of upcomingTours"
|
||||
@if (upcomingTours$ | async; as upcomingTours) {
|
||||
<div
|
||||
class="flex flex-col justify-between flex-wrap m-2 xl:m-4"
|
||||
>
|
||||
<div class="h-4 w-4 mr-2 rounded-full" [ngClass]="tour.colorClass"></div>
|
||||
<div class="cursor-pointer" (click)="openTourViewDialog(tour)">
|
||||
{{ tour.operationId }} - {{ tour.startDate | date: 'HH:mm' }} Uhr
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #emptyTmpl
|
||||
>Keine Touren in den nächsten 20 Minuten ohne Anfahrt</ng-template
|
||||
>
|
||||
</div>
|
||||
@if (upcomingTours.length) {
|
||||
@for (tour of upcomingTours; track tour) {
|
||||
<div
|
||||
class="py-2 xl:px-2 flex flex-nowrap items-center"
|
||||
>
|
||||
<div class="h-4 w-4 mr-2 rounded-full" [ngClass]="tour.colorClass"></div>
|
||||
<div class="cursor-pointer" (click)="openTourViewDialog(tour)">
|
||||
{{ tour.operationId }} - {{ tour.startDate | date: 'HH:mm' }} Uhr
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
Keine Touren in den nächsten 20 Minuten ohne Anfahrt
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<div
|
||||
*ngIf="echartsOptions$ | async as echartsOptions"
|
||||
dksEcharts
|
||||
[defaultHeight]="300"
|
||||
[options]="echartsOptions"
|
||||
[extentions]="echartsExtensions"
|
||||
></div>
|
||||
@if (echartsOptions$ | async; as echartsOptions) {
|
||||
<div
|
||||
dksEcharts
|
||||
[defaultHeight]="300"
|
||||
[options]="echartsOptions"
|
||||
[extentions]="echartsExtensions"
|
||||
></div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
<div class="m-auto flex flex-col" *ngIf="metricsSummary$ | async as summary">
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.averageRevenue | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}} Ø Umsatz</span>
|
||||
@if (metricsSummary$ | async; as summary) {
|
||||
<div class="m-auto flex flex-col">
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.averageRevenue | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}} Ø Umsatz</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
(summary.availableTime / summary.tourAmount) | number : '1.2-2'
|
||||
}} Index</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
(summary.availableTime / summary.tourAmount) | number : '1.2-2'
|
||||
}} Index</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
<div
|
||||
*ngIf="echartsOptions$ | async as echartsOptions"
|
||||
dksEcharts
|
||||
[defaultHeight]="300"
|
||||
[options]="echartsOptions"
|
||||
[extentions]="echartsExtensions"
|
||||
></div>
|
||||
@if (echartsOptions$ | async; as echartsOptions) {
|
||||
<div
|
||||
dksEcharts
|
||||
[defaultHeight]="300"
|
||||
[options]="echartsOptions"
|
||||
[extentions]="echartsExtensions"
|
||||
></div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,47 @@
|
|||
<div class="m-auto flex flex-col" *ngIf="metricsSummary$ | async as summary">
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.revenue | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span>
|
||||
<div class="p-1 text-sm" title="Umsatz">
|
||||
<mat-icon>euro</mat-icon>
|
||||
@if (metricsSummary$ | async; as summary) {
|
||||
<div class="m-auto flex flex-col">
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.revenue | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span>
|
||||
<div class="p-1 text-sm" title="Umsatz">
|
||||
<mat-icon>euro</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.personnelCost | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span>
|
||||
<div class="p-1 text-sm" title="Personalkosten">
|
||||
<mat-icon>people</mat-icon>
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.personnelCost | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span>
|
||||
<div class="p-1 text-sm" title="Personalkosten">
|
||||
<mat-icon>people</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.consumptionCost | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span>
|
||||
<div class="p-1 text-sm" title="Verbrauch">
|
||||
<mat-icon>local_shipping</mat-icon>
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.consumptionCost | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span>
|
||||
<div class="p-1 text-sm" title="Verbrauch">
|
||||
<mat-icon>local_shipping</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.breakEven | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span>
|
||||
<div class="p-1 text-sm" title="Deckungsbeitrag">
|
||||
<mat-icon>account_balance</mat-icon>
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.breakEven | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span>
|
||||
<div class="p-1 text-sm" title="Deckungsbeitrag">
|
||||
<mat-icon>account_balance</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: Add feature toggle for forecast -->
|
||||
<!-- <div class="flex items-center justify-end text-yellow-500">
|
||||
<!-- TODO: Add feature toggle for forecast -->
|
||||
<!-- <div class="flex items-center justify-end text-yellow-500">
|
||||
<span class="text-base"
|
||||
>+{{
|
||||
summary.forecastRevenue | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
summary.forecastRevenue | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span
|
||||
>
|
||||
<div class="p-1 text-sm" title="Umsatz Vorausschau">
|
||||
<mat-icon>account_balance</mat-icon>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
>
|
||||
<div class="p-1 text-sm" title="Umsatz Vorausschau">
|
||||
<mat-icon>account_balance</mat-icon>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,43 @@
|
|||
<div class="grid grid-cols-1 md:grid-cols-4" *ngIf="vm$ | async as vm">
|
||||
<ng-container [ngSwitch]="selectedMetric">
|
||||
<ng-container *ngSwitchCase="DiagrammType.TimePerTour">
|
||||
<dks-time-per-tour-metric
|
||||
class="col-span-3"
|
||||
[labelFormat]="labelFormat"
|
||||
[metrics]="vm.metrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
[showCategories]="showCategories"
|
||||
></dks-time-per-tour-metric>
|
||||
<div class="flex flex-col justify-center items-center">
|
||||
<mat-slide-toggle (toggleChange)="showCategories = !showCategories"
|
||||
>Zeige Kategorien</mat-slide-toggle
|
||||
>
|
||||
<dks-time-per-tour-summary
|
||||
class="m-auto"
|
||||
@if (vm$ | async; as vm) {
|
||||
<div class="grid grid-cols-1 md:grid-cols-4">
|
||||
@switch (selectedMetric) {
|
||||
@case (DiagrammType.TimePerTour) {
|
||||
<dks-time-per-tour-metric
|
||||
class="col-span-3"
|
||||
[labelFormat]="labelFormat"
|
||||
[metrics]="vm.metrics"
|
||||
></dks-time-per-tour-summary>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="DiagrammType.Revenue">
|
||||
<dks-breakeven-metric
|
||||
class="col-span-3"
|
||||
[labelFormat]="getLabelFormatForMetricTimeScale(metricTimeScale)"
|
||||
[metrics]="vm.metrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
></dks-breakeven-metric>
|
||||
<dks-breakeven-summary [metrics]="vm.metrics"></dks-breakeven-summary>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="DiagrammType.AverageRevenue">
|
||||
<dks-average-revenue-metric
|
||||
class="col-span-3"
|
||||
[labelFormat]="getLabelFormatForMetricTimeScale(metricTimeScale)"
|
||||
[metrics]="vm.metrics"
|
||||
></dks-average-revenue-metric>
|
||||
<dks-average-revenue-summary
|
||||
[metrics]="vm.metrics"
|
||||
></dks-average-revenue-summary>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
[showCategories]="showCategories"
|
||||
></dks-time-per-tour-metric>
|
||||
<div class="flex flex-col justify-center items-center">
|
||||
<mat-slide-toggle (toggleChange)="showCategories = !showCategories"
|
||||
>Zeige Kategorien</mat-slide-toggle
|
||||
>
|
||||
<dks-time-per-tour-summary
|
||||
class="m-auto"
|
||||
[metrics]="vm.metrics"
|
||||
></dks-time-per-tour-summary>
|
||||
</div>
|
||||
}
|
||||
@case (DiagrammType.Revenue) {
|
||||
<dks-breakeven-metric
|
||||
class="col-span-3"
|
||||
[labelFormat]="getLabelFormatForMetricTimeScale(metricTimeScale)"
|
||||
[metrics]="vm.metrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
></dks-breakeven-metric>
|
||||
<dks-breakeven-summary [metrics]="vm.metrics"></dks-breakeven-summary>
|
||||
}
|
||||
@case (DiagrammType.AverageRevenue) {
|
||||
<dks-average-revenue-metric
|
||||
class="col-span-3"
|
||||
[labelFormat]="getLabelFormatForMetricTimeScale(metricTimeScale)"
|
||||
[metrics]="vm.metrics"
|
||||
></dks-average-revenue-metric>
|
||||
<dks-average-revenue-summary
|
||||
[metrics]="vm.metrics"
|
||||
></dks-average-revenue-summary>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<div
|
||||
*ngIf="echartsOptions$ | async as echartsOptions"
|
||||
dksEcharts
|
||||
[defaultHeight]="300"
|
||||
[options]="echartsOptions"
|
||||
[extentions]="echartsExtensions"
|
||||
></div>
|
||||
@if (echartsOptions$ | async; as echartsOptions) {
|
||||
<div
|
||||
dksEcharts
|
||||
[defaultHeight]="300"
|
||||
[options]="echartsOptions"
|
||||
[extentions]="echartsExtensions"
|
||||
></div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
<div class="m-auto flex flex-col" *ngIf="metricsSummary$ | async as summary">
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{ summary.tourAmount | number : '1.2-2' }}</span>
|
||||
<div class="p-1 text-sm" title="Menge Touren">
|
||||
<mat-icon>local_shipping</mat-icon>
|
||||
@if (metricsSummary$ | async; as summary) {
|
||||
<div class="m-auto flex flex-col">
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{ summary.tourAmount | number : '1.2-2' }}</span>
|
||||
<div class="p-1 text-sm" title="Menge Touren">
|
||||
<mat-icon>local_shipping</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: Add feature toggle for forecast -->
|
||||
<!-- <div class="flex items-center justify-end text-yellow-500">
|
||||
<!-- TODO: Add feature toggle for forecast -->
|
||||
<!-- <div class="flex items-center justify-end text-yellow-500">
|
||||
<span class="text-base">+{{ summary.forecast | number : '1.0-0' }}</span>
|
||||
<div class="p-1 text-sm" title="Vorausschau">
|
||||
<mat-icon>local_shipping</mat-icon>
|
||||
|
|
@ -15,28 +16,29 @@
|
|||
<div class="flex items-center justify-end">
|
||||
<span class="text-base"
|
||||
>{{ summary.availableTime | number : '1.2-2' }} h</span
|
||||
>
|
||||
<div class="p-1 text-sm" title="Verfügbare Zeit">
|
||||
<mat-icon>departure_board</mat-icon>
|
||||
>
|
||||
<div class="p-1 text-sm" title="Verfügbare Zeit">
|
||||
<mat-icon>departure_board</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.breakEven | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span>
|
||||
<div class="p-1 text-sm" title="Deckungsbeitrag">
|
||||
<mat-icon>account_balance</mat-icon>
|
||||
<div class="flex items-center justify-end">
|
||||
<span class="text-base">{{
|
||||
summary.breakEven | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span>
|
||||
<div class="p-1 text-sm" title="Deckungsbeitrag">
|
||||
<mat-icon>account_balance</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: Add feature toggle for forecast -->
|
||||
<!-- <div class="flex items-center justify-end text-yellow-500">
|
||||
<!-- TODO: Add feature toggle for forecast -->
|
||||
<!-- <div class="flex items-center justify-end text-yellow-500">
|
||||
<span class="text-base"
|
||||
>+{{
|
||||
summary.forecastRevenue | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
summary.forecastRevenue | currency : 'EUR' : 'symbol' : '1.2-2'
|
||||
}}</span
|
||||
>
|
||||
<div class="p-1 text-sm" title="Vorausschau">
|
||||
<mat-icon>account_balance</mat-icon>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
>
|
||||
<div class="p-1 text-sm" title="Vorausschau">
|
||||
<mat-icon>account_balance</mat-icon>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,53 +1,53 @@
|
|||
<!-- class="py-5 w-full flex flex-col justify-center items-center bg-white shadow rounded cursor-pointer transition ease-in-out hover:shadow-md hover:scale-105 duration-200" -->
|
||||
<mat-card
|
||||
appearance="raised"
|
||||
class="cursor-pointer transition ease-in-out hover:scale-105 duration-200"
|
||||
@if (metricSummary$ | async; as summary) {
|
||||
<mat-card
|
||||
appearance="raised"
|
||||
class="cursor-pointer transition ease-in-out hover:scale-105 duration-200"
|
||||
[ngClass]="{
|
||||
'!border': selected
|
||||
}"
|
||||
style="border-color: var(--mat-sys-primary)"
|
||||
(click)="setSelectedViewType()"
|
||||
*ngIf="metricSummary$ | async as summary"
|
||||
>
|
||||
<mat-card-header class="!pb-2 justify-center">
|
||||
<mat-card-title>{{ viewType }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<!-- <span class="text-lg pb-4">{{ viewType }}</span> -->
|
||||
|
||||
<mat-card-content class="!flex justify-center">
|
||||
@switch (currentMetric) { @case (DiagrammType.TimePerTour) {
|
||||
<div class="w-1/4 flex flex-col items-end">
|
||||
<div class="flex items-center pb-2" title="Touren Menge">
|
||||
{{ summary.tourAmount.toFixed(2) }}
|
||||
<mat-icon class="mx-2">local_shipping</mat-icon>
|
||||
</div>
|
||||
<div class="flex items-center" title="Verfügbare Zeit">
|
||||
{{ summary.availableTime.toFixed(2) }}
|
||||
<mat-icon class="mx-2">departure_board</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
} @case (DiagrammType.Revenue) {
|
||||
<div class="w-1/4 flex flex-col items-end">
|
||||
<div class="flex items-center pb-2" title="Umsatz">
|
||||
{{ summary.revenue | currency : "EUR" : "symbol" : ".2" }}
|
||||
<mat-icon class="mx-2">euro</mat-icon>
|
||||
</div>
|
||||
<div class="flex items-center" title="Deckungsbeitrag">
|
||||
{{ summary.breakEven | currency : "EUR" }}
|
||||
<mat-icon class="mx-2">account_balance</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
} @case (DiagrammType.AverageRevenue) {
|
||||
<div class="w-1/4 flex flex-col items-end">
|
||||
<div class="flex items-center pb-2" title="Durchschnittlicher Umsatz">
|
||||
<span class="mr-1">Ø</span>
|
||||
{{ summary.averageRevenue | currency : "EUR" }}
|
||||
</div>
|
||||
<div class="flex items-center" title="Zeit pro Tour Index">
|
||||
{{ calculateIndex(summary).toFixed(2) }}
|
||||
<span class="mx-2">Index</span>
|
||||
</div>
|
||||
</div>
|
||||
} }
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
style="border-color: var(--mat-sys-primary)"
|
||||
(click)="setSelectedViewType()"
|
||||
>
|
||||
<mat-card-header class="!pb-2 justify-center">
|
||||
<mat-card-title>{{ viewType }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<!-- <span class="text-lg pb-4">{{ viewType }}</span> -->
|
||||
<mat-card-content class="!flex justify-center">
|
||||
@switch (currentMetric) { @case (DiagrammType.TimePerTour) {
|
||||
<div class="w-1/4 flex flex-col items-end">
|
||||
<div class="flex items-center pb-2" title="Touren Menge">
|
||||
{{ summary.tourAmount.toFixed(2) }}
|
||||
<mat-icon class="mx-2">local_shipping</mat-icon>
|
||||
</div>
|
||||
<div class="flex items-center" title="Verfügbare Zeit">
|
||||
{{ summary.availableTime.toFixed(2) }}
|
||||
<mat-icon class="mx-2">departure_board</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
} @case (DiagrammType.Revenue) {
|
||||
<div class="w-1/4 flex flex-col items-end">
|
||||
<div class="flex items-center pb-2" title="Umsatz">
|
||||
{{ summary.revenue | currency : "EUR" : "symbol" : ".2" }}
|
||||
<mat-icon class="mx-2">euro</mat-icon>
|
||||
</div>
|
||||
<div class="flex items-center" title="Deckungsbeitrag">
|
||||
{{ summary.breakEven | currency : "EUR" }}
|
||||
<mat-icon class="mx-2">account_balance</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
} @case (DiagrammType.AverageRevenue) {
|
||||
<div class="w-1/4 flex flex-col items-end">
|
||||
<div class="flex items-center pb-2" title="Durchschnittlicher Umsatz">
|
||||
<span class="mr-1">Ø</span>
|
||||
{{ summary.averageRevenue | currency : "EUR" }}
|
||||
</div>
|
||||
<div class="flex items-center" title="Zeit pro Tour Index">
|
||||
{{ calculateIndex(summary).toFixed(2) }}
|
||||
<span class="mx-2">Index</span>
|
||||
</div>
|
||||
</div>
|
||||
} }
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
import {
|
||||
AsyncPipe,
|
||||
CurrencyPipe,
|
||||
NgClass,
|
||||
NgIf
|
||||
} from '@angular/common';
|
||||
import { AsyncPipe, CurrencyPipe, NgClass } from '@angular/common';
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
|
@ -15,13 +10,12 @@ import { summarizeMetrics } from '../../util/summarize-metrics';
|
|||
@Component({
|
||||
selector: 'dks-view-type-selector',
|
||||
imports: [
|
||||
NgIf,
|
||||
NgClass,
|
||||
AsyncPipe,
|
||||
CurrencyPipe,
|
||||
MatIconModule,
|
||||
MatCardModule,
|
||||
],
|
||||
MatCardModule
|
||||
],
|
||||
templateUrl: './view-type-selector.component.html',
|
||||
styleUrls: ['./view-type-selector.component.css'],
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,141 +1,143 @@
|
|||
<ng-container *ngIf="vm$ | async as vm; else loadingTmpl">
|
||||
<div class="flex flex-col m-0 md:m-4" *ngIf="!vm.isLoading; else loadingTmpl">
|
||||
<!-- Top row with user input elements -->
|
||||
<div class="flex flex-wrap md:flex-nowrap justify-between items-center">
|
||||
<div class="flex items-center m-2">
|
||||
<button mat-icon-button (click)="addMonthToSelectedDate(-1)">
|
||||
<mat-icon>chevron_left</mat-icon>
|
||||
</button>
|
||||
<div class="flex items-center">
|
||||
<span class="pr-2">
|
||||
{{ vm.selectedDate | date : "MM.yyyy" }}
|
||||
</span>
|
||||
<input
|
||||
[formControl]="selectedDate"
|
||||
[matDatepicker]="picker"
|
||||
[matDatepickerFilter]="notSunday"
|
||||
[min]="minSelectableDate"
|
||||
class="invisible w-0"
|
||||
/>
|
||||
<button mat-icon-button (click)="picker.open()">
|
||||
<mat-icon>today</mat-icon>
|
||||
@if (vm$ | async; as vm) {
|
||||
@if (!vm.isLoading) {
|
||||
<div class="flex flex-col m-0 md:m-4">
|
||||
<!-- Top row with user input elements -->
|
||||
<div class="flex flex-wrap md:flex-nowrap justify-between items-center">
|
||||
<div class="flex items-center m-2">
|
||||
<button mat-icon-button (click)="addMonthToSelectedDate(-1)">
|
||||
<mat-icon>chevron_left</mat-icon>
|
||||
</button>
|
||||
<div class="flex items-center">
|
||||
<span class="pr-2">
|
||||
{{ vm.selectedDate | date : "MM.yyyy" }}
|
||||
</span>
|
||||
<input
|
||||
[formControl]="selectedDate"
|
||||
[matDatepicker]="picker"
|
||||
[matDatepickerFilter]="notSunday"
|
||||
[min]="minSelectableDate"
|
||||
class="invisible w-0"
|
||||
/>
|
||||
<button mat-icon-button (click)="picker.open()">
|
||||
<mat-icon>today</mat-icon>
|
||||
</button>
|
||||
<mat-datepicker
|
||||
#picker
|
||||
startView="multi-year"
|
||||
(monthSelected)="setMonthAndYear($event, picker)"
|
||||
class="month-picker"
|
||||
></mat-datepicker>
|
||||
</div>
|
||||
<button mat-icon-button (click)="addMonthToSelectedDate(1)">
|
||||
<mat-icon>chevron_right</mat-icon>
|
||||
</button>
|
||||
<mat-datepicker
|
||||
#picker
|
||||
startView="multi-year"
|
||||
(monthSelected)="setMonthAndYear($event, picker)"
|
||||
class="month-picker"
|
||||
></mat-datepicker>
|
||||
</div>
|
||||
<button mat-icon-button (click)="addMonthToSelectedDate(1)">
|
||||
<mat-icon>chevron_right</mat-icon>
|
||||
</button>
|
||||
<mat-chip-listbox (change)="changeSelectedType($event)">
|
||||
<mat-chip-option
|
||||
[value]="DiagrammType.Revenue"
|
||||
[selected]="vm.selectedMetric === DiagrammType.Revenue"
|
||||
>
|
||||
Umsatz
|
||||
</mat-chip-option>
|
||||
<mat-chip-option
|
||||
[value]="DiagrammType.TimePerTour"
|
||||
[selected]="vm.selectedMetric === DiagrammType.TimePerTour"
|
||||
>
|
||||
Kapazität pro Tour
|
||||
</mat-chip-option>
|
||||
<mat-chip-option
|
||||
[value]="DiagrammType.AverageRevenue"
|
||||
[selected]="vm.selectedMetric === DiagrammType.AverageRevenue"
|
||||
>
|
||||
Durchschnittsumsatz
|
||||
</mat-chip-option>
|
||||
</mat-chip-listbox>
|
||||
</div>
|
||||
|
||||
<mat-chip-listbox (change)="changeSelectedType($event)">
|
||||
<mat-chip-option
|
||||
[value]="DiagrammType.Revenue"
|
||||
[selected]="vm.selectedMetric === DiagrammType.Revenue"
|
||||
<div
|
||||
class="my-5 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 lg:gap-4"
|
||||
>
|
||||
Umsatz
|
||||
</mat-chip-option>
|
||||
<mat-chip-option
|
||||
[value]="DiagrammType.TimePerTour"
|
||||
[selected]="vm.selectedMetric === DiagrammType.TimePerTour"
|
||||
<dks-view-type-selector
|
||||
[viewType]="DashboardViewType.BTW"
|
||||
[currentMetric]="vm.selectedMetric"
|
||||
[metrics]="vm.btwMetrics"
|
||||
[selected]="vm.selectedViewType === DashboardViewType.BTW"
|
||||
(viewTypeSelected)="setSelectedViewType(DashboardViewType.BTW)"
|
||||
></dks-view-type-selector>
|
||||
<dks-view-type-selector
|
||||
[viewType]="DashboardViewType.TSW"
|
||||
[currentMetric]="vm.selectedMetric"
|
||||
[metrics]="vm.tswMetrics"
|
||||
[selected]="vm.selectedViewType === DashboardViewType.TSW"
|
||||
(viewTypeSelected)="setSelectedViewType(DashboardViewType.TSW)"
|
||||
></dks-view-type-selector>
|
||||
<dks-view-type-selector
|
||||
[viewType]="DashboardViewType.KTW"
|
||||
[currentMetric]="vm.selectedMetric"
|
||||
[metrics]="vm.ktwMetrics"
|
||||
[selected]="vm.selectedViewType === DashboardViewType.KTW"
|
||||
(viewTypeSelected)="setSelectedViewType(DashboardViewType.KTW)"
|
||||
></dks-view-type-selector>
|
||||
<dks-view-type-selector
|
||||
[viewType]="DashboardViewType.Gesamt"
|
||||
[currentMetric]="vm.selectedMetric"
|
||||
[metrics]="vm.combinedMetrics"
|
||||
[selected]="vm.selectedViewType === DashboardViewType.Gesamt"
|
||||
(viewTypeSelected)="setSelectedViewType(DashboardViewType.Gesamt)"
|
||||
></dks-view-type-selector>
|
||||
</div>
|
||||
<!-- Visualisation -->
|
||||
<mat-card
|
||||
appearance="outlined"
|
||||
class="grid grid-cols-2 lg:grid-cols-4 gap-2 lg:gap-4"
|
||||
>
|
||||
Kapazität pro Tour
|
||||
</mat-chip-option>
|
||||
<mat-chip-option
|
||||
[value]="DiagrammType.AverageRevenue"
|
||||
[selected]="vm.selectedMetric === DiagrammType.AverageRevenue"
|
||||
>
|
||||
Durchschnittsumsatz
|
||||
</mat-chip-option>
|
||||
</mat-chip-listbox>
|
||||
</div>
|
||||
<div
|
||||
class="my-5 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 lg:gap-4"
|
||||
>
|
||||
<dks-view-type-selector
|
||||
[viewType]="DashboardViewType.BTW"
|
||||
[currentMetric]="vm.selectedMetric"
|
||||
[metrics]="vm.btwMetrics"
|
||||
[selected]="vm.selectedViewType === DashboardViewType.BTW"
|
||||
(viewTypeSelected)="setSelectedViewType(DashboardViewType.BTW)"
|
||||
></dks-view-type-selector>
|
||||
<dks-view-type-selector
|
||||
[viewType]="DashboardViewType.TSW"
|
||||
[currentMetric]="vm.selectedMetric"
|
||||
[metrics]="vm.tswMetrics"
|
||||
[selected]="vm.selectedViewType === DashboardViewType.TSW"
|
||||
(viewTypeSelected)="setSelectedViewType(DashboardViewType.TSW)"
|
||||
></dks-view-type-selector>
|
||||
<dks-view-type-selector
|
||||
[viewType]="DashboardViewType.KTW"
|
||||
[currentMetric]="vm.selectedMetric"
|
||||
[metrics]="vm.ktwMetrics"
|
||||
[selected]="vm.selectedViewType === DashboardViewType.KTW"
|
||||
(viewTypeSelected)="setSelectedViewType(DashboardViewType.KTW)"
|
||||
></dks-view-type-selector>
|
||||
<dks-view-type-selector
|
||||
[viewType]="DashboardViewType.Gesamt"
|
||||
[currentMetric]="vm.selectedMetric"
|
||||
[metrics]="vm.combinedMetrics"
|
||||
[selected]="vm.selectedViewType === DashboardViewType.Gesamt"
|
||||
(viewTypeSelected)="setSelectedViewType(DashboardViewType.Gesamt)"
|
||||
></dks-view-type-selector>
|
||||
</div>
|
||||
<!-- Visualisation -->
|
||||
<mat-card
|
||||
appearance="outlined"
|
||||
class="grid grid-cols-2 lg:grid-cols-4 gap-2 lg:gap-4"
|
||||
>
|
||||
<mat-card-content>
|
||||
@switch (vm.selectedViewType) { @case (DashboardViewType.BTW) {
|
||||
<div class="col-span-2 lg:col-span-4 w-full">
|
||||
<dks-metric-summary
|
||||
[metrics]="vm.btwMetrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
[selectedMetric]="vm.selectedMetric"
|
||||
[labelFormat]="'d.M.yy'"
|
||||
></dks-metric-summary>
|
||||
</div>
|
||||
} @case (DashboardViewType.TSW) {
|
||||
<div class="col-span-2 lg:col-span-4 w-full">
|
||||
<dks-metric-summary
|
||||
[metrics]="vm.tswMetrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
[selectedMetric]="vm.selectedMetric"
|
||||
[labelFormat]="'d.M.yy'"
|
||||
></dks-metric-summary>
|
||||
</div>
|
||||
} @case (DashboardViewType.KTW) {
|
||||
<div
|
||||
class="col-span-2 lg:col-span-4 w-full rounded"
|
||||
>
|
||||
<dks-metric-summary
|
||||
[metrics]="vm.ktwMetrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
[selectedMetric]="vm.selectedMetric"
|
||||
[labelFormat]="'d.M.yy'"
|
||||
></dks-metric-summary>
|
||||
</div>
|
||||
} @case (DashboardViewType.Gesamt) {
|
||||
<div
|
||||
class="col-span-2 lg:col-span-4 w-full"
|
||||
>
|
||||
<dks-metric-summary
|
||||
[metrics]="vm.combinedMetrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
[selectedMetric]="vm.selectedMetric"
|
||||
[labelFormat]="'d.M.yy'"
|
||||
></dks-metric-summary>
|
||||
</div>
|
||||
} }
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #loadingTmpl>
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</ng-template>
|
||||
<mat-card-content>
|
||||
@switch (vm.selectedViewType) { @case (DashboardViewType.BTW) {
|
||||
<div class="col-span-2 lg:col-span-4 w-full">
|
||||
<dks-metric-summary
|
||||
[metrics]="vm.btwMetrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
[selectedMetric]="vm.selectedMetric"
|
||||
[labelFormat]="'d.M.yy'"
|
||||
></dks-metric-summary>
|
||||
</div>
|
||||
} @case (DashboardViewType.TSW) {
|
||||
<div class="col-span-2 lg:col-span-4 w-full">
|
||||
<dks-metric-summary
|
||||
[metrics]="vm.tswMetrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
[selectedMetric]="vm.selectedMetric"
|
||||
[labelFormat]="'d.M.yy'"
|
||||
></dks-metric-summary>
|
||||
</div>
|
||||
} @case (DashboardViewType.KTW) {
|
||||
<div
|
||||
class="col-span-2 lg:col-span-4 w-full rounded"
|
||||
>
|
||||
<dks-metric-summary
|
||||
[metrics]="vm.ktwMetrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
[selectedMetric]="vm.selectedMetric"
|
||||
[labelFormat]="'d.M.yy'"
|
||||
></dks-metric-summary>
|
||||
</div>
|
||||
} @case (DashboardViewType.Gesamt) {
|
||||
<div
|
||||
class="col-span-2 lg:col-span-4 w-full"
|
||||
>
|
||||
<dks-metric-summary
|
||||
[metrics]="vm.combinedMetrics"
|
||||
[metricTimeScale]="metricTimeScale"
|
||||
[selectedMetric]="vm.selectedMetric"
|
||||
[labelFormat]="'d.M.yy'"
|
||||
></dks-metric-summary>
|
||||
</div>
|
||||
} }
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
} @else {
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
}
|
||||
} @else {
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,71 +1,75 @@
|
|||
<div
|
||||
class="flex flex-col m-0 md:m-4"
|
||||
*ngIf="vm$ | async as vm; else loadingTmpl"
|
||||
>
|
||||
<div class="flex flex-wrap md:flex-nowrap">
|
||||
<!-- KPI -->
|
||||
<dks-time-per-tour-kpi
|
||||
class="w-full md:w-1/3 m-2 cursor-pointer rounded-xl transition ease-in-out hover:scale-105 duration-200"
|
||||
@if (vm$ | async; as vm) {
|
||||
<div
|
||||
class="flex flex-col m-0 md:m-4"
|
||||
>
|
||||
<div class="flex flex-wrap md:flex-nowrap">
|
||||
<!-- KPI -->
|
||||
<dks-time-per-tour-kpi
|
||||
class="w-full md:w-1/3 m-2 cursor-pointer rounded-xl transition ease-in-out hover:scale-105 duration-200"
|
||||
[ngClass]="{
|
||||
'border': vm.selectedOrdinanceType === OrdinanceTypes.BTW
|
||||
}"
|
||||
style="border-color: var(--mat-sys-primary);"
|
||||
[text]="OrdinanceTypes.BTW"
|
||||
[kpis]="vm.btwKPI.summaries"
|
||||
(click)="setSelectedOrdinanceType(OrdinanceTypes.BTW)"
|
||||
></dks-time-per-tour-kpi>
|
||||
<dks-time-per-tour-kpi
|
||||
class="w-full md:w-1/3 m-2 cursor-pointer rounded-xl transition ease-in-out hover:scale-105 duration-200"
|
||||
style="border-color: var(--mat-sys-primary);"
|
||||
[text]="OrdinanceTypes.BTW"
|
||||
[kpis]="vm.btwKPI.summaries"
|
||||
(click)="setSelectedOrdinanceType(OrdinanceTypes.BTW)"
|
||||
></dks-time-per-tour-kpi>
|
||||
<dks-time-per-tour-kpi
|
||||
class="w-full md:w-1/3 m-2 cursor-pointer rounded-xl transition ease-in-out hover:scale-105 duration-200"
|
||||
[ngClass]="{
|
||||
'border': vm.selectedOrdinanceType === OrdinanceTypes.TSW
|
||||
}"
|
||||
style="border-color: var(--mat-sys-primary);"
|
||||
[text]="OrdinanceTypes.TSW"
|
||||
[kpis]="vm.tswKPI.summaries"
|
||||
(click)="setSelectedOrdinanceType(OrdinanceTypes.TSW)"
|
||||
></dks-time-per-tour-kpi>
|
||||
<dks-time-per-tour-kpi
|
||||
class="w-full md:w-1/3 m-2 cursor-pointer rounded-xl transition ease-in-out hover:scale-105 duration-200"
|
||||
style="border-color: var(--mat-sys-primary);"
|
||||
[text]="OrdinanceTypes.TSW"
|
||||
[kpis]="vm.tswKPI.summaries"
|
||||
(click)="setSelectedOrdinanceType(OrdinanceTypes.TSW)"
|
||||
></dks-time-per-tour-kpi>
|
||||
<dks-time-per-tour-kpi
|
||||
class="w-full md:w-1/3 m-2 cursor-pointer rounded-xl transition ease-in-out hover:scale-105 duration-200"
|
||||
[ngClass]="{
|
||||
'border': vm.selectedOrdinanceType === OrdinanceTypes.KTW
|
||||
}"
|
||||
style="border-color: var(--mat-sys-primary);"
|
||||
[text]="OrdinanceTypes.KTW"
|
||||
[kpis]="vm.ktwKPI.summaries"
|
||||
(click)="setSelectedOrdinanceType(OrdinanceTypes.KTW)"
|
||||
></dks-time-per-tour-kpi>
|
||||
style="border-color: var(--mat-sys-primary);"
|
||||
[text]="OrdinanceTypes.KTW"
|
||||
[kpis]="vm.ktwKPI.summaries"
|
||||
(click)="setSelectedOrdinanceType(OrdinanceTypes.KTW)"
|
||||
></dks-time-per-tour-kpi>
|
||||
</div>
|
||||
@if (vm.selectedOrdinanceType) {
|
||||
<div
|
||||
class="md:p-2 flex flex-wrap md:flex-nowrap"
|
||||
>
|
||||
<!-- Data Table -->
|
||||
<mat-card appearance="outlined" class="w-full">
|
||||
<mat-card-content>
|
||||
@switch (vm.selectedOrdinanceType) {
|
||||
@case (OrdinanceTypes.BTW) {
|
||||
<dks-time-per-tour-by-car
|
||||
[columns]="vm.btwKPI.cars.columns"
|
||||
[data]="vm.btwKPI.cars.data"
|
||||
class="w-full"
|
||||
></dks-time-per-tour-by-car>
|
||||
}
|
||||
@case (OrdinanceTypes.TSW) {
|
||||
<dks-time-per-tour-by-car
|
||||
[columns]="vm.tswKPI.cars.columns"
|
||||
[data]="vm.tswKPI.cars.data"
|
||||
class="w-full"
|
||||
></dks-time-per-tour-by-car>
|
||||
}
|
||||
@case (OrdinanceTypes.KTW) {
|
||||
<dks-time-per-tour-by-car
|
||||
[columns]="vm.ktwKPI.cars.columns"
|
||||
[data]="vm.ktwKPI.cars.data"
|
||||
class="w-full"
|
||||
></dks-time-per-tour-by-car>
|
||||
}
|
||||
}
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
class="md:p-2 flex flex-wrap md:flex-nowrap"
|
||||
*ngIf="vm.selectedOrdinanceType"
|
||||
>
|
||||
<!-- Data Table -->
|
||||
<mat-card appearance="outlined" class="w-full">
|
||||
<mat-card-content>
|
||||
<ng-container [ngSwitch]="vm.selectedOrdinanceType">
|
||||
<dks-time-per-tour-by-car
|
||||
[columns]="vm.btwKPI.cars.columns"
|
||||
[data]="vm.btwKPI.cars.data"
|
||||
class="w-full"
|
||||
*ngSwitchCase="OrdinanceTypes.BTW"
|
||||
></dks-time-per-tour-by-car>
|
||||
<dks-time-per-tour-by-car
|
||||
[columns]="vm.tswKPI.cars.columns"
|
||||
[data]="vm.tswKPI.cars.data"
|
||||
class="w-full"
|
||||
*ngSwitchCase="OrdinanceTypes.TSW"
|
||||
></dks-time-per-tour-by-car>
|
||||
<dks-time-per-tour-by-car
|
||||
[columns]="vm.ktwKPI.cars.columns"
|
||||
[data]="vm.ktwKPI.cars.data"
|
||||
class="w-full"
|
||||
*ngSwitchCase="OrdinanceTypes.KTW"
|
||||
></dks-time-per-tour-by-car>
|
||||
</ng-container>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #loadingTmpl>
|
||||
} @else {
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</ng-template>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,29 +6,33 @@
|
|||
class="full-width-table"
|
||||
matSort
|
||||
aria-label="Fahrzeuge"
|
||||
>
|
||||
<ng-container matColumnDef="{{ column.id }}" *ngFor="let column of columns">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ column.title }}</th>
|
||||
<td mat-cell *matCellDef="let carData" [ngClass]="{'bg-gray-50': carData.carName === 'Touren ohne Zuweisung'}">{{ carData[column.id] }}{{(column.id !=='carName' && carData.carName === 'Touren ohne Zuweisung') ? 'T' : ''}}</td>
|
||||
</ng-container>
|
||||
>
|
||||
@for (column of columns; track column) {
|
||||
<ng-container matColumnDef="{{ column.id }}">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ column.title }}</th>
|
||||
<td mat-cell *matCellDef="let carData" [ngClass]="{'bg-gray-50': carData.carName === 'Touren ohne Zuweisung'}">{{ carData[column.id] }}{{(column.id !=='carName' && carData.carName === 'Touren ohne Zuweisung') ? 'T' : ''}}</td>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
<ng-container matColumnDef="expandedDetail">
|
||||
<td mat-cell *matCellDef="let carData" [attr.colspan]="displayedColumns.length">
|
||||
<div class="overflow-hidden flex flex-col items-start pl-0 md:pl-8" [@detailExpand]="carData === expandedCarData ? 'expanded' : 'collapsed'">
|
||||
<div class="flex" *ngFor="let detail of carData.additionalData; index as idx">
|
||||
<div class="pr-2 md:px-4">
|
||||
<span class="text-base font-semibold">{{getSlotTitleByIndex(idx)}}</span>
|
||||
@for (detail of carData.additionalData; track detail; let idx = $index) {
|
||||
<div class="flex">
|
||||
<div class="pr-2 md:px-4">
|
||||
<span class="text-base font-semibold">{{getSlotTitleByIndex(idx)}}</span>
|
||||
</div>
|
||||
<div class="px-2 md:px-4 w-20 md:w-32 flex items-center justify-end">
|
||||
<span class="pr-2 text-base">{{detail.availableTime}}</span><mat-icon title="Verfügbare Zeit">departure_board</mat-icon>
|
||||
</div>
|
||||
<div class="px-2 md:px-4 w-20 md:w-32 flex items-center justify-end">
|
||||
<span class="pr-2 text-base">{{detail.tourAmount}}</span><mat-icon title="Menge Touren">local_shipping</mat-icon>
|
||||
</div>
|
||||
<div class="px-2 md:px-4 w-20 md:w-32 flex items-center justify-end">
|
||||
<span class="pr-2 text-base">{{detail.breakEven}}€</span><mat-icon title="Deckungsbeitrag">account_balance</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-2 md:px-4 w-20 md:w-32 flex items-center justify-end">
|
||||
<span class="pr-2 text-base">{{detail.availableTime}}</span><mat-icon title="Verfügbare Zeit">departure_board</mat-icon>
|
||||
</div>
|
||||
<div class="px-2 md:px-4 w-20 md:w-32 flex items-center justify-end">
|
||||
<span class="pr-2 text-base">{{detail.tourAmount}}</span><mat-icon title="Menge Touren">local_shipping</mat-icon>
|
||||
</div>
|
||||
<div class="px-2 md:px-4 w-20 md:w-32 flex items-center justify-end">
|
||||
<span class="pr-2 text-base">{{detail.breakEven}}€</span><mat-icon title="Deckungsbeitrag">account_balance</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
|
|
|||
|
|
@ -9,31 +9,32 @@
|
|||
<div class="p-1 text-sm" title="Menge Touren"><mat-icon>local_shipping</mat-icon></div>
|
||||
<div class="p-1 text-sm" title="Deckungsbeitrag"><mat-icon>account_balance</mat-icon></div>
|
||||
</div>
|
||||
<div
|
||||
*ngFor="let kpi of kpis; index as idx"
|
||||
class="w-1/3 flex flex-col justify-center items-center"
|
||||
>
|
||||
@for (kpi of kpis; track kpi; let idx = $index) {
|
||||
<div
|
||||
class="font-bold"
|
||||
style="border-color: var(--mat-sys-primary);"
|
||||
[ngClass]="{ 'border-b-2': (currentTimeSlot$ | async) === idx }"
|
||||
>
|
||||
{{ kpi.interval.start | date: 'HH:mm' }} -
|
||||
{{ kpi.interval.end | date: 'HH:mm' }}
|
||||
class="w-1/3 flex flex-col justify-center items-center"
|
||||
>
|
||||
<div
|
||||
class="font-bold"
|
||||
style="border-color: var(--mat-sys-primary);"
|
||||
[ngClass]="{ 'border-b-2': (currentTimeSlot$ | async) === idx }"
|
||||
>
|
||||
{{ kpi.interval.start | date: 'HH:mm' }} -
|
||||
{{ kpi.interval.end | date: 'HH:mm' }}
|
||||
</div>
|
||||
<div class="p-4 text-base">
|
||||
{{ kpi.kpi.value.toString().replace('.', ',') }}
|
||||
</div>
|
||||
<div class="p-2 flex justify-center items-center text-sm">
|
||||
{{ kpi.kpi.meta.availableTime }}
|
||||
</div>
|
||||
<div class="p-2 flex justify-center items-center text-sm">
|
||||
{{ kpi.kpi.meta.tourAmount }}
|
||||
</div>
|
||||
<div class="p-2 flex justify-center items-center text-sm">
|
||||
{{ kpi.kpi.meta.breakEven}}€
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 text-base">
|
||||
{{ kpi.kpi.value.toString().replace('.', ',') }}
|
||||
</div>
|
||||
<div class="p-2 flex justify-center items-center text-sm">
|
||||
{{ kpi.kpi.meta.availableTime }}
|
||||
</div>
|
||||
<div class="p-2 flex justify-center items-center text-sm">
|
||||
{{ kpi.kpi.meta.tourAmount }}
|
||||
</div>
|
||||
<div class="p-2 flex justify-center items-center text-sm">
|
||||
{{ kpi.kpi.meta.breakEven}}€
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
|
|
|||
|
|
@ -1,28 +1,29 @@
|
|||
<ng-container *ngIf="ticket">
|
||||
@if (ticket) {
|
||||
<div class="flex justify-between">
|
||||
<span class="text-base">
|
||||
{{ ticket.tours.at(0)?.ordinanceType }}
|
||||
</span>
|
||||
<div>
|
||||
<mat-icon
|
||||
*ngIf="ticket.errors.length"
|
||||
[matBadge]="ticket.errors.length"
|
||||
matBadgeColor="warn"
|
||||
[title]="ticket.errors.length + ' Anmerkungen'"
|
||||
aria-hidden="false"
|
||||
>
|
||||
error
|
||||
</mat-icon>
|
||||
@if (ticket.errors.length) {
|
||||
<mat-icon
|
||||
[matBadge]="ticket.errors.length"
|
||||
matBadgeColor="warn"
|
||||
[title]="ticket.errors.length + ' Anmerkungen'"
|
||||
aria-hidden="false"
|
||||
>
|
||||
error
|
||||
</mat-icon>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
>{{ ticket.tours.at(0)?.patientName }}
|
||||
{{ ticket.tours.at(0)?.patientSurname }}</span
|
||||
>
|
||||
<span
|
||||
>{{ getToursLength() }} Tour{{
|
||||
>
|
||||
<span
|
||||
>{{ getToursLength() }} Tour{{
|
||||
getToursLength() > 1 ? 'en' : ''
|
||||
}}</span
|
||||
>
|
||||
<span class="text-xs self-end">{{ getDistance(ticket.createdAt) }}</span>
|
||||
</ng-container>
|
||||
}}</span
|
||||
>
|
||||
<span class="text-xs self-end">{{ getDistance(ticket.createdAt) }}</span>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { NgIf } from '@angular/common';
|
||||
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
|
|
@ -32,7 +32,7 @@ import CategoryColors from './category-colors';
|
|||
}
|
||||
`,
|
||||
],
|
||||
imports: [MatIconModule, MatBadgeModule, NgIf],
|
||||
imports: [MatIconModule, MatBadgeModule],
|
||||
})
|
||||
export class TicketItemComponent implements OnInit {
|
||||
@Input() ticket!: Ticket & { tours: Tour[] };
|
||||
|
|
|
|||
|
|
@ -1,31 +1,41 @@
|
|||
import { NgIf } from '@angular/common';
|
||||
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { TicketStateMeta } from '../../../core/data-access/graphql/generated/generated';
|
||||
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'dks-ticket-lane-footer',
|
||||
template: `<span *ngIf="withTicketCount" class="text-left text-xs">
|
||||
{{ ticketStateMeta.count }} Tickets
|
||||
</span>
|
||||
<span *ngIf="withApprovalRequiredCount" class="text-left text-xs">
|
||||
{{ ticketStateMeta.approvalRequiredCount }} davon genehmigungspflichtig
|
||||
</span>
|
||||
<span *ngIf="withApprovalUnknownCount" class="text-left text-xs">
|
||||
{{ ticketStateMeta.approvalUnknownCount }} davon auf Genehmigungspflicht
|
||||
zu prüfen
|
||||
</span>
|
||||
<span *ngIf="withApprovalNotCompanyCount" class="text-left text-xs">
|
||||
{{ ticketStateMeta.approvalNotCompanyCount }} davon fehlende Genehmigung
|
||||
</span>
|
||||
<span *ngIf="withTdNotCompanyCount" class="text-left text-xs">
|
||||
{{ ticketStateMeta.tdNotCompanyCount }} davon fehlender Transportschein
|
||||
</span> `,
|
||||
template: `@if (withTicketCount) {
|
||||
<span class="text-left text-xs">
|
||||
{{ ticketStateMeta.count }} Tickets
|
||||
</span>
|
||||
}
|
||||
@if (withApprovalRequiredCount) {
|
||||
<span class="text-left text-xs">
|
||||
{{ ticketStateMeta.approvalRequiredCount }} davon genehmigungspflichtig
|
||||
</span>
|
||||
}
|
||||
@if (withApprovalUnknownCount) {
|
||||
<span class="text-left text-xs">
|
||||
{{ ticketStateMeta.approvalUnknownCount }} davon auf Genehmigungspflicht
|
||||
zu prüfen
|
||||
</span>
|
||||
}
|
||||
@if (withApprovalNotCompanyCount) {
|
||||
<span class="text-left text-xs">
|
||||
{{ ticketStateMeta.approvalNotCompanyCount }} davon fehlende Genehmigung
|
||||
</span>
|
||||
}
|
||||
@if (withTdNotCompanyCount) {
|
||||
<span class="text-left text-xs">
|
||||
{{ ticketStateMeta.tdNotCompanyCount }} davon fehlender Transportschein
|
||||
</span>
|
||||
}`,
|
||||
host: {
|
||||
class: 'flex flex-col items-center justify-center p-2',
|
||||
},
|
||||
styles: [``],
|
||||
imports: [NgIf],
|
||||
imports: [],
|
||||
})
|
||||
export class TicketLaneFooterComponent {
|
||||
@Input() ticketStateMeta!: TicketStateMeta;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { NgFor, NgSwitch, NgSwitchCase } from '@angular/common';
|
||||
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
|
|
@ -24,92 +24,96 @@ import {
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'dks-ticket-lane-header-filter',
|
||||
template: `
|
||||
<ng-container [ngSwitch]="state">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="createdMenu"
|
||||
aria-label="Touren angenommen Filter button"
|
||||
*ngSwitchCase="TicketValidationState.Created"
|
||||
@switch (state) {
|
||||
@case (TicketValidationState.Created) {
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="createdMenu"
|
||||
aria-label="Touren angenommen Filter button"
|
||||
>
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="userInteractionMenu"
|
||||
aria-label="Touren angenommen Filter button"
|
||||
*ngSwitchCase="TicketValidationState.UserInteractionRequired"
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
}
|
||||
@case (TicketValidationState.UserInteractionRequired) {
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="userInteractionMenu"
|
||||
aria-label="Touren angenommen Filter button"
|
||||
>
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="tdUncertainMenu"
|
||||
aria-label="Touren angenommen Filter button"
|
||||
*ngSwitchCase="TicketValidationState.TdUncertain"
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
}
|
||||
@case (TicketValidationState.TdUncertain) {
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="tdUncertainMenu"
|
||||
aria-label="Touren angenommen Filter button"
|
||||
>
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="documentsMenu"
|
||||
aria-label="Touren angenommen Filter button"
|
||||
*ngSwitchCase="TicketValidationState.DocumentsMissing"
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
}
|
||||
@case (TicketValidationState.DocumentsMissing) {
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="documentsMenu"
|
||||
aria-label="Touren angenommen Filter button"
|
||||
>
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
</ng-container>
|
||||
<form #form="ngForm">
|
||||
<mat-menu #createdMenu="matMenu">
|
||||
<section
|
||||
mat-menu-item
|
||||
(click)="$event.stopPropagation()"
|
||||
*ngFor="let item of filter.errors"
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
<form #form="ngForm">
|
||||
<mat-menu #createdMenu="matMenu">
|
||||
@for (item of filter.errors; track item) {
|
||||
<section
|
||||
mat-menu-item
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
<mat-checkbox [(ngModel)]="item.checked" [name]="item.value">{{
|
||||
item.text
|
||||
}}</mat-checkbox>
|
||||
</section>
|
||||
</mat-menu>
|
||||
<mat-menu #userInteractionMenu="matMenu">
|
||||
<section
|
||||
mat-menu-item
|
||||
(click)="$event.stopPropagation()"
|
||||
*ngFor="let item of filter.approvalStates"
|
||||
<mat-checkbox [(ngModel)]="item.checked" [name]="item.value">{{
|
||||
item.text
|
||||
}}</mat-checkbox>
|
||||
</section>
|
||||
}
|
||||
</mat-menu>
|
||||
<mat-menu #userInteractionMenu="matMenu">
|
||||
@for (item of filter.approvalStates; track item) {
|
||||
<section
|
||||
mat-menu-item
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
<mat-checkbox [(ngModel)]="item.checked" [name]="item.value">{{
|
||||
item.text
|
||||
}}</mat-checkbox>
|
||||
</section>
|
||||
</mat-menu>
|
||||
<mat-menu #tdUncertainMenu="matMenu">
|
||||
<section mat-menu-item>
|
||||
<mat-checkbox checked="true" disabled="true">Alle</mat-checkbox>
|
||||
</section>
|
||||
</mat-menu>
|
||||
<mat-menu #documentsMenu="matMenu">
|
||||
<section
|
||||
mat-menu-item
|
||||
(click)="$event.stopPropagation()"
|
||||
*ngFor="let item of filter.documentInfo"
|
||||
<mat-checkbox [(ngModel)]="item.checked" [name]="item.value">{{
|
||||
item.text
|
||||
}}</mat-checkbox>
|
||||
</section>
|
||||
}
|
||||
</mat-menu>
|
||||
<mat-menu #tdUncertainMenu="matMenu">
|
||||
<section mat-menu-item>
|
||||
<mat-checkbox checked="true" disabled="true">Alle</mat-checkbox>
|
||||
</section>
|
||||
</mat-menu>
|
||||
<mat-menu #documentsMenu="matMenu">
|
||||
@for (item of filter.documentInfo; track item) {
|
||||
<section
|
||||
mat-menu-item
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
<mat-checkbox [(ngModel)]="item.checked" [name]="item.value">{{
|
||||
item.text
|
||||
}}</mat-checkbox>
|
||||
</section>
|
||||
</mat-menu>
|
||||
</form>
|
||||
`,
|
||||
<mat-checkbox [(ngModel)]="item.checked" [name]="item.value">{{
|
||||
item.text
|
||||
}}</mat-checkbox>
|
||||
</section>
|
||||
}
|
||||
</mat-menu>
|
||||
</form>
|
||||
`,
|
||||
styles: [],
|
||||
imports: [
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgFor,
|
||||
FormsModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
]
|
||||
FormsModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule
|
||||
]
|
||||
})
|
||||
export class TicketLaneHeaderFilterComponent implements AfterViewInit {
|
||||
@Input() state!: TicketValidationState;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { NgSwitch, NgSwitchCase } from '@angular/common';
|
||||
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
|
|
@ -16,23 +16,33 @@ import { TicketLaneHeaderFilterComponent } from './ticket-lane-header-filter.com
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'dks-ticket-lane-header',
|
||||
template: `<div></div>
|
||||
<ng-container [ngSwitch]="state">
|
||||
<div *ngSwitchCase="TicketValidationState.Created">Touren angenommen</div>
|
||||
<div *ngSwitchCase="TicketValidationState.UserInteractionRequired">
|
||||
Genehmigung/Transportschein
|
||||
</div>
|
||||
<div *ngSwitchCase="TicketValidationState.TdUncertain">
|
||||
Transportschein unklar
|
||||
</div>
|
||||
<div *ngSwitchCase="TicketValidationState.DocumentsMissing">
|
||||
Dokumente fehlen
|
||||
</div>
|
||||
<div *ngSwitchCase="TicketValidationState.Archived">Archiv</div>
|
||||
</ng-container>
|
||||
<dks-ticket-lane-header-filter
|
||||
[state]="state"
|
||||
(filterChange)="filterChanged($event)"
|
||||
></dks-ticket-lane-header-filter>`,
|
||||
@switch (state) {
|
||||
@case (TicketValidationState.Created) {
|
||||
<div>Touren angenommen</div>
|
||||
}
|
||||
@case (TicketValidationState.UserInteractionRequired) {
|
||||
<div>
|
||||
Genehmigung/Transportschein
|
||||
</div>
|
||||
}
|
||||
@case (TicketValidationState.TdUncertain) {
|
||||
<div>
|
||||
Transportschein unklar
|
||||
</div>
|
||||
}
|
||||
@case (TicketValidationState.DocumentsMissing) {
|
||||
<div>
|
||||
Dokumente fehlen
|
||||
</div>
|
||||
}
|
||||
@case (TicketValidationState.Archived) {
|
||||
<div>Archiv</div>
|
||||
}
|
||||
}
|
||||
<dks-ticket-lane-header-filter
|
||||
[state]="state"
|
||||
(filterChange)="filterChanged($event)"
|
||||
></dks-ticket-lane-header-filter>`,
|
||||
host: {
|
||||
class: 'flex items-center justify-between p-2',
|
||||
},
|
||||
|
|
@ -44,7 +54,7 @@ import { TicketLaneHeaderFilterComponent } from './ticket-lane-header-filter.com
|
|||
}
|
||||
`,
|
||||
],
|
||||
imports: [NgSwitch, NgSwitchCase, TicketLaneHeaderFilterComponent]
|
||||
imports: [TicketLaneHeaderFilterComponent]
|
||||
})
|
||||
export class TicketLaneHeaderComponent {
|
||||
@Input() state!: TicketValidationState;
|
||||
|
|
|
|||
|
|
@ -3,34 +3,34 @@
|
|||
[state]="ticketState"
|
||||
(filterChange)="filterChanged($event)"
|
||||
></dks-ticket-lane-header>
|
||||
<ng-container *ngIf="tickets$ | async as tickets">
|
||||
@if (tickets$ | async; as tickets) {
|
||||
<cdk-virtual-scroll-viewport
|
||||
itemSize="104"
|
||||
(scrolledIndexChange)="fetchNextPage()"
|
||||
>
|
||||
>
|
||||
<dks-ticket-item
|
||||
*cdkVirtualFor="let ticket of tickets; trackBy: trackByFn"
|
||||
class="mat-elevation-z4 animate-fadeIn"
|
||||
(click)="openTicketView(ticket)"
|
||||
[ticket]="ticket"
|
||||
>
|
||||
>
|
||||
</dks-ticket-item>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<!-- <ng-container *ngIf="ticketStateMeta$ | async as ticketStateMeta">
|
||||
<dks-ticket-lane-footer
|
||||
*dksRole="['manager', 'admin']"
|
||||
[ticketStateMeta]="ticketStateMeta"
|
||||
[withTicketCount]="true"
|
||||
<dks-ticket-lane-footer
|
||||
*dksRole="['manager', 'admin']"
|
||||
[ticketStateMeta]="ticketStateMeta"
|
||||
[withTicketCount]="true"
|
||||
[withApprovalRequiredCount]="
|
||||
ticketState === TicketState.UserInteractionRequired
|
||||
"
|
||||
[withApprovalUnknownCount]="
|
||||
ticketState === TicketState.UserInteractionRequired
|
||||
"
|
||||
></dks-ticket-lane-footer>
|
||||
<dks-ticket-lane-footer
|
||||
*dksRole="['controlcenter']"
|
||||
[ticketStateMeta]="ticketStateMeta"
|
||||
></dks-ticket-lane-footer>
|
||||
<dks-ticket-lane-footer
|
||||
*dksRole="['controlcenter']"
|
||||
[ticketStateMeta]="ticketStateMeta"
|
||||
></dks-ticket-lane-footer
|
||||
></ng-container> -->
|
||||
</ng-container>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import {
|
|||
CdkVirtualScrollViewport,
|
||||
ScrollingModule,
|
||||
} from '@angular/cdk/scrolling';
|
||||
import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
|
|
@ -51,10 +51,9 @@ import { TicketLaneHeaderComponent } from './ticket-lane-header.component';
|
|||
imports: [
|
||||
TicketLaneHeaderComponent,
|
||||
TicketItemComponent,
|
||||
NgIf,
|
||||
AsyncPipe,
|
||||
ScrollingModule,
|
||||
],
|
||||
ScrollingModule
|
||||
],
|
||||
})
|
||||
export class TicketLaneComponent {
|
||||
private dialog = inject(DialogService);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { NgIf } from '@angular/common';
|
||||
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
|
|
@ -23,45 +23,45 @@ import {
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'dks-ticket-documents-missing',
|
||||
template: `<div class="flex items-center p-3">
|
||||
<form #form="ngForm" class="flex flex-col w-full">
|
||||
<div class="flex justify-end pt-4">
|
||||
<button mat-stroked-button class="mr-2" (click)="form.resetForm()">
|
||||
Zurücksetzen
|
||||
</button>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
[disabled]="form.invalid"
|
||||
(click)="saveForm()"
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col p-2">
|
||||
<label>Alles vorhanden?</label>
|
||||
<mat-checkbox
|
||||
*ngIf="ticket.approvalState !== ApprovalState.Free"
|
||||
[(ngModel)]="formModel.approval.location"
|
||||
name="approval"
|
||||
>Genehmigung</mat-checkbox
|
||||
>
|
||||
<mat-checkbox
|
||||
[(ngModel)]="formModel.tdLocation"
|
||||
name="transport_document"
|
||||
>Transportschein</mat-checkbox
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>`,
|
||||
<form #form="ngForm" class="flex flex-col w-full">
|
||||
<div class="flex justify-end pt-4">
|
||||
<button mat-stroked-button class="mr-2" (click)="form.resetForm()">
|
||||
Zurücksetzen
|
||||
</button>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
[disabled]="form.invalid"
|
||||
(click)="saveForm()"
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col p-2">
|
||||
<label>Alles vorhanden?</label>
|
||||
@if (ticket.approvalState !== ApprovalState.Free) {
|
||||
<mat-checkbox
|
||||
[(ngModel)]="formModel.approval.location"
|
||||
name="approval"
|
||||
>Genehmigung</mat-checkbox
|
||||
>
|
||||
}
|
||||
<mat-checkbox
|
||||
[(ngModel)]="formModel.tdLocation"
|
||||
name="transport_document"
|
||||
>Transportschein</mat-checkbox
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>`,
|
||||
imports: [
|
||||
NgIf,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatFormFieldModule,
|
||||
MatRadioModule,
|
||||
MatCheckboxModule,
|
||||
FormsModule,
|
||||
]
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatFormFieldModule,
|
||||
MatRadioModule,
|
||||
MatCheckboxModule,
|
||||
FormsModule
|
||||
]
|
||||
})
|
||||
export class TicketDocumentsMissingComponent {
|
||||
@Input() ticket!: Ticket & { tours: Tour[] };
|
||||
|
|
|
|||
|
|
@ -1,10 +1,4 @@
|
|||
import {
|
||||
NgClass,
|
||||
NgForOf,
|
||||
NgIf,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
} from '@angular/common';
|
||||
import { NgClass } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
|
|
@ -18,79 +12,89 @@ import {
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'dks-ticket-overview',
|
||||
template: `<div class="grid grid-cols-4">
|
||||
<mat-list class="col-span-2" role="list">
|
||||
<div mat-subheader class="text-sm">Ticket Informationen</div>
|
||||
<mat-list-item role="listitem">
|
||||
Typ:
|
||||
<span class="font-bold">
|
||||
{{ ticket.tours[0].type }}
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item role="listitem">
|
||||
Kategorie:
|
||||
<span class="font-bold">
|
||||
{{ ticket.tours[0].category }}
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
<mat-list class="col-span-2" role="list" *ngIf="ticket.approvalState">
|
||||
<div mat-subheader class="text-sm">Gehnemigungsinformationen</div>
|
||||
<mat-list-item role="listitem">
|
||||
<ng-container [ngSwitch]="ticket.approvalState">
|
||||
<span *ngSwitchCase="ApprovalState.Free">Genehmigungsfrei</span>
|
||||
<span
|
||||
*ngSwitchCase="ApprovalState.Unknown"
|
||||
title="Automatische Prüfung nicht möglich"
|
||||
>Automatische Prüfung nicht möglich</span
|
||||
>
|
||||
<span *ngSwitchCase="ApprovalState.Required"
|
||||
>Genehmigung benötigt</span
|
||||
>
|
||||
<span *ngSwitchCase="ApprovalState.Requested"
|
||||
>Genehmigung angefragt</span
|
||||
>
|
||||
<span *ngSwitchCase="ApprovalState.Approved"
|
||||
>Genehmigung eingegangen</span
|
||||
>
|
||||
</ng-container>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
<mat-list
|
||||
class="col-span-2 max-h-96 overflow-y-auto"
|
||||
*ngIf="ticket.errors.length > 0 || ticket.notes.length > 0"
|
||||
role="list"
|
||||
>
|
||||
<div mat-subheader class="text-sm">Ticket Anmerkungen</div>
|
||||
<mat-list-item
|
||||
*ngFor="let error of ticket.errors"
|
||||
lines="2"
|
||||
role="listitem"
|
||||
>
|
||||
<mat-icon matListItemIcon class="!text-red-600">error</mat-icon>
|
||||
<span matListItemTitle>{{ error.split(':').at(0) }}</span>
|
||||
<span [title]="error.split(':').at(1)">{{
|
||||
error.split(':').at(1)
|
||||
}}</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item
|
||||
*ngFor="let note of ticket.notes"
|
||||
lines="2"
|
||||
role="listitem"
|
||||
>
|
||||
<mat-icon matListItemIcon class="!text-yellow-400">warning</mat-icon>
|
||||
<span matListItemTitle>{{ note.split(':').at(0) }}</span>
|
||||
<span [title]="note.split(':').at(1)">{{ note.split(':').at(1) }}</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>`,
|
||||
<mat-list class="col-span-2" role="list">
|
||||
<div mat-subheader class="text-sm">Ticket Informationen</div>
|
||||
<mat-list-item role="listitem">
|
||||
Typ:
|
||||
<span class="font-bold">
|
||||
{{ ticket.tours[0].type }}
|
||||
</span>
|
||||
</mat-list-item>
|
||||
<mat-list-item role="listitem">
|
||||
Kategorie:
|
||||
<span class="font-bold">
|
||||
{{ ticket.tours[0].category }}
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
@if (ticket.approvalState) {
|
||||
<mat-list class="col-span-2" role="list">
|
||||
<div mat-subheader class="text-sm">Gehnemigungsinformationen</div>
|
||||
<mat-list-item role="listitem">
|
||||
@switch (ticket.approvalState) {
|
||||
@case (ApprovalState.Free) {
|
||||
<span>Genehmigungsfrei</span>
|
||||
}
|
||||
@case (ApprovalState.Unknown) {
|
||||
<span
|
||||
title="Automatische Prüfung nicht möglich"
|
||||
>Automatische Prüfung nicht möglich</span
|
||||
>
|
||||
}
|
||||
@case (ApprovalState.Required) {
|
||||
<span
|
||||
>Genehmigung benötigt</span
|
||||
>
|
||||
}
|
||||
@case (ApprovalState.Requested) {
|
||||
<span
|
||||
>Genehmigung angefragt</span
|
||||
>
|
||||
}
|
||||
@case (ApprovalState.Approved) {
|
||||
<span
|
||||
>Genehmigung eingegangen</span
|
||||
>
|
||||
}
|
||||
}
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
}
|
||||
@if (ticket.errors.length > 0 || ticket.notes.length > 0) {
|
||||
<mat-list
|
||||
class="col-span-2 max-h-96 overflow-y-auto"
|
||||
role="list"
|
||||
>
|
||||
<div mat-subheader class="text-sm">Ticket Anmerkungen</div>
|
||||
@for (error of ticket.errors; track error) {
|
||||
<mat-list-item
|
||||
lines="2"
|
||||
role="listitem"
|
||||
>
|
||||
<mat-icon matListItemIcon class="!text-red-600">error</mat-icon>
|
||||
<span matListItemTitle>{{ error.split(':').at(0) }}</span>
|
||||
<span [title]="error.split(':').at(1)">{{
|
||||
error.split(':').at(1)
|
||||
}}</span>
|
||||
</mat-list-item>
|
||||
}
|
||||
@for (note of ticket.notes; track note) {
|
||||
<mat-list-item
|
||||
lines="2"
|
||||
role="listitem"
|
||||
>
|
||||
<mat-icon matListItemIcon class="!text-yellow-400">warning</mat-icon>
|
||||
<span matListItemTitle>{{ note.split(':').at(0) }}</span>
|
||||
<span [title]="note.split(':').at(1)">{{ note.split(':').at(1) }}</span>
|
||||
</mat-list-item>
|
||||
}
|
||||
</mat-list>
|
||||
}
|
||||
</div>`,
|
||||
imports: [
|
||||
MatListModule,
|
||||
MatIconModule,
|
||||
NgIf,
|
||||
NgForOf,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
]
|
||||
MatListModule,
|
||||
MatIconModule
|
||||
]
|
||||
})
|
||||
export class TicketOverviewComponent {
|
||||
@Input() ticket!: Ticket & { tours: Tour[] };
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { NgIf } from '@angular/common';
|
||||
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { DatePipe, NgClass, NgIf } from '@angular/common';
|
||||
import { DatePipe, NgClass } from '@angular/common';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { NgIf } from '@angular/common';
|
||||
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
|
|
@ -32,92 +32,88 @@ import { AuthService } from '../../../core/components/ms-authentication/auth.ser
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'dks-ticket-user-action-required',
|
||||
template: `<div class="flex flex-col p-3">
|
||||
<form #form="ngForm" class="flex flex-col w-full">
|
||||
<div class="flex justify-end pt-4">
|
||||
<button mat-stroked-button class="mr-2" (click)="form.resetForm()">
|
||||
Zurücksetzen
|
||||
</button>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
[disabled]="form.invalid"
|
||||
(click)="saveForm()"
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col p-2">
|
||||
<label>Wird eine Genehmigung benötigt?</label>
|
||||
<mat-radio-group
|
||||
[(ngModel)]="formModel().approval.isNeeded"
|
||||
name="approvalNeeded"
|
||||
required
|
||||
>
|
||||
<mat-radio-button [value]="false"> Nein </mat-radio-button>
|
||||
<mat-radio-button [value]="true"> Ja </mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
<ng-container
|
||||
*ngIf="
|
||||
(ticket.approvalState === ApprovalState.Required &&
|
||||
formModel().approval.isNeeded) ||
|
||||
(ticket.approvalState === ApprovalState.Unknown &&
|
||||
formModel().approval.isNeeded)
|
||||
"
|
||||
>
|
||||
<form #form="ngForm" class="flex flex-col w-full">
|
||||
<div class="flex justify-end pt-4">
|
||||
<button mat-stroked-button class="mr-2" (click)="form.resetForm()">
|
||||
Zurücksetzen
|
||||
</button>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
[disabled]="form.invalid"
|
||||
(click)="saveForm()"
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col p-2">
|
||||
<label>Wer fragte die Genehmigung an?</label>
|
||||
<label>Wird eine Genehmigung benötigt?</label>
|
||||
<mat-radio-group
|
||||
[(ngModel)]="formModel().approval.whoRequested"
|
||||
name="who"
|
||||
[(ngModel)]="formModel().approval.isNeeded"
|
||||
name="approvalNeeded"
|
||||
required
|
||||
>
|
||||
<mat-radio-button value="Avicenna"> Avicenna </mat-radio-button>
|
||||
<mat-radio-button value="Kunde"> Kunde </mat-radio-button>
|
||||
>
|
||||
<mat-radio-button [value]="false"> Nein </mat-radio-button>
|
||||
<mat-radio-button [value]="true"> Ja </mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
@if (
|
||||
(ticket.approvalState === ApprovalState.Required &&
|
||||
formModel().approval.isNeeded) ||
|
||||
(ticket.approvalState === ApprovalState.Unknown &&
|
||||
formModel().approval.isNeeded)
|
||||
) {
|
||||
<div class="flex flex-col p-2">
|
||||
<label>Wer fragte die Genehmigung an?</label>
|
||||
<mat-radio-group
|
||||
[(ngModel)]="formModel().approval.whoRequested"
|
||||
name="who"
|
||||
required
|
||||
>
|
||||
<mat-radio-button value="Avicenna"> Avicenna </mat-radio-button>
|
||||
<mat-radio-button value="Kunde"> Kunde </mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
<mat-form-field class="p-2">
|
||||
<mat-label>Name des Sachbearbeiters?</mat-label>
|
||||
<input
|
||||
type="text"
|
||||
matInput
|
||||
id="staff"
|
||||
[(ngModel)]="formModel().approval.answeredFrom"
|
||||
name="staff"
|
||||
required
|
||||
/>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
<mat-form-field class="p-2">
|
||||
<mat-label>Name des Sachbearbeiters?</mat-label>
|
||||
<input
|
||||
type="text"
|
||||
matInput
|
||||
id="staff"
|
||||
[(ngModel)]="formModel().approval.answeredFrom"
|
||||
name="staff"
|
||||
<div class="flex flex-col p-2">
|
||||
<label>Wo befindet sich der Transportschein?</label>
|
||||
<mat-radio-group
|
||||
[(ngModel)]="formModel().tdLocation"
|
||||
name="tdLocation"
|
||||
required
|
||||
/>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
|
||||
<div class="flex flex-col p-2">
|
||||
<label>Wo befindet sich der Transportschein?</label>
|
||||
<mat-radio-group
|
||||
[(ngModel)]="formModel().tdLocation"
|
||||
name="tdLocation"
|
||||
required
|
||||
>
|
||||
<mat-radio-button [value]="'company'"> Im Betrieb </mat-radio-button>
|
||||
<mat-radio-button [value]="'practice'">
|
||||
In der Praxis
|
||||
</mat-radio-button>
|
||||
<mat-radio-button [value]="'customer'">
|
||||
Beim Kunden
|
||||
</mat-radio-button>
|
||||
<mat-radio-button [value]="'uncertain'"> Unklar </mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</form>
|
||||
</div>`,
|
||||
>
|
||||
<mat-radio-button [value]="'company'"> Im Betrieb </mat-radio-button>
|
||||
<mat-radio-button [value]="'practice'">
|
||||
In der Praxis
|
||||
</mat-radio-button>
|
||||
<mat-radio-button [value]="'customer'">
|
||||
Beim Kunden
|
||||
</mat-radio-button>
|
||||
<mat-radio-button [value]="'uncertain'"> Unklar </mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</form>
|
||||
</div>`,
|
||||
imports: [
|
||||
NgIf,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatFormFieldModule,
|
||||
MatRadioModule,
|
||||
MatInputModule,
|
||||
FormsModule,
|
||||
],
|
||||
FormsModule
|
||||
],
|
||||
})
|
||||
export class TicketUserActionRequiredComponent implements OnInit {
|
||||
@Input() ticket!: Ticket & { tours: Tour[] };
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { AsyncPipe, NgFor, NgIf } from '@angular/common';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
|
|
@ -20,50 +20,50 @@ import { TicketsService } from '../tickets.service';
|
|||
|
||||
@Component({
|
||||
template: `<div class="flex flex-col items-center justify-start p-4">
|
||||
<div class="text-lg font-bold pb-2">Ticket suchen</div>
|
||||
<hr />
|
||||
<mat-form-field class="w-full">
|
||||
<mat-label>Einsatznummer oder Patient</mat-label>
|
||||
<input class="w-full" matInput [formControl]="searchCtrl" name="search" />
|
||||
</mat-form-field>
|
||||
<hr />
|
||||
<div class="w-full">
|
||||
<div
|
||||
class="flex flex-col justify-between items-center py-2 border-b border-gray-200 cursor-pointer"
|
||||
*ngFor="let ticket of tickets$ | async"
|
||||
(click)="showTicketView(ticket)"
|
||||
>
|
||||
<div class="w-full">
|
||||
{{ ticket.tours.at(0)?.ordinanceType }}
|
||||
<span
|
||||
*ngIf="ticket.tours.at(0)?.category"
|
||||
class="text-sm text-gray-600"
|
||||
>
|
||||
({{ ticket.tours.at(0)?.category }})
|
||||
</span>
|
||||
</div>
|
||||
<div class="w-full flex justify-between">
|
||||
<span>
|
||||
{{ ticket.tours.at(0)?.patientName }}
|
||||
{{ ticket.tours.at(0)?.patientSurname }}
|
||||
</span>
|
||||
<span>
|
||||
{{ ticket.tours.length }} Tour{{
|
||||
ticket.tours.length > 1 ? 'en' : ''
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-lg font-bold pb-2">Ticket suchen</div>
|
||||
<hr />
|
||||
<mat-form-field class="w-full">
|
||||
<mat-label>Einsatznummer oder Patient</mat-label>
|
||||
<input class="w-full" matInput [formControl]="searchCtrl" name="search" />
|
||||
</mat-form-field>
|
||||
<hr />
|
||||
<div class="w-full">
|
||||
@for (ticket of tickets$ | async; track ticket) {
|
||||
<div
|
||||
class="flex flex-col justify-between items-center py-2 border-b border-gray-200 cursor-pointer"
|
||||
(click)="showTicketView(ticket)"
|
||||
>
|
||||
<div class="w-full">
|
||||
{{ ticket.tours.at(0)?.ordinanceType }}
|
||||
@if (ticket.tours.at(0)?.category) {
|
||||
<span
|
||||
class="text-sm text-gray-600"
|
||||
>
|
||||
({{ ticket.tours.at(0)?.category }})
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<div class="w-full flex justify-between">
|
||||
<span>
|
||||
{{ ticket.tours.at(0)?.patientName }}
|
||||
{{ ticket.tours.at(0)?.patientSurname }}
|
||||
</span>
|
||||
<span>
|
||||
{{ ticket.tours.length }} Tour{{
|
||||
ticket.tours.length > 1 ? 'en' : ''
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>`,
|
||||
</div>`,
|
||||
imports: [
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
AsyncPipe,
|
||||
NgFor,
|
||||
NgIf,
|
||||
],
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
AsyncPipe
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class TicketsSearchDialogComponent {
|
||||
|
|
|
|||
Loading…
Reference in New Issue