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 12 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 { PartialExportComponent } 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,
PartialExportComponent,
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 { openPartialExportSelectionModal } 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() {
openPartialExportSelectionModal(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 { PartialExportComponent } from './partial-export-modal/partial-export-modal.component';

export function openPartialExportSelectionModal(ngbModalService: NgbModal) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
export function openPartialExportSelectionModal(ngbModalService: NgbModal) {
export function openPartialExportModal(ngbModalService: NgbModal) {

ngbModalService.open(PartialExportComponent, {
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 PartialExportComponent {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
export class PartialExportComponent {
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,59 @@
import { Component } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { plainToInstance } from 'class-transformer';
import {
PartialExport,
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 importInstance = plainToInstance(
PartialExport,
this.partialExport
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const importInstance = plainToInstance(
PartialExport,
this.partialExport
);

You should be able to use partialExport directly, as this is just data.

const result = await this.exerciseService.proposeAction({
type: '[Exercise] Import Templates',
mode,
partialExport: preparePartialExportForImport(importInstance),
});
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