Skip to content

Feature/interface signaller UI #1076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 143 commits into
base: dev
Choose a base branch
from

Conversation

lukasrad02
Copy link
Collaborator

@lukasrad02 lukasrad02 commented May 20, 2025

This PR adds a new modal that allows interface signallers to interact with the software

PR Checklist

Please make sure to fulfil the following conditions before marking this PR ready for review:

  • If this PR adds or changes features or fixes bugs, this has been added to the changelog
  • If this PR adds new actions or other ways to alter the state, test scenarios have been added.
  • I have the right to license the code submitted with this Pull Request under the mentioned license in the file LICENSE-README.md (i.e., this is my
    own code or code licensed under a license compatible to AGPL v3.0 or later, for exceptions look into LICENSE-README.md) and
    hereby license the code in this Pull Request under it.
    I certify that by signing off my commits (see In case of using third party code, I have given appropriate credit.
    We are using DCO for that, see here for more information.
  • If I have used third party code and I mentioned it in the code, I also updated the inspired-by-or-copied-from-list.html list to include the links.

lukasrad02 added 30 commits May 20, 2025 10:40
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
lukasrad02 added 12 commits June 2, 2025 10:41
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
@lukasrad02 lukasrad02 marked this pull request as ready for review July 9, 2025 11:39
Copy link
Collaborator

@ClFeSc ClFeSc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, my review as far as I've gotten today.

Comment on lines 145 to 153
// ESLint seems to be wrong here. Without the type assertion, we receive an ExerciseSimulationBehaviorState
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const reportBehavior = selectStateSnapshot(
createSelectBehaviorState(
this.simulatedRegionId,
this.reportBehaviorId
),
this.store
) as ReportBehaviorState;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use this instead:

Suggested change
// ESLint seems to be wrong here. Without the type assertion, we receive an ExerciseSimulationBehaviorState
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const reportBehavior = selectStateSnapshot(
createSelectBehaviorState(
this.simulatedRegionId,
this.reportBehaviorId
),
this.store
) as ReportBehaviorState;
const reportBehavior = selectStateSnapshot(
createSelectBehaviorState<ReportBehaviorState>(
this.simulatedRegionId,
this.reportBehaviorId
),
this.store
);

This should in theory be done to all calls to createSelectBehaviorState as its B generic parameter can never be inferred from usage. But this should not be something for this PR, I think.

By the way, createSelectBehaviorState has a weird contract: It allows you to pass in any B extends ExerciseSimulationBehaviorState but filters for a condition unrelated (at the surface at least) to B, simply assuming the resulting element is a B.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggested fix! I've added it in 255fcd5

I understand why you think the contract is weird, but I claim that it makes sense given how I expect the selector to be used: If I got a UUID and know from the context what type of behavior to expect, I wouldn't check the type again, but just assume it (i.e., perform a type cast). So I'd rather keep this shorthand way of doing it

@@ -0,0 +1,5 @@
# EOC Information

TODO: It would be better if the EOC could sent radiograms for requested information, too. However, radiograms must have a SimulatedRegion they're sent by and refactoring this would have been too much work at least for now. Therefore, information requested from the EOC will be shown in the details modal instead.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider opening an issue for this and link it here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to #1093

Comment on lines +16 to +20
<app-hotkey-indicator keys="Leertaste" />: Aktuelles Steuerelement
an-/ausschalten
<br />
<app-hotkey-indicator keys="Tab" />: Nächstes Steuerelement auswählen
<br />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are also shown when there are no elements to select or toggle, e.g. in the sent alarm groups dialog. Maybe there is a way to check whether there is anything meaningful to tab through.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice suggestion, implemented in 9b8e6cd

}
}

public updateInterval(newInterval: string) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document which unit is expected here, e.g. by renaming the parameter to newIntervalMinutes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 24d684f

requiredBehaviors: ['transferBehavior'],
errorMessage: 'Dieser Bereich kann keine Fahrzeuge bereitstellen',
},
// TODO: Radio channels
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO for this PR?

hotkey: new Hotkey('2', true, () =>
this.editTransferPatientTrays()
),
requiredBehaviors: ['managePatientTransportToHospitalBehavior'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little weird that these requiredBehaviors are defined twice: Once here, and again in the *ngIf of the respective ng-template.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, but I don't know how to resolve this. In the HTML template, I need the *ngIf to resolve the observable to a specific value to pass it as input into the component and in the TypeScript code, I need the array of required behaviors to en-/disable the button and hotkey to show some user feedback before the user actually clicked on the button.

What I could do:

  • Change *ngIf to *appLet so that it does not look like a condition (however, it basically does the same in the end)
  • Remove the templates showing an error message for the else branch, as it should never be visible due to the checks in the code behind.

But the general issue of duplication remains. Even if I only pass the ID of the region into the subcomponents, the required behaviors would still be checked twice (once in the current commands component and once in each of the subcomponents).

Do you have a suggestion how to handle this?

Comment on lines +53 to +56
hotkey: new Hotkey('A', false, () => this.requestPatientCount()),
secondaryHotkey: new Hotkey('⇧ + A', true, () =>
this.openRecurringReportModal('patientCount')
),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like some kind of unification would be good here. Currently, the modal type is declared twice, once in the secondary hotkey and once in the function called by the hotkey. Also, these calls are more or less identical between the different interactions. However, I'm also not sure how this could be unified in a reasonable way.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only way to simplify/deduplicate this would be to define an additional, reduced data model and then build the full data model from this definition. However, this feels like overall adding more overhead and complexity due to the additional indirection…

errorMessage: 'Dieser Bereich behandelt keine Patienten',
loading$: new BehaviorSubject<boolean>(false),
},
// TODO: Location
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO for this PR? Also for the two TODOs below.

get filteredInteractions() {
const lowerFilter = this.filter.toLowerCase();

return this.interactions.filter(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a little weird in the sense that you cannot filter by multiple phrases by entering multiple words. Maybe you could split the lowerFilter in whitespace and then require that all parts match, each using the existing filter logic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice idea, added in 9b6fa4d

lukasrad02 and others added 11 commits July 15, 2025 20:09
Co-authored-by: Clemens <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Signed-off-by: Lukas Radermacher <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants