feat: add associated entities to anomalies

This commit is contained in:
Marcel Arndt
2025-06-26 09:04:53 +02:00
parent b31df96a44
commit d9b615fd47
38 changed files with 1094 additions and 133 deletions
@@ -3,19 +3,17 @@ import {
ErrorHandler,
importProvidersFrom,
provideZoneChangeDetection,
provideAppInitializer,
} from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { routes } from './app.routes';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { MsAuthenticationModule } from './core/components/ms-authentication/ms-authentication.module';
import { environment } from '../environments/environment';
import { provideHttpClient } from '@angular/common/http';
import { graphqlProvider } from './graphql.provider';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideHotToastConfig } from '@ngxpert/hot-toast';
import { faroInitializer } from './core/telemetry/faro-initializer';
import { environment } from '../environments/environment';
import { routes } from './app.routes';
import { MsAuthenticationModule } from './core/components/ms-authentication/ms-authentication.module';
import { GlobalErrorHandler } from './core/telemetry/global-error-handler';
import { graphqlProvider } from './graphql.provider';
export const appConfig: ApplicationConfig = {
providers: [
@@ -28,7 +26,7 @@ export const appConfig: ApplicationConfig = {
useClass: GlobalErrorHandler,
},
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideRouter(routes, withComponentInputBinding()),
provideAnimationsAsync(),
importProvidersFrom(
MsAuthenticationModule.forRoot({
@@ -42,7 +42,6 @@ export type Anomaly = {
sleepSince?: Maybe<Scalars['DateTime']['output']>;
sleepTimer: Scalars['Int']['output'];
solution: Array<SolutionTarget>;
tour?: Maybe<Tour>;
type: Scalars['String']['output'];
workedOnBy: Scalars['String']['output'];
};
@@ -76,12 +75,41 @@ export enum ApprovalState {
Unknown = 'UNKNOWN'
}
export type AssociatedEntities = {
__typename?: 'AssociatedEntities';
attendanceRegistration?: Maybe<Array<AttendanceRegistration>>;
employee?: Maybe<Array<Employee>>;
plannedTime?: Maybe<Array<PlannedTime>>;
tour?: Maybe<Array<Tour>>;
workTime?: Maybe<Array<WorkTime>>;
};
export type AttendanceRegistration = {
__typename?: 'AttendanceRegistration';
attendanceRegistrationId: Scalars['Int']['output'];
dateTime: Scalars['DateTime']['output'];
employeeId: Scalars['Int']['output'];
event: Scalars['String']['output'];
personnelNumber: Scalars['String']['output'];
};
export type DocumentInfo = {
__typename?: 'DocumentInfo';
approval: ApprovalInfo;
tdLocation?: Maybe<Scalars['String']['output']>;
};
export type Employee = {
__typename?: 'Employee';
id: Scalars['String']['output'];
name: Scalars['String']['output'];
occuptationalHealthExamination?: Maybe<Scalars['DateTime']['output']>;
personTransportCertificate?: Maybe<Scalars['DateTime']['output']>;
personnelNumber: Scalars['String']['output'];
qualification?: Maybe<Scalars['String']['output']>;
surname: Scalars['String']['output'];
};
export type HomeDashboardKpIs = {
__typename?: 'HomeDashboardKPIs';
anomalyCount: Scalars['Int']['output'];
@@ -194,6 +222,20 @@ export type PaginatedTour = {
totalCount: Scalars['Int']['output'];
};
export type PlannedTime = {
__typename?: 'PlannedTime';
department: Scalars['String']['output'];
duration: Scalars['Int']['output'];
endDate: Scalars['DateTime']['output'];
firstname: Scalars['String']['output'];
id: Scalars['String']['output'];
note?: Maybe<Scalars['String']['output']>;
pause: Scalars['Int']['output'];
startDate: Scalars['DateTime']['output'];
surname: Scalars['String']['output'];
userId: Scalars['String']['output'];
};
export type Query = {
__typename?: 'Query';
accountingStateMeta: TourStateMeta;
@@ -201,6 +243,7 @@ export type Query = {
anomalies: PaginatedAnomaly;
anomaliesProto: Array<Anomaly>;
anomaly: Anomaly;
associatedEntities: AssociatedEntities;
groupedAnomalies: PaginatedAnomalyGroup;
homeDashboardKpis: HomeDashboardKpIs;
managerKpi: KpiInfo;
@@ -242,6 +285,11 @@ export type QueryAnomalyArgs = {
};
export type QueryAssociatedEntitiesArgs = {
id: Scalars['String']['input'];
};
export type QueryGroupedAnomaliesArgs = {
cursor?: InputMaybe<Scalars['String']['input']>;
take?: Scalars['Int']['input'];
@@ -492,8 +540,28 @@ export type TourStateMeta = {
revenueSum?: Maybe<Scalars['Float']['output']>;
};
export type WorkTime = {
__typename?: 'WorkTime';
department: Scalars['String']['output'];
duration: Scalars['Int']['output'];
endDate: Scalars['DateTime']['output'];
firstname: Scalars['String']['output'];
id: Scalars['String']['output'];
pause: Scalars['Int']['output'];
startDate: Scalars['DateTime']['output'];
surname: Scalars['String']['output'];
userId: Scalars['String']['output'];
};
export type AnomalyAllFragment = { __typename?: 'Anomaly', id: string, type: string, description: string, groupKey: string, groupDescription: string, sleepTimer: number, sleepSince?: any | null, workedOnBy: string, resolvedAt?: any | null, createdAt: any, solution: Array<{ __typename?: 'SolutionTarget', anomalyId: string, id: string, name: string, steps: Array<{ __typename?: 'SolutionTargetStep', description: string, id: string, solutionTargetId: string }> }> };
export type GetAnomalyQueryVariables = Exact<{
id: Scalars['String']['input'];
}>;
export type GetAnomalyQuery = { __typename?: 'Query', anomaly: { __typename?: 'Anomaly', id: string, type: string, description: string, groupKey: string, groupDescription: string, sleepTimer: number, sleepSince?: any | null, workedOnBy: string, resolvedAt?: any | null, createdAt: any, solution: Array<{ __typename?: 'SolutionTarget', anomalyId: string, id: string, name: string, steps: Array<{ __typename?: 'SolutionTargetStep', description: string, id: string, solutionTargetId: string }> }> } };
export type GetAnomaliesQueryVariables = Exact<{
cursor?: InputMaybe<Scalars['String']['input']>;
take: Scalars['Int']['input'];
@@ -524,6 +592,23 @@ export type AcceptAnomalyMutationVariables = Exact<{
export type AcceptAnomalyMutation = { __typename?: 'Mutation', acceptAnomaly: { __typename?: 'Anomaly', id: string, type: string, description: string, groupKey: string, groupDescription: string, sleepTimer: number, sleepSince?: any | null, workedOnBy: string, resolvedAt?: any | null, createdAt: any, solution: Array<{ __typename?: 'SolutionTarget', anomalyId: string, id: string, name: string, steps: Array<{ __typename?: 'SolutionTargetStep', description: string, id: string, solutionTargetId: string }> }> } };
export type EmployeeAllFragment = { __typename?: 'Employee', name: string, id: string, surname: string, personnelNumber: string, qualification?: string | null, occuptationalHealthExamination?: any | null, personTransportCertificate?: any | null };
export type PlannedTimeAllFragment = { __typename?: 'PlannedTime', id: string, userId: string, firstname: string, surname: string, startDate: any, endDate: any, department: string, pause: number, duration: number, note?: string | null };
export type WorkTimeAllFragment = { __typename?: 'WorkTime', id: string, userId: string, firstname: string, surname: string, startDate: any, endDate: any, department: string, pause: number, duration: number };
export type AttendanceRegistrationAllFragment = { __typename?: 'AttendanceRegistration', personnelNumber: string, attendanceRegistrationId: number, employeeId: number, dateTime: any, event: string };
export type TourAllFragment = { __typename?: 'Tour', billDate?: any | null, billNumber?: string | null, carName?: string | null, category?: string | null, check: number, codriverId?: string | null, codriverName?: string | null, consumptionCosts: number, createdAt?: any | null, deletedAt?: any | null, direction: string, done: boolean, driverId?: string | null, driverName?: string | null, empty?: string | null, hasInfection?: boolean | null, healthInsurance?: string | null, healthInsuranceNumber?: string | null, id: string, infectionName?: string | null, occupiedKm: number, operationId: string, ordinanceType?: string | null, patientCity?: string | null, patientId?: string | null, patientName?: string | null, patientStreet?: string | null, patientSurname?: string | null, patientZip?: string | null, rangeEndDate?: any | null, revenue: number, revenueDeviation: number, revenueDispoLive: number, startBegin?: string | null, startCity?: string | null, startDate?: any | null, startEnd?: string | null, startInstitution?: string | null, startStreet?: string | null, startZip?: string | null, target?: string | null, targetBegin?: string | null, targetCity?: string | null, targetInstitution?: string | null, targetStreet?: string | null, targetZip?: string | null, ticketId?: string | null, totalKm: number, transportType?: string | null, type: string, ticket?: { __typename?: 'Ticket', currentState: TicketValidationState, approvalState?: ApprovalState | null, createdAt: any } | null };
export type GetAnomaliesAssociatedEntitiesQueryVariables = Exact<{
id: Scalars['String']['input'];
}>;
export type GetAnomaliesAssociatedEntitiesQuery = { __typename?: 'Query', associatedEntities: { __typename?: 'AssociatedEntities', employee?: Array<{ __typename?: 'Employee', name: string, id: string, surname: string, personnelNumber: string, qualification?: string | null, occuptationalHealthExamination?: any | null, personTransportCertificate?: any | null }> | null, plannedTime?: Array<{ __typename?: 'PlannedTime', id: string, userId: string, firstname: string, surname: string, startDate: any, endDate: any, department: string, pause: number, duration: number, note?: string | null }> | null, workTime?: Array<{ __typename?: 'WorkTime', id: string, userId: string, firstname: string, surname: string, startDate: any, endDate: any, department: string, pause: number, duration: number }> | null, attendanceRegistration?: Array<{ __typename?: 'AttendanceRegistration', personnelNumber: string, attendanceRegistrationId: number, employeeId: number, dateTime: any, event: string }> | null, tour?: Array<{ __typename?: 'Tour', billDate?: any | null, billNumber?: string | null, carName?: string | null, category?: string | null, check: number, codriverId?: string | null, codriverName?: string | null, consumptionCosts: number, createdAt?: any | null, deletedAt?: any | null, direction: string, done: boolean, driverId?: string | null, driverName?: string | null, empty?: string | null, hasInfection?: boolean | null, healthInsurance?: string | null, healthInsuranceNumber?: string | null, id: string, infectionName?: string | null, occupiedKm: number, operationId: string, ordinanceType?: string | null, patientCity?: string | null, patientId?: string | null, patientName?: string | null, patientStreet?: string | null, patientSurname?: string | null, patientZip?: string | null, rangeEndDate?: any | null, revenue: number, revenueDeviation: number, revenueDispoLive: number, startBegin?: string | null, startCity?: string | null, startDate?: any | null, startEnd?: string | null, startInstitution?: string | null, startStreet?: string | null, startZip?: string | null, target?: string | null, targetBegin?: string | null, targetCity?: string | null, targetInstitution?: string | null, targetStreet?: string | null, targetZip?: string | null, ticketId?: string | null, totalKm: number, transportType?: string | null, type: string, ticket?: { __typename?: 'Ticket', currentState: TicketValidationState, approvalState?: ApprovalState | null, createdAt: any } | null }> | null } };
export type HomeDashboardQueryVariables = Exact<{ [key: string]: never; }>;
@@ -669,6 +754,130 @@ export const AnomalyAllFragmentDoc = gql`
createdAt
}
`;
export const EmployeeAllFragmentDoc = gql`
fragment EmployeeAll on Employee {
name
id
surname
personnelNumber
qualification
occuptationalHealthExamination
personTransportCertificate
}
`;
export const PlannedTimeAllFragmentDoc = gql`
fragment PlannedTimeAll on PlannedTime {
id
userId
firstname
surname
startDate
endDate
department
pause
duration
note
}
`;
export const WorkTimeAllFragmentDoc = gql`
fragment WorkTimeAll on WorkTime {
id
userId
firstname
surname
startDate
endDate
department
pause
duration
}
`;
export const AttendanceRegistrationAllFragmentDoc = gql`
fragment AttendanceRegistrationAll on AttendanceRegistration {
personnelNumber
attendanceRegistrationId
employeeId
dateTime
event
}
`;
export const TourAllFragmentDoc = gql`
fragment TourAll on Tour {
billDate
billNumber
carName
category
check
codriverId
codriverName
consumptionCosts
createdAt
deletedAt
direction
done
driverId
driverName
empty
hasInfection
healthInsurance
healthInsuranceNumber
id
infectionName
occupiedKm
operationId
ordinanceType
patientCity
patientId
patientName
patientStreet
patientSurname
patientZip
rangeEndDate
revenue
revenueDeviation
revenueDispoLive
startBegin
startCity
startDate
startEnd
startInstitution
startStreet
startZip
target
targetBegin
targetCity
targetInstitution
targetStreet
targetZip
ticketId
ticket {
currentState
approvalState
createdAt
}
totalKm
transportType
type
}
`;
export const GetAnomalyDocument = gql`
query GetAnomaly($id: String!) {
anomaly(id: $id) {
...AnomalyAll
}
}
${AnomalyAllFragmentDoc}`;
@Injectable({
providedIn: 'root'
})
export class GetAnomalyGQL extends Apollo.Query<GetAnomalyQuery, GetAnomalyQueryVariables> {
override document = GetAnomalyDocument;
constructor(apollo: Apollo.Apollo) {
super(apollo);
}
}
export const GetAnomaliesDocument = gql`
query GetAnomalies($cursor: String, $take: Int!) {
anomalies(cursor: $cursor, take: $take) {
@@ -752,6 +961,42 @@ export const AcceptAnomalyDocument = gql`
export class AcceptAnomalyGQL extends Apollo.Mutation<AcceptAnomalyMutation, AcceptAnomalyMutationVariables> {
override document = AcceptAnomalyDocument;
constructor(apollo: Apollo.Apollo) {
super(apollo);
}
}
export const GetAnomaliesAssociatedEntitiesDocument = gql`
query GetAnomaliesAssociatedEntities($id: String!) {
associatedEntities(id: $id) {
employee {
...EmployeeAll
}
plannedTime {
...PlannedTimeAll
}
workTime {
...WorkTimeAll
}
attendanceRegistration {
...AttendanceRegistrationAll
}
tour {
...TourAll
}
}
}
${EmployeeAllFragmentDoc}
${PlannedTimeAllFragmentDoc}
${WorkTimeAllFragmentDoc}
${AttendanceRegistrationAllFragmentDoc}
${TourAllFragmentDoc}`;
@Injectable({
providedIn: 'root'
})
export class GetAnomaliesAssociatedEntitiesGQL extends Apollo.Query<GetAnomaliesAssociatedEntitiesQuery, GetAnomaliesAssociatedEntitiesQueryVariables> {
override document = GetAnomaliesAssociatedEntitiesDocument;
constructor(apollo: Apollo.Apollo) {
super(apollo);
}
@@ -21,6 +21,12 @@ fragment AnomalyAll on Anomaly {
createdAt
}
query GetAnomaly($id: String!) {
anomaly(id: $id) {
...AnomalyAll
}
}
query GetAnomalies($cursor: String, $take: Int!) {
anomalies(cursor: $cursor, take: $take) {
prevCursor
@@ -0,0 +1,120 @@
fragment EmployeeAll on Employee {
name
id
surname
personnelNumber
qualification
occuptationalHealthExamination
personTransportCertificate
}
fragment PlannedTimeAll on PlannedTime {
id
userId
firstname
surname
startDate
endDate
department
pause
duration
note
}
fragment WorkTimeAll on WorkTime {
id
userId
firstname
surname
startDate
endDate
department
pause
duration
}
fragment AttendanceRegistrationAll on AttendanceRegistration {
personnelNumber
attendanceRegistrationId
employeeId
dateTime
event
}
fragment TourAll on Tour {
billDate
billNumber
carName
category
check
codriverId
codriverName
consumptionCosts
createdAt
deletedAt
direction
done
driverId
driverName
empty
hasInfection
healthInsurance
healthInsuranceNumber
id
infectionName
occupiedKm
operationId
ordinanceType
patientCity
patientId
patientName
patientStreet
patientSurname
patientZip
rangeEndDate
revenue
revenueDeviation
revenueDispoLive
startBegin
startCity
startDate
startEnd
startInstitution
startStreet
startZip
target
targetBegin
targetCity
targetInstitution
targetStreet
targetZip
ticketId
ticket {
currentState
approvalState
createdAt
}
totalKm
transportType
type
}
query GetAnomaliesAssociatedEntities($id: String!) {
associatedEntities(id: $id) {
employee {
...EmployeeAll
}
plannedTime {
...PlannedTimeAll
}
workTime {
...WorkTimeAll
}
attendanceRegistration {
...AttendanceRegistrationAll
}
tour {
...TourAll
}
}
}
@@ -32,14 +32,14 @@ interface Anomaly {
}
@Component({
selector: 'dks-anomaly-list-item',
templateUrl: './anomaly-list-item.component.html',
styleUrls: ['./anomaly-list-item.component.css'],
host: {
class: 'w-full rounded flex items-center',
},
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false
selector: 'dks-anomaly-list-item',
templateUrl: './anomaly-list-item.component.html',
styleUrls: ['./anomaly-list-item.component.css'],
host: {
class: 'w-full rounded flex items-center',
},
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class AnomalyListItemComponent {
isHovering = false;
@@ -76,7 +76,7 @@ export class AnomalyListItemComponent {
title: 'Ist dies kein Fehler?',
body: 'Sind sie sicher, dass diese Anomalie kein Fehler ist? Dann bestätigen Sie diesen Dialog',
},
width: 500
width: 500,
})
.afterClosed$.subscribe(
(confirmed) => confirmed && this.anomalyAccepted.emit(id)
@@ -88,6 +88,7 @@ export class AnomalyListItemComponent {
data: {
title: this.anomaly.description,
solution: this.anomaly.solution,
anomalyId: this.anomaly.id,
},
});
dialogRef.afterClosed$.subscribe((result) => {
@@ -1,17 +1,20 @@
<h2>{{ ref.data.title }}</h2>
<mat-divider></mat-divider>
<article class="my-4 mx-2">
<div class="pb-4" *dksRole="['admin']">
<a [routerLink]="['/control-center', ref.data.anomalyId]">View Anomaly</a>
</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>
<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>
@@ -9,16 +9,19 @@ interface SolutionTarget {
}
@Component({
selector: 'dks-anomaly-solution-dialog',
templateUrl: './anomaly-solution-dialog.component.html',
styleUrls: ['./anomaly-solution-dialog.component.scss'],
host: {
class: 'm-4',
},
standalone: false
selector: 'dks-anomaly-solution-dialog',
templateUrl: './anomaly-solution-dialog.component.html',
styleUrls: ['./anomaly-solution-dialog.component.scss'],
host: {
class: 'm-4',
},
standalone: false,
})
export class AnomalySolutionDialogComponent {
constructor(
public ref: DialogRef<{ title: string; solution: SolutionTarget[] }, 'sleep' | 'accept'>
public ref: DialogRef<
{ title: string; solution: SolutionTarget[]; anomalyId: string },
'sleep' | 'accept'
>
) {}
}
@@ -0,0 +1,77 @@
<div class="w-full">
@if(anomaly()) {
<div>{{anomaly()?.description}}</div>
@for (target of anomaly()?.solution; track $index) {
<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>
}
}
<div class="flex m-4 justify-between overflow-y-hidden overflow-scroll [&>div]:flex-shrink-0">
@if(tours().length > 0) {
@for (tour of tours(); track $index) {
<div class="flex flex-col mx-2 my-4 p-4 rounded-2xl" style="box-shadow: var(--mat-sys-level2);">
@for (item of tour | keyvalue; track $index) {
<div>
<span>{{item.key}}</span>: <span>{{item.value}}</span>
</div>
}
</div>
}
}
@if(plannedTimes().length > 0) {
@for (plannedTime of plannedTimes(); track $index) {
<div class="flex flex-col mx-2 my-4 p-4 rounded-2xl" style="box-shadow: var(--mat-sys-level2);">
@for (item of plannedTime | keyvalue; track $index) {
<div>
<span>{{item.key}}</span>: <span>{{item.value}}</span>
</div>
}
</div>
}
}
@if(workTimes().length > 0) {
@for (workTime of workTimes(); track $index) {
<div class="flex flex-col mx-2 my-4 p-4 rounded-2xl" style="box-shadow: var(--mat-sys-level2);">
@for (item of workTime | keyvalue; track $index) {
<div>
<span>{{item.key}}</span>: <span>{{item.value}}</span>
</div>
}
</div>
}
}
@if(attendanceRegistrations().length > 0) {
<div class="flex flex-col">
@for (attendanceRegistration of attendanceRegistrations(); track $index) {
<div class="flex flex-col mx-2 my-4 p-4 rounded-2xl" style="box-shadow: var(--mat-sys-level2);">
@for (item of attendanceRegistration | keyvalue; track $index) {
<div>
<span>{{item.key}}</span>: <span>{{item.value}}</span>
</div>
}
</div>
}
</div>
}
@if(employees().length > 0) {
@for (employee of employees(); track $index) {
<div class="flex flex-col mx-2 my-4 p-4 rounded-2xl" style="box-shadow: var(--mat-sys-level2);">
@for (item of employee | keyvalue; track $index) {
<div>
<span>{{item.key}}</span>: <span>{{item.value}}</span>
</div>
}
</div>
}
}
</div>
</div>
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AnomalyViewComponent } from './anomaly-view.component';
describe('AnomalyViewComponent', () => {
let component: AnomalyViewComponent;
let fixture: ComponentFixture<AnomalyViewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AnomalyViewComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AnomalyViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,81 @@
import {
Component,
DestroyRef,
effect,
inject,
input,
signal,
} from '@angular/core';
import {
Anomaly,
AttendanceRegistration,
Employee,
GetAnomaliesAssociatedEntitiesGQL,
GetAnomalyGQL,
PlannedTime,
Tour,
WorkTime,
} from '../../../core/data-access/graphql/generated/generated';
import { map, tap } from 'rxjs';
import { KeyValuePipe } from '@angular/common';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-anomaly-view',
imports: [KeyValuePipe],
templateUrl: './anomaly-view.component.html',
styles: ``,
})
export class AnomalyViewComponent {
anomalyId = input.required<string>();
anomalyByIdGql = inject(GetAnomalyGQL);
associatedEntitiesGql = inject(GetAnomaliesAssociatedEntitiesGQL);
destroyRef = inject(DestroyRef);
anomaly = signal<Anomaly | undefined>(undefined);
tours = signal<Partial<Tour>[]>([]);
plannedTimes = signal<PlannedTime[]>([]);
workTimes = signal<WorkTime[]>([]);
attendanceRegistrations = signal<AttendanceRegistration[]>([]);
employees = signal<Employee[]>([]);
constructor() {
effect(() => {
this.anomalyByIdGql
.fetch({ id: this.anomalyId() })
.pipe(
takeUntilDestroyed(this.destroyRef),
tap((res) => this.anomaly.set(res.data.anomaly))
)
.subscribe();
this.associatedEntitiesGql
.fetch({ id: this.anomalyId() })
.pipe(
takeUntilDestroyed(this.destroyRef),
map((res) => res.data.associatedEntities),
tap(
({
tour,
attendanceRegistration,
plannedTime,
workTime,
employee,
}) => {
if (tour) this.tours.set(tour as Tour[]);
if (attendanceRegistration)
this.attendanceRegistrations.set(attendanceRegistration);
if (plannedTime) this.plannedTimes.set(plannedTime);
if (workTime) this.workTimes.set(workTime);
if (employee) this.employees.set(employee);
}
)
)
.subscribe();
});
}
ngOnInit() {
console.log(this.anomalyId());
}
}
@@ -1,42 +1 @@
<div class="flex flex-col m-4">
<div class="flex flex-wrap lg:flex-nowrap">
<dks-tours-per-time-kpi
class="w-full md:w-1/2 p-2"
[ordinanceType]="'BTW'"
></dks-tours-per-time-kpi>
<dks-tours-per-time-kpi
class="w-full md:w-1/2 p-2"
[ordinanceType]="'TSW'"
></dks-tours-per-time-kpi>
<dks-tours-per-time-kpi
class="w-full md:w-1/2 p-2"
[ordinanceType]="'KTW'"
></dks-tours-per-time-kpi>
</div>
<div class="flex lg:flex-nowrap flex-wrap-reverse">
<mat-card appearance="outlined" class="w-full md:flex-grow lg:w-2/5 m-2 !flex flex-col">
<mat-card-header>
<mat-card-title> Anomalien </mat-card-title>
</mat-card-header>
<mat-card-content class="flex-grow">
<dks-anomaly-tree></dks-anomaly-tree>
</mat-card-content>
</mat-card>
<mat-card appearance="outlined" class="w-full md:flex-grow lg:w-2/5 m-2 !flex flex-col">
<mat-card-header>
<mat-card-title> Mitarbeiter ohne Tour </mat-card-title>
</mat-card-header>
<mat-card-content class="flex-grow">
<dks-anomaly-list></dks-anomaly-list>
</mat-card-content>
</mat-card>
<mat-card appearance="outlined" class="w-full lg:w-1/5 m-2">
<mat-card-header>
<mat-card-title> Nächste Touren ohne Anfahrt </mat-card-title>
</mat-card-header>
<mat-card-content>
<dks-upcoming-tours></dks-upcoming-tours>
</mat-card-content>
</mat-card>
</div>
</div>
<router-outlet></router-outlet>
@@ -1,22 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlCenterDashboardComponent } from './control-center-dashboard.component';
describe('ControlCenterDashboardComponent', () => {
let component: ControlCenterDashboardComponent;
let fixture: ComponentFixture<ControlCenterDashboardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ControlCenterDashboardComponent],
}).compileComponents();
fixture = TestBed.createComponent(ControlCenterDashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -3,7 +3,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'dks-control-center-dashboard',
templateUrl: './control-center-dashboard.component.html',
styleUrls: ['./control-center-dashboard.component.scss'],
styles: [],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false
})
@@ -10,16 +10,19 @@ import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTreeModule } from '@angular/material/tree';
import { RouterModule } from '@angular/router';
import { ConfirmDialogComponent } from '../../core/components/confirm-dialog/confirm-dialog.component';
import { AnomaliesService } from './anomalies.service';
import { AnomalyListItemComponent } from './anomaly-list/anomaly-list-item/anomaly-list-item.component';
import { AnomalyListComponent } from './anomaly-list/anomaly-list.component';
import { AnomalySolutionDialogComponent } from './anomaly-list/anomaly-solution-dialog/anomaly-solution-dialog.component';
import { AnomalyTreeComponent } from './anomaly-tree/anomaly-tree.component';
import { AnomalyViewComponent } from './anomaly-view/anomaly-view.component';
import { ControlCenterDashboardComponent } from './control-center-dashboard.component';
import { ToursPerTimeKPIComponent } from './kpis/tours-per-time-kpi/tours-per-time-kpi.component';
import { TourViewDialogComponent } from './upcoming-tours/tour-view-dialog/tour-view-dialog.component';
import { UpcomingToursComponent } from './upcoming-tours/upcoming-tours.component';
import { ConfirmDialogComponent } from '../../core/components/confirm-dialog/confirm-dialog.component';
import { OverviewDashboardComponent } from './overview-dashboard/overview-dashboard.component';
import { RoleDirective } from '../../core/components/ms-authentication/role.directive';
@NgModule({
imports: [
@@ -28,6 +31,21 @@ import { ConfirmDialogComponent } from '../../core/components/confirm-dialog/con
{
path: '',
component: ControlCenterDashboardComponent,
children: [
{
path: 'overview',
component: OverviewDashboardComponent,
},
{
path: ':anomalyId',
component: AnomalyViewComponent,
},
{
path: '',
pathMatch: 'full',
redirectTo: 'overview'
}
],
},
]),
MatGridListModule,
@@ -40,9 +58,11 @@ import { ConfirmDialogComponent } from '../../core/components/confirm-dialog/con
MatTreeModule,
MatPaginatorModule,
ConfirmDialogComponent,
RoleDirective,
],
declarations: [
ControlCenterDashboardComponent,
OverviewDashboardComponent,
AnomalyListComponent,
AnomalyListItemComponent,
AnomalySolutionDialogComponent,
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OverviewDashboardComponent } from './overview-dashboard.component';
describe('OverviewDashboardComponent', () => {
let component: OverviewDashboardComponent;
let fixture: ComponentFixture<OverviewDashboardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [OverviewDashboardComponent]
})
.compileComponents();
fixture = TestBed.createComponent(OverviewDashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,58 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-overview-dashboard',
standalone: false,
template: `
<div class="flex flex-col m-4">
<div class="flex flex-wrap lg:flex-nowrap">
<dks-tours-per-time-kpi
class="w-full md:w-1/2 p-2"
[ordinanceType]="'BTW'"
></dks-tours-per-time-kpi>
<dks-tours-per-time-kpi
class="w-full md:w-1/2 p-2"
[ordinanceType]="'TSW'"
></dks-tours-per-time-kpi>
<dks-tours-per-time-kpi
class="w-full md:w-1/2 p-2"
[ordinanceType]="'KTW'"
></dks-tours-per-time-kpi>
</div>
<div class="flex lg:flex-nowrap flex-wrap-reverse">
<mat-card
appearance="outlined"
class="w-full md:flex-grow lg:w-2/5 m-2 !flex flex-col"
>
<mat-card-header>
<mat-card-title> Anomalien </mat-card-title>
</mat-card-header>
<mat-card-content class="flex-grow">
<dks-anomaly-tree></dks-anomaly-tree>
</mat-card-content>
</mat-card>
<mat-card
appearance="outlined"
class="w-full md:flex-grow lg:w-2/5 m-2 !flex flex-col"
>
<mat-card-header>
<mat-card-title> Mitarbeiter ohne Tour </mat-card-title>
</mat-card-header>
<mat-card-content class="flex-grow">
<dks-anomaly-list></dks-anomaly-list>
</mat-card-content>
</mat-card>
<mat-card appearance="outlined" class="w-full lg:w-1/5 m-2">
<mat-card-header>
<mat-card-title> Nächste Touren ohne Anfahrt </mat-card-title>
</mat-card-header>
<mat-card-content>
<dks-upcoming-tours></dks-upcoming-tours>
</mat-card-content>
</mat-card>
</div>
</div>
`,
styles: ``,
})
export class OverviewDashboardComponent {}