diff --git a/src/app/config/custom-texts.json b/src/app/config/custom-texts.json index a34c9196458f483401c5b75388621efc5269fbca..5e85f94a200bfd95bdacf54ff5657b406557313c 100644 --- a/src/app/config/custom-texts.json +++ b/src/app/config/custom-texts.json @@ -192,6 +192,10 @@ "label": "Control: Entsperren", "defaultvalue": "Entsperren" }, + "gm_control_unlock_success_warning": { + "label": "Wird angezeigt, wenn tests entsperrt wurden", + "defaultvalue": "ACHTUNG! Die betreffenden Browser müssen ggf. neu gestartet werden." + }, "gm_control_finish_everything": { "label": "Control: Testung beenden", "defaultvalue": "Testung beenden" diff --git a/src/app/group-monitor/group-monitor.component.css b/src/app/group-monitor/group-monitor.component.css index 12647f3b485e408df1b781e6346004b66c1ed462..301d5f55d47b759fba66c75559dcabe9899e9be7 100644 --- a/src/app/group-monitor/group-monitor.component.css +++ b/src/app/group-monitor/group-monitor.component.css @@ -224,28 +224,15 @@ mat-sidenav { color: #821123 } -.alert-warning { - border-radius: 4px; - box-sizing: border-box; - margin: 0 0 0.5em 0; - width: 100%; - padding: 14px 16px; - min-height: 48px; - color: hsla(0,0%,100%,.7); - background: #821123; - display: inline-flex; - vertical-align: middle; - align-items: center; - animation: fade 30s reverse forwards; +#message-panel alert:first-of-type { + background: #b2ff59; + animation: fade 7s reverse forwards; } -.alert-warning mat-icon { - margin-right: 4px; -} @keyframes fade { 0% { - opacity: 0; + opacity: 0.1; } 100% { diff --git a/src/app/group-monitor/group-monitor.component.html b/src/app/group-monitor/group-monitor.component.html index 85c6014d74fc3cc8e8d446f7524ee583ff879010..6e9c0a999099f1ff2a109f3aa29502ef00ef6e1e 100644 --- a/src/app/group-monitor/group-monitor.component.html +++ b/src/app/group-monitor/group-monitor.component.html @@ -86,41 +86,40 @@ <h2>{{'Test-Steuerung' | customtext:'gm_controls' | async}}</h2> <div class="selection-info" *ngIf="gms.sessionsStats$ | async as sessionsStats"> - <div *ngIf="sessionsStats.differentBookletSpecies > 1; else: singleBookletSpecies"> - {{ - 'Die verwendeten Booklets sind zu unterschiedlich, um gemeinsam benutzt zu werden.' - | customtext:'gm_multiple_booklet_species_warning' - | async - }} - </div> - <ng-template #singleBookletSpecies> - <mat-slide-toggle + <mat-slide-toggle color="accent" (change)="toggleAlwaysCheckAll($event)" [disabled]="!gms.checkingOptions.disableAutoCheckAll" - [checked]="!gms.checkingOptions.autoCheckAll" - > - Einzelauswahl der Sitzungen - </mat-slide-toggle> - </ng-template> + [checked]="gms.checkingOptions.autoCheckAll" + > + Alle Tests gleichzeitig steuern + </mat-slide-toggle> + <alert + *ngIf="sessionsStats.differentBookletSpecies > 1" + level="warning" + text="Die verwendeten Booklets sind zu unterschiedlich, um gemeinsam gesteuert zu werden." + customtext="gm_multiple_booklet_species_warning" + > + </alert> </div> <div class="selection-info" *ngIf="displayOptions.manualChecking"> <ng-container *ngIf="gms.checkedStats$ | async as checkedStats"> - <ng-container *ngIf="checkedStats.number; else noCheckedSession"> - {{ - '%s %s Test%s mit %s Testheft%s ausgewählt.' - | customtext:'gm_selection_info' - : (checkedStats.all ? ' Alle' : '') - : checkedStats.number.toString(10) - : (checkedStats.number !== 1 ? 's' : '') - : checkedStats.differentBooklets.toString(10) - : (checkedStats.differentBooklets !== 1 ? 'en' : '') - | async - }} - </ng-container> + <alert + *ngIf="checkedStats.number; else noCheckedSession" + level="info" + customtext="gm_selection_info" + text="%s %s Test%s mit %s Testheft%s ausgewählt." + [replacements]="[ + (checkedStats.all ? ' Alle' : ''), + checkedStats.number.toString(10), + (checkedStats.number !== 1 ? 's' : ''), + checkedStats.differentBooklets.toString(10), + (checkedStats.differentBooklets !== 1 ? 'en' : '') + ]" + ></alert> <ng-template #noCheckedSession> - {{'Kein Test gewählt.' | customtext:'gm_selection_info_none' | async}} + <alert level="info" customtext="gm_selection_info_none" text="Kein Test ausgewählt."></alert> </ng-template> </ng-container> </div> @@ -138,19 +137,10 @@ </div> <div class="toolbar-section"> - <button - mat-raised-button - class="control" - color="primary" - (click)="testCommandGoto()" - [disabled]="!selectedElement?.element?.blockId" - > + <button mat-raised-button class="control" color="primary" (click)="testCommandGoto()"> <mat-icon>arrow_forward</mat-icon> {{'Springe zu' | customtext:'gm_control_goto' | async}} <span class="emph">{{selectedElement?.element?.blockId}}</span> - <span class="emph" *ngIf="!selectedElement?.element?.blockId"> - {{'Block auswählen!' | customtext:'gm_test_command_no_selected_block' | async}} - </span> </button> </div> @@ -167,10 +157,8 @@ </button> </div> - <alert customtext="gm_control_unlock_tooltip" text="schnrugl" level="info"></alert> - - <div class="toolbar-section"> - <alert *ngFor="let m of messages" [text]="m.text" [level]="m.level"></alert> + <div id="message-panel" class="toolbar-section"> + <alert *ngFor="let m of messages" [text]="m.text" [level]="m.level" customtext="m.customtext" [replacements]="m.replacements"></alert> </div> <div class="toolbar-section toolbar-section-bottom"> diff --git a/src/app/group-monitor/group-monitor.component.ts b/src/app/group-monitor/group-monitor.component.ts index aeccb6d47b943be9715cad8a8a84f71a05b8101f..31176cc628937493cf0832f91c522cbb24afb167 100644 --- a/src/app/group-monitor/group-monitor.component.ts +++ b/src/app/group-monitor/group-monitor.component.ts @@ -6,7 +6,7 @@ import { Sort } from '@angular/material/sort'; import { MatSidenav } from '@angular/material/sidenav'; import { interval, Observable, Subscription } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; -import { ConfirmDialogComponent, ConfirmDialogData } from 'iqb-components'; +import { ConfirmDialogComponent, ConfirmDialogData, CustomtextService } from 'iqb-components'; import { MatSlideToggleChange } from '@angular/material/slide-toggle'; import { MatCheckboxChange } from '@angular/material/checkbox'; import { switchMap } from 'rxjs/operators'; @@ -30,7 +30,8 @@ export class GroupMonitorComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private bs: BackendService, // TODO move completely to service public gms: GroupMonitorService, - private router: Router + private router: Router, + private cts: CustomtextService ) {} ownGroup$: Observable<GroupData>; @@ -78,24 +79,26 @@ export class GroupMonitorComponent implements OnInit, OnDestroy { this.messages.push(this.commandResponseToMessage(commandResponse)); }); this.gms.commandResponses$ - .pipe(switchMap(() => interval(2000))) + .pipe(switchMap(() => interval(7000))) .subscribe(() => this.messages.shift()); } private commandResponseToMessage(commandResponse: CommandResponse): UIMessage { + const command = this.cts.getCustomText(`gm_control_${commandResponse.commandType}`) || commandResponse.commandType; + const successWarning = this.cts.getCustomText(`gm_control_${commandResponse.commandType}_success_warning`) || ''; if (!commandResponse.testIds.length) { return { level: 'warning', text: 'Keine Tests Betroffen von: `%s`', customtext: 'gm_message_no_session_affected_by_command', - replacements: [commandResponse.commandType, commandResponse.testIds.length.toString(10)] + replacements: [command, commandResponse.testIds.length.toString(10)] }; } return { - level: 'warning', - text: '`%s` and `%s` tests gesendet!', + level: successWarning ? 'warning' : 'info', + text: '`%s` an `%s` tests gesendet! %s', customtext: 'gm_message_command_sent_n_sessions', - replacements: [commandResponse.commandType, commandResponse.testIds.length.toString(10)] + replacements: [command, commandResponse.testIds.length.toString(10), successWarning] }; } @@ -187,7 +190,7 @@ export class GroupMonitorComponent implements OnInit, OnDestroy { if (confirmed) { this.isClosing = true; this.gms.finishEverything() - .add(() => { + .subscribe(() => { setTimeout(() => { this.router.navigateByUrl('/r/login'); }, 5000); // go away }); } @@ -195,22 +198,19 @@ export class GroupMonitorComponent implements OnInit, OnDestroy { } testCommandGoto(): void { - this.gms.testCommandGoto(this.selectedElement); + if (!this.selectedElement?.element?.blockId) { + this.messages.push({ + level: 'warning', + customtext: 'gm_test_command_no_selected_block', + text: 'Kein Zielblock ausgewählt' + }); + } else { + this.gms.testCommandGoto(this.selectedElement); + } } unlockCommand(): void { this.gms.testCommandUnlock(); - // .subscribe(commandResponse => { - // if (commandResponse.error) { - // const plural = this.gms.sessions.length > 1; - // this.addWarning('reload-some-clients', - // `${plural ? this.gms.sessions.length : 'Ein'} Test${plural ? 's' : ''} - // wurde${plural ? 'n' : ''} entsperrt. ${plural ? 'Die' : 'Der'} Teilnehmer - // ${plural ? 'müssen' : 'muss'} die Webseite aufrufen bzw. neuladen, - // damit ${plural ? 'die' : 'der'} Test${plural ? 's' : ''} wieder aufgenommen werden - // ${plural ? 'können' : 'kann'}!`); - // } - // }); } toggleChecked(checked: boolean, session: TestSession): void { diff --git a/src/app/group-monitor/group-monitor.interfaces.ts b/src/app/group-monitor/group-monitor.interfaces.ts index a632843833ccc8dec6397629a59d9081044055ae..a6588d89dc53ac572bedc81d59428fbf4851e711 100644 --- a/src/app/group-monitor/group-monitor.interfaces.ts +++ b/src/app/group-monitor/group-monitor.interfaces.ts @@ -157,7 +157,7 @@ export interface UIMessage { level: 'error' | 'warning' | 'info' | 'success'; text: string; customtext: string; - replacements: string[] + replacements?: string[] } export interface CommandResponse { diff --git a/src/app/group-monitor/group-monitor.service.ts b/src/app/group-monitor/group-monitor.service.ts index 19d25251768a43b67180136c099c726112cd85fe..9cdcbd38dce73750776190fc20bcef7d1761d20a 100644 --- a/src/app/group-monitor/group-monitor.service.ts +++ b/src/app/group-monitor/group-monitor.service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@angular/core'; import { - BehaviorSubject, combineLatest, Observable, Subject, Subscription, zip + BehaviorSubject, combineLatest, Observable, Subject, zip } from 'rxjs'; import { Sort } from '@angular/material/sort'; -import { map, switchMap, tap } from 'rxjs/operators'; +import { + delay, flatMap, map, switchMap, tap +} from 'rxjs/operators'; import { BackendService } from './backend.service'; import { BookletService } from './booklet.service'; import { TestSessionService } from './test-session.service'; @@ -18,12 +20,12 @@ import { /** * func: * # checkAll - * - stop / resume usw. ohne erlaubnis-check! sonst macht alwaysAll keinen Sinn + * # stop / resume usw. ohne erlaubnis-check! sonst macht alwaysAll keinen Sinn * --> customText und alert kombinieren! * - automatisch den nächsten wählen (?) * - problem beim markieren * tidy: - * - was geben die commands zurück? + * # was geben die commands zurück? * - wie wird alles reseted? * test * polish: @@ -421,7 +423,7 @@ export class GroupMonitorService { }; } - finishEverything(): Subscription { + finishEverything(): Observable<CommandResponse> { // TODO was ist hier mit gefilterten sessions?! const getUnlockedConnectedTestIds = () => Object.values(this._sessions$.getValue()) .filter(session => !TestSessionService.hasState(session.data.testState, 'status', 'locked') && @@ -434,9 +436,10 @@ export class GroupMonitorService { .filter(session => !TestSessionService.hasState(session.data.testState, 'status', 'locked')) .map(session => session.data.testId); - return this.bs.command('terminate', [], getUnlockedConnectedTestIds()) // kill running tests - .subscribe(() => { - setTimeout(() => this.bs.lock(this.groupName, getUnlockedTestIds()), 2000); // lock everything - }); + return this.bs.command('terminate', [], getUnlockedConnectedTestIds()) + .pipe( + delay(2000), + flatMap(() => this.bs.lock(this.groupName, getUnlockedTestIds())) + ); } }