Skip to content
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

Allow partial export #522

Open
wants to merge 15 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions frontend/src/app/pages/exercises/exercise/exercise.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { ExerciseSettingsModalComponent } from './shared/exercise-settings/exerc
import { ExerciseStateBadgeComponent } from './shared/exercise-state-badge/exercise-state-badge.component';
import { ExerciseStatisticsModule } from './shared/exercise-statistics/exercise-statistics.module';
import { HospitalEditorModule } from './shared/hospital-editor/hospital-editor.module';
import { PartialExportModalComponent } from './shared/partial-export/partial-export-modal/partial-export-modal.component';
import { PartialImportModalComponent } from './shared/partial-import/partial-import-modal/partial-import-modal.component';
import { TimeTravelComponent } from './shared/time-travel/time-travel.component';
import { TrainerMapEditorComponent } from './shared/trainer-map-editor/trainer-map-editor.component';
import { TrainerToolbarComponent } from './shared/trainer-toolbar/trainer-toolbar.component';
Expand All @@ -32,6 +34,8 @@ import { TransferOverviewModule } from './shared/transfer-overview/transfer-over
CreateImageTemplateModalComponent,
EditImageTemplateModalComponent,
ImageTemplateFormComponent,
PartialExportModalComponent,
PartialImportModalComponent,
],
imports: [
CommonModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ <h2>
um {{ timeConstraints.current | formatDuration }}
</span>
</button>
<button ngbDropdownItem (click)="partialExport()">
<i class="bi-card-list me-2"> </i>
Vorlagen exportieren
</button>
</div>
</div>
</h2>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { OnDestroy } from '@angular/core';
import { Component } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Store } from '@ngrx/store';
import {
cloneDeepMutable,
StateExport,
cloneDeepMutable,
StateHistoryCompound,
} from 'digital-fuesim-manv-shared';
import { Subject } from 'rxjs';
Expand All @@ -13,16 +14,17 @@ import { MessageService } from 'src/app/core/messages/message.service';
import { saveBlob } from 'src/app/shared/functions/save-blob';
import type { AppState } from 'src/app/state/app.state';
import {
selectExerciseId,
selectExerciseStateMode,
selectTimeConstraints,
selectExerciseId,
} from 'src/app/state/application/selectors/application.selectors';
import {
selectExerciseState,
selectParticipantId,
selectExerciseState,
} from 'src/app/state/application/selectors/exercise.selectors';
import { selectStateSnapshot } from 'src/app/state/get-state-snapshot';
import { selectOwnClient } from 'src/app/state/application/selectors/shared.selectors';
import { selectStateSnapshot } from 'src/app/state/get-state-snapshot';
import { openPartialExportModal } from '../shared/partial-export/open-partial-export-selection-modal';

@Component({
selector: 'app-exercise',
Expand All @@ -43,7 +45,8 @@ export class ExerciseComponent implements OnDestroy {
private readonly store: Store<AppState>,
private readonly apiService: ApiService,
private readonly applicationService: ApplicationService,
private readonly messageService: MessageService
private readonly messageService: MessageService,
private readonly modalService: NgbModal
) {}

public shareExercise(type: 'participantId' | 'trainerId') {
Expand Down Expand Up @@ -103,6 +106,10 @@ export class ExerciseComponent implements OnDestroy {
saveBlob(blob, `exercise-state-${currentState.participantId}.json`);
}

public partialExport() {
openPartialExportModal(this.modalService);
}

public exportExerciseState() {
const currentState = selectStateSnapshot(
selectExerciseState,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { PartialExportModalComponent } from './partial-export-modal/partial-export-modal.component';

export function openPartialExportModal(ngbModalService: NgbModal) {
ngbModalService.open(PartialExportModalComponent, {
size: 'm',
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<div class="modal-header">
<h4 class="modal-title">Partieller Export</h4>
<button type="button" class="btn-close" (click)="close()"></button>
</div>
<div class="modal-body">
<p>Wählen Sie aus, welche Vorlagen exportiert werden sollen.</p>
<ul class="list-group list-group-flush">
<li class="list-group-item">
<input
[(ngModel)]="configuration.patientCategories"
class="form-check-input"
type="checkbox"
/>
Patientenvorlagen
</li>
<li class="list-group-item">
<input
[(ngModel)]="configuration.vehicleTemplates"
class="form-check-input"
type="checkbox"
/>
Fahrzeugvorlagen
</li>
<li class="list-group-item">
<input
[(ngModel)]="configuration.mapImageTemplates"
class="form-check-input"
type="checkbox"
/>
Bildvorlagen
</li>
</ul>
<br />
<button
(click)="partialExport()"
[disabled]="
!(
configuration.mapImageTemplates ||
configuration.patientCategories ||
configuration.vehicleTemplates
)
"
class="btn btn-primary"
>
Exportieren
</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Component } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { Store } from '@ngrx/store';
import { cloneDeepMutable, PartialExport } from 'digital-fuesim-manv-shared';
import { saveBlob } from 'src/app/shared/functions/save-blob';
import type { AppState } from 'src/app/state/app.state';
import { selectExerciseState } from 'src/app/state/application/selectors/exercise.selectors';
import { selectStateSnapshot } from 'src/app/state/get-state-snapshot';

interface PartialExportConfiguration {
patientCategories: boolean;
vehicleTemplates: boolean;
mapImageTemplates: boolean;
}

@Component({
selector: 'app-partial-export-modal',
templateUrl: './partial-export-modal.component.html',
styleUrls: ['./partial-export-modal.component.scss'],
})
export class PartialExportModalComponent {
constructor(
private readonly store: Store<AppState>,
private readonly activeModal: NgbActiveModal
) {}

public configuration: PartialExportConfiguration = {
mapImageTemplates: false,
patientCategories: false,
vehicleTemplates: false,
};

public close() {
this.activeModal.close();
}

public partialExport() {
const currentState = selectStateSnapshot(
selectExerciseState,
this.store
);
const patientCategories = this.configuration.patientCategories
? currentState.patientCategories
: undefined;
const vehicleTemplates = this.configuration.vehicleTemplates
? currentState.vehicleTemplates
: undefined;
const mapImageTemplates = this.configuration.mapImageTemplates
? currentState.mapImageTemplates
: undefined;
const blob = new Blob([
JSON.stringify(
new PartialExport(
cloneDeepMutable(patientCategories),
cloneDeepMutable(vehicleTemplates),
cloneDeepMutable(mapImageTemplates)
)
),
]);
saveBlob(blob, `exercise-partial-${currentState.participantId}.json`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import type { PartialExport } from 'digital-fuesim-manv-shared';
import { PartialImportModalComponent } from './partial-import-modal/partial-import-modal.component';

/**
*
* @param partialExport The migrated {@link PartialExport} to import.
*/
export function openPartialImportOverwriteModal(
ngbModalService: NgbModal,
partialExport: PartialExport
) {
const modelRef = ngbModalService.open(PartialImportModalComponent, {
size: 'm',
});
const componentInstance =
modelRef.componentInstance as PartialImportModalComponent;
componentInstance.partialExport = partialExport;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<div class="modal-header">
<h4 class="modal-title">Vorlagenimport</h4>
<button type="button" class="btn-close" (click)="close()"></button>
</div>
<div class="modal-body">
<p>
Wollen Sie die vorhandenen Vorlagen mit den importierten Vorlagen
ergänzen oder überschreiben?
</p>
<button
type="button"
class="btn btn-primary me-5"
[disabled]="importingPartialExport"
(click)="partialImportOverwrite('append')"
>
Ergänzen
</button>
<button
type="button"
class="btn btn-danger"
[disabled]="importingPartialExport"
(click)="partialImportOverwrite('overwrite')"
>
Überschreiben
</button>
<div *ngIf="importingPartialExport">
<span class="spinner-border spinner-border-sm"></span>
Import läuft
</div>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-outline-secondary m-0"
[appAutofocus]="true"
[disabled]="importingPartialExport"
(click)="close()"
>
Abbrechen
</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Component } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import type { PartialExport } from 'digital-fuesim-manv-shared';
import { preparePartialExportForImport } from 'digital-fuesim-manv-shared';
import { ExerciseService } from 'src/app/core/exercise.service';
import { MessageService } from 'src/app/core/messages/message.service';

@Component({
selector: 'app-partial-import-modal',
templateUrl: './partial-import-modal.component.html',
styleUrls: ['./partial-import-modal.component.scss'],
})
export class PartialImportModalComponent {
public importingPartialExport = false;
constructor(
public activeModal: NgbActiveModal,
private readonly messageService: MessageService,
private readonly exerciseService: ExerciseService
) {}

public partialExport!: PartialExport;

public close() {
this.activeModal.close();
}

public async partialImportOverwrite(mode: 'append' | 'overwrite') {
this.importingPartialExport = true;
try {
const result = await this.exerciseService.proposeAction({
type: '[Exercise] Import Templates',
mode,
partialExport: preparePartialExportForImport(
this.partialExport
),
});
if (!result.success) {
throw new Error((result as { message?: string }).message);
}
this.messageService.postMessage({
title: 'Vorlagen erfolgreich importiert',
color: 'success',
});
this.close();
} catch (error: unknown) {
this.messageService.postError({
title: 'Fehler beim Importieren von Vorlagen',
error,
});
} finally {
this.importingPartialExport = false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,23 @@
</div>
<div class="card col-3 p-0 rounded-0 rounded-end">
<div class="card-header text-center">
<h5 class="mb-0">Editor</h5>
<h5 class="mb-0">
Editor
<label class="btn btn-outline-primary btn-sm ms-3">
<i class="bi-cloud-upload me-2"> </i>
Vorlagen Importieren
<span
*ngIf="importingTemplates"
class="spinner-border spinner-border-sm"
></span>
<input
(appFileInput)="importPartialExport($event)"
type="file"
accept="application/json"
class="d-none"
/>
</label>
</h5>
</div>
<!-- We want the editor panel to overflow, if it is too high -->
<ul
Expand Down
Loading