Skip to content

Commit 84bc8a4

Browse files
committed
Vehicles can now be the leader of a simulated region
1 parent dfcc26c commit 84bc8a4

File tree

15 files changed

+480
-94
lines changed

15 files changed

+480
-94
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ and this project does **not** adhere to [Semantic Versioning](https://semver.org
2222

2323
- Add behaviors button now opens towards the top.
2424
- Simulated regions can now send patients to any hospital. The hospitals tab was removed.
25+
- The assign leader behavior now prioritized vehicles instead of gfs. Custom command vehicles can be set in the frontend.
2526

2627
### Fixed
2728

Original file line numberDiff line numberDiff line change
@@ -1,9 +1,64 @@
1-
<h6>Aktuell zugewiesene Führungskraft</h6>
1+
<h5>Aktuell zugewiesene Führungskraft</h5>
22

3-
<p *ngIf="currentLeader; else noLeader">
4-
{{ (currentLeader | async)?.personnelType | personnelName }}
3+
<p *ngIf="currentLeader$ | async as currentLeader; else noLeader">
4+
{{ currentLeader.personnelType | personnelName }}
5+
<ng-container
6+
*ngIf="vehicleOfCurrentLeader$ | async as vehicleOfCurrentLeader"
7+
>aus Fahrzeug {{ vehicleOfCurrentLeader.name }}</ng-container
8+
>
59
</p>
610

711
<ng-template #noLeader>
812
<p>Keine Führungskraft zugewiesen.</p>
913
</ng-template>
14+
15+
<h6>Mögliche Kommandofahrzeuge</h6>
16+
17+
<ul class="list-group" *ngIf="behaviorState$ | async as behaviorState">
18+
<li
19+
*ngFor="
20+
let leadershipVehicleType of behaviorState.leadershipVehicleTypes
21+
| keys
22+
"
23+
class="list-group-item d-flex flex-row justify-content-between align-items-center"
24+
>
25+
<span>{{ leadershipVehicleType }}</span>
26+
<button
27+
class="btn btn-outline-danger"
28+
type="button"
29+
(click)="removeVehicleType(leadershipVehicleType)"
30+
>
31+
<span class="bi-trash"></span>
32+
</button>
33+
</li>
34+
</ul>
35+
36+
<div
37+
ngbDropdown
38+
*ngIf="vehicleTypesToAdd$ | async as vehicleTypesToAdd"
39+
placement="bottom-start"
40+
autoClose="outside"
41+
class="d-inline-block overflow-visible text-center mb-3"
42+
>
43+
<button
44+
ngbDropdownToggle
45+
type="button"
46+
class="btn btn-outline-primary"
47+
[disabled]="vehicleTypesToAdd.length === 0"
48+
>
49+
<span class="bi-plus me-1"></span>
50+
Weiteren Kommandofahrzeugtyp hinzufügen
51+
</button>
52+
<div *ngIf="vehicleTypesToAdd.length > 0" ngbDropdownMenu>
53+
<button
54+
*ngFor="let vehicleType of vehicleTypesToAdd"
55+
ngbDropdownItem
56+
class="dropdown-item"
57+
type="button"
58+
(click)="addVehicleType(vehicleType)"
59+
>
60+
<span class="bi-plus me-1"></span>
61+
{{ vehicleType }}
62+
</button>
63+
</div>
64+
</div>
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
import type { OnChanges } from '@angular/core';
22
import { Component, Input } from '@angular/core';
33
import { Store } from '@ngrx/store';
4-
import type { Personnel } from 'digital-fuesim-manv-shared';
5-
import { AssignLeaderBehaviorState } from 'digital-fuesim-manv-shared';
4+
import { UUID } from 'digital-fuesim-manv-shared';
5+
import type {
6+
AssignLeaderBehaviorState,
7+
Personnel,
8+
Vehicle,
9+
} from 'digital-fuesim-manv-shared';
610
import type { Observable } from 'rxjs';
11+
import { combineLatest, map } from 'rxjs';
12+
import { ExerciseService } from 'src/app/core/exercise.service';
713
import type { AppState } from 'src/app/state/app.state';
8-
import { createSelectPersonnel } from 'src/app/state/application/selectors/exercise.selectors';
14+
import {
15+
createSelectBehaviorState,
16+
selectPersonnel,
17+
selectVehicleTemplates,
18+
selectVehicles,
19+
} from 'src/app/state/application/selectors/exercise.selectors';
920

1021
@Component({
1122
selector: 'app-simulated-region-overview-behavior-assign-leader',
@@ -18,18 +29,86 @@ import { createSelectPersonnel } from 'src/app/state/application/selectors/exerc
1829
export class SimulatedRegionOverviewBehaviorAssignLeaderComponent
1930
implements OnChanges
2031
{
21-
@Input()
22-
assignLeaderBehaviorState!: AssignLeaderBehaviorState;
32+
@Input() assignLeaderBehaviorId!: UUID;
33+
@Input() simulatedRegionId!: UUID;
2334

24-
currentLeader?: Observable<Personnel>;
35+
behaviorState$!: Observable<AssignLeaderBehaviorState>;
2536

26-
constructor(private readonly store: Store<AppState>) {}
37+
currentLeader$!: Observable<Personnel | undefined>;
38+
39+
vehicleOfCurrentLeader$!: Observable<Vehicle | undefined>;
40+
41+
vehicleTypesToAdd$!: Observable<string[]>;
42+
43+
constructor(
44+
private readonly store: Store<AppState>,
45+
private readonly exerciseService: ExerciseService
46+
) {}
2747

2848
ngOnChanges(): void {
29-
if (this.assignLeaderBehaviorState.leaderId) {
30-
this.currentLeader = this.store.select(
31-
createSelectPersonnel(this.assignLeaderBehaviorState.leaderId)
32-
);
33-
}
49+
this.behaviorState$ = this.store.select(
50+
createSelectBehaviorState<AssignLeaderBehaviorState>(
51+
this.simulatedRegionId,
52+
this.assignLeaderBehaviorId
53+
)
54+
);
55+
56+
const personnel$ = this.store.select(selectPersonnel);
57+
58+
this.currentLeader$ = combineLatest([
59+
this.behaviorState$,
60+
personnel$,
61+
]).pipe(
62+
map(([behaviorState, personnel]) =>
63+
behaviorState.leaderId
64+
? personnel[behaviorState.leaderId]
65+
: undefined
66+
)
67+
);
68+
69+
const vehicles$ = this.store.select(selectVehicles);
70+
71+
this.vehicleOfCurrentLeader$ = combineLatest([
72+
this.currentLeader$,
73+
vehicles$,
74+
]).pipe(
75+
map(([currentLeader, vehicles]) =>
76+
currentLeader ? vehicles[currentLeader.vehicleId] : undefined
77+
)
78+
);
79+
80+
const vehicleTemplates$ = this.store.select(selectVehicleTemplates);
81+
82+
this.vehicleTypesToAdd$ = combineLatest([
83+
this.behaviorState$,
84+
vehicleTemplates$,
85+
]).pipe(
86+
map(([behaviorState, vehicleTemplates]) =>
87+
vehicleTemplates
88+
.map((vehicleTemplate) => vehicleTemplate.vehicleType)
89+
.filter(
90+
(vehicleType) =>
91+
!behaviorState.leadershipVehicleTypes[vehicleType]
92+
)
93+
)
94+
);
95+
}
96+
97+
addVehicleType(vehicleType: string) {
98+
this.exerciseService.proposeAction({
99+
type: '[AssignLeaderBehavior] Add Leadership Vehicle Type',
100+
simulatedRegionId: this.simulatedRegionId,
101+
behaviorId: this.assignLeaderBehaviorId,
102+
vehicleType,
103+
});
104+
}
105+
106+
removeVehicleType(vehicleType: string) {
107+
this.exerciseService.proposeAction({
108+
type: '[AssignLeaderBehavior] Remove Leadership Vehicle Type',
109+
simulatedRegionId: this.simulatedRegionId,
110+
behaviorId: this.assignLeaderBehaviorId,
111+
vehicleType,
112+
});
34113
}
35114
}

frontend/src/app/pages/exercises/exercise/shared/simulated-region-overview/tabs/behavior-tab/simulated-region-overview-behavior-tab.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
<div [ngSwitch]="selectedBehavior?.type">
3131
<app-simulated-region-overview-behavior-assign-leader
3232
*ngSwitchCase="'assignLeaderBehavior'"
33-
[assignLeaderBehaviorState]="$any(selectedBehavior)"
33+
[simulatedRegionId]="simulatedRegion.id"
34+
[assignLeaderBehaviorId]="selectedBehavior!.id"
3435
></app-simulated-region-overview-behavior-assign-leader
3536
><app-simulated-region-overview-behavior-treat-patients
3637
*ngSwitchCase="'treatPatientsBehavior'"

frontend/src/app/shared/components/vehicle-occupation-editor/vehicle-occupation-editor.component.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
reserviert.
2525
</span>
2626
</ng-container>
27+
<ng-container *ngSwitchCase="'isLeaderOccupation'">
28+
<span>Das Fahrzeug führt diese Region</span>
29+
</ng-container>
2730
<ng-container *ngSwitchDefault>
2831
<span class="text-muted">Die Tätigkeit ist unbekannt.</span>
2932
</ng-container>

shared/src/models/utils/occupations/exercise-occupation.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { LoadOccupation } from './load-occupation';
77
import { WaitForTransferOccupation } from './wait-for-transfer-occupation';
88
import { UnloadingOccupation } from './unloading-occupation';
99
import { PatientTransferOccupation } from './patient-transfer-occupation';
10+
import { IsLeaderOccupation } from './is-leader-occupation';
1011

1112
export const occupations = {
1213
IntermediateOccupation,
@@ -15,6 +16,7 @@ export const occupations = {
1516
WaitForTransferOccupation,
1617
UnloadingOccupation,
1718
PatientTransferOccupation,
19+
IsLeaderOccupation,
1820
};
1921

2022
export type ExerciseOccupation = InstanceType<
@@ -34,6 +36,7 @@ export const occupationDictionary: ExerciseOccupationDictionary = {
3436
waitForTransferOccupation: WaitForTransferOccupation,
3537
unloadingOccupation: UnloadingOccupation,
3638
patientTransferOccupation: PatientTransferOccupation,
39+
isLeaderOccupation: IsLeaderOccupation,
3740
};
3841

3942
export const occupationTypeOptions: Parameters<typeof Type> = [

shared/src/models/utils/occupations/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export * from './occupation-helpers';
77
export * from './patient-transfer-occupation';
88
export * from './unloading-occupation';
99
export * from './wait-for-transfer-occupation';
10+
export * from './is-leader-occupation';
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { IsUUID } from 'class-validator';
2+
import { UUID, uuidValidationOptions } from '../../../utils';
3+
import { IsValue } from '../../../utils/validators';
4+
import { getCreate } from '../get-create';
5+
import type { Occupation } from './occupation';
6+
7+
export class IsLeaderOccupation implements Occupation {
8+
@IsValue('isLeaderOccupation')
9+
readonly type = 'isLeaderOccupation';
10+
11+
@IsUUID(4, uuidValidationOptions)
12+
readonly simulatedRegionId: UUID;
13+
14+
/**
15+
* @deprecated Use static `create` method instead.
16+
*/
17+
constructor(simulatedRegionId: UUID) {
18+
this.simulatedRegionId = simulatedRegionId;
19+
}
20+
21+
static readonly create = getCreate(this);
22+
}

0 commit comments

Comments
 (0)