Skip to content
Snippets Groups Projects
Commit aa87850a authored by paf's avatar paf
Browse files

introduce medium and small views

parent 000f6e05
No related branches found
No related tags found
No related merge requests found
......@@ -25,7 +25,31 @@
<div class="page-body">
<div class="adminbackground">
<table class="test-view-table" matSort (matSortChange)="sortSessions($event)">
<mat-menu #rootMenu="matMenu">
<button mat-menu-item [matMenuTriggerFor]="activity">Aktivität</button>
</mat-menu>
<mat-menu #activity="matMenu">
<button mat-menu-item (click)="setDisplayOption('view', 'full')">
<mat-icon *ngIf="displayOptions.view === 'full'">check</mat-icon>
<span>Vollständig</span>
</button>
<button mat-menu-item (click)="setDisplayOption('view', 'medium')">
<mat-icon *ngIf="displayOptions.view === 'medium'">check</mat-icon>
<span>Nur Blöcke</span>
</button>
<button mat-menu-item (click)="setDisplayOption('view', 'small')">
<mat-icon *ngIf="displayOptions.view === 'small'">check</mat-icon>
<span>Kurz</span>
</button>
</mat-menu>
<button mat-icon-button [matMenuTriggerFor]="rootMenu">
Ansicht
</button>
<table class="test-view-table" matSort (matSortChange)="sortSessions($event)">
<thead>
<tr class="mat-sort-container">
......@@ -36,12 +60,17 @@
</tr>
</thead>
<test-view *ngFor="let session of sessions$ | async; trackBy: trackSession" [testStatus]="session"></test-view>
</table>
<test-view
*ngFor="let session of sessions$ | async; trackBy: trackSession"
[testStatus]="session"
[displayOptions]="displayOptions"
>
</test-view>
</table>
<!-- <p>Connected Admins: {{clientCount$ | async}}</p>-->
<!-- <pre>{{sessions$ | async | json}}</pre>-->
</div>
</div>
</div>
import {Component, OnDestroy, OnInit} from '@angular/core';
import {BackendService} from './backend.service';
import {BehaviorSubject, combineLatest, Observable, Subject, Subscription} from 'rxjs';
import {GroupData, TestSession} from './group-monitor.interfaces';
import {GroupData, TestSession, TestViewDisplayOptions} from './group-monitor.interfaces';
import {ActivatedRoute} from '@angular/router';
import {ConnectionStatus} from './websocket-backend.service';
import {map} from 'rxjs/operators';
import {Sort} from '@angular/material/sort';
type TestViewDisplayOptionKey = 'view';
@Component({
selector: 'app-group-monitor',
templateUrl: './group-monitor.component.html',
......@@ -25,6 +27,10 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
sortBy$: Subject<Sort>;
sessions$: Observable<TestSession[]>;
displayOptions: TestViewDisplayOptions = {
view: 'full'
};
constructor(
private route: ActivatedRoute,
private bs: BackendService,
......@@ -87,4 +93,10 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
this.sortBy$.next(sort);
}
setDisplayOption(option: TestViewDisplayOptionKey, value: TestViewDisplayOptions[TestViewDisplayOptionKey]) {
this.displayOptions[option] = value;
}
}
......@@ -64,3 +64,7 @@ export interface GroupData {
name: string;
label: string;
}
export interface TestViewDisplayOptions {
view: 'full' | 'medium' | 'small';
}
......@@ -16,15 +16,16 @@ import {MatIconModule} from '@angular/material/icon';
import {MatBadgeModule} from '@angular/material/badge';
import {FlexModule} from '@angular/flex-layout';
import {MatSortModule} from '@angular/material/sort';
import {MatMenuModule} from '@angular/material/menu';
import {MatButtonModule} from '@angular/material/button';
@NgModule({
declarations: [
GroupMonitorComponent,
TestViewComponent
],
declarations: [
GroupMonitorComponent,
TestViewComponent,
],
imports: [
CommonModule,
GroupMonitorRoutingModule,
......@@ -35,12 +36,14 @@ import {MatSortModule} from '@angular/material/sort';
MatIconModule,
MatBadgeModule,
FlexModule,
MatSortModule
MatSortModule,
MatMenuModule,
MatButtonModule,
],
providers: [
BackendService,
BookletService
],
providers: [
BackendService,
BookletService
],
})
export class GroupMonitorModule {
}
......@@ -49,6 +49,10 @@ mat-icon + h1 {
text-align: center;
}
.unit.aggregated {
width: 5em;
}
.locked .unit {
background: #333333;
}
......
......@@ -51,42 +51,62 @@
<td *ngIf="booklet.units as testlet">
<div class="units-container" [class]="hasState(testStatus.testState, 'status', 'locked') ? 'locked' : ''">
<div class="units">
<ng-container *ngTemplateOutlet="testletTemplate; context: {$implicit: testlet}"></ng-container>
</div>
<div
class="units-container"
[class]="hasState(testStatus.testState, 'status', 'locked') ? 'locked' : ''"
[ngSwitch]="displayOptions.view"
>
<div class="units full" *ngSwitchCase="'full'" >
<ng-container *ngTemplateOutlet="testletTemplate; context: {$implicit: testlet}"></ng-container>
</div>
<div class="units medium" *ngSwitchCase="'medium'" >
<ng-container *ngTemplateOutlet="bookletTemplateMedium; context: {$implicit: testlet}"></ng-container>
</div>
<div class="units small" *ngSwitchCase="'small'" >
<ng-container *ngTemplateOutlet="testletBookletSmall; context: {$implicit: testlet}"></ng-container>
</div>
</div>
<div class="featured-unit" *ngIf="(featuredUnit$| async) as unitContext; else: noFeaturedUnit">
<ng-container *ngIf="displayOptions.view === 'full'">
<ng-container *ngTemplateOutlet="featuredUnit"></ng-container>
</ng-container>
<h1>{{unitContext.parent.label || 'Aktueller Abschnitt'}}</h1>
</td>
</ng-container>
<mat-icon class="unit-badge"
*ngIf="maxTimeLeft && (maxTimeLeft[unitContext.parent.id] !== undefined)"
matBadge="{{maxTimeLeft[unitContext.parent.id].toString()}}"
matBadgeColor="accent"
matTooltip="Verbleibende Zeit"
>alarm
</mat-icon>
<ng-template #featuredUnit>
<h1>:</h1>
<div class="featured-unit" *ngIf="(featuredUnit$| async) as unitContext; else: noFeaturedUnit">
<h2>{{unitContext.unit.label || unitContext.unit.labelShort || unitContext.unit.id}}</h2>
<h1>{{unitContext.parent.label || 'Aktueller Abschnitt'}}</h1>
<mat-icon class="unit-badge"
*ngIf="hasState(testStatus.unitState, 'PRESENTATIONCOMPLETE', 'yes')"
matTooltip="Vollständig betrachtet / angehört"
>remove_red_eye
</mat-icon>
<mat-icon class="unit-badge"
*ngIf="maxTimeLeft && (maxTimeLeft[unitContext.parent.id] !== undefined)"
matBadge="{{maxTimeLeft[unitContext.parent.id].toString()}}"
matBadgeColor="accent"
matTooltip="Verbleibende Zeit"
>alarm
</mat-icon>
<mat-icon class="unit-badge"
*ngIf="hasState(testStatus.unitState, 'RESPONSESCOMPLETE', 'all')"
matTooltip="Fertig beantwortet"
>done_all
</mat-icon>
</div>
</td>
</ng-container>
<h1>:</h1>
<h2>{{unitContext.unit.label || unitContext.unit.labelShort || unitContext.unit.id}}</h2>
<mat-icon class="unit-badge"
*ngIf="hasState(testStatus.unitState, 'PRESENTATIONCOMPLETE', 'yes')"
matTooltip="Vollständig betrachtet / angehört"
>remove_red_eye
</mat-icon>
<mat-icon class="unit-badge"
*ngIf="hasState(testStatus.unitState, 'RESPONSESCOMPLETE', 'all')"
matTooltip="Fertig beantwortet"
>done_all
</mat-icon>
</div>
</ng-template>
<ng-template #noFeaturedUnit>
</ng-template>
......@@ -115,15 +135,67 @@
[class]="(testStatus.unitName === testletOrUnit.id) ? 'unit current': 'unit'"
matTooltip="{{testletOrUnit.label}}"
matTooltipPosition="above"
>{{testletOrUnit.labelShort || "&nbsp;"}}
>{{testletOrUnit.labelShort || "&nbsp;"}}
</span>
<span *ngSwitchCase="'testlet'" class="testlet" matTooltip="{{testletOrUnit.label}}">
<ng-container *ngTemplateOutlet="testletTemplate; context: {$implicit: testletOrUnit}"></ng-container>
</span>
<!-- <pre>{{testletOrUnit|json}}</pre>-->
</ng-container>
</ng-template>
<ng-template #bookletTemplateMedium let-testlet>
<ng-container *ngFor="let testletOrUnit of testlet.children; let i = index; trackBy: trackUnits" [ngSwitch]="getTestletType(testletOrUnit)">
<span *ngSwitchCase="'unit'"
[class]="(testStatus.unitName === testletOrUnit.id) ? 'unit current': 'unit'"
matTooltip="{{testletOrUnit.label}}"
matTooltipPosition="above"
>·
</span>
<span *ngSwitchCase="'testlet'" class="testlet" matTooltip="{{testletOrUnit.label}}">
<ng-container *ngIf="featuredUnit$ | async as featuredUnit; else: nonFeaturedTestlet">
<span
*ngIf="featuredUnit.parent.id === testletOrUnit.id; else: nonFeaturedTestlet"
class="unit current aggregated"
matTooltip="{{featuredUnit.unit.label}}"
matTooltipPosition="above"
>{{featuredUnit.indexInTestlet + 1}} / {{testletOrUnit.children.length}}
</span>
</ng-container>
<ng-template #nonFeaturedTestlet>
<span class="unit aggregated">{{testletOrUnit.children.length}}</span>
</ng-template>
</span>
</ng-container>
</ng-template>
<ng-template #testletBookletSmall let-testlet>
<span
class="testlet" *ngIf="featuredUnit$ | async as featuredUnit; else: nonFeaturedTestlet"
matTooltip="{{featuredUnit.parent.label}}"
>
<span
class="unit current aggregated"
matTooltip="{{featuredUnit.unit.label}}"
matTooltipPosition="above"
>
{{featuredUnit.indexInBooklet + 1}} / {{countTestletChildren(testlet)}}
</span>
</span>
<ng-template #nonFeaturedTestlet>
<span class="testlet" >
<span class="unit aggregated">{{countTestletChildren(testlet)}}</span>
</span>
</ng-template>
</ng-template>
......
import {Component, Input, OnChanges, OnDestroy, OnInit, SimpleChange} from '@angular/core';
import {BookletService} from '../booklet.service';
import {combineLatest, Observable, Subject} from 'rxjs';
import {Booklet, TestSession, Testlet, Unit} from '../group-monitor.interfaces';
import {Booklet, TestSession, Testlet, Unit, TestViewDisplayOptions} from '../group-monitor.interfaces';
import {map} from 'rxjs/operators';
import {TestMode} from '../../config/test-mode';
......@@ -11,6 +11,13 @@ function isUnit(testletOrUnit: Testlet|Unit): testletOrUnit is Unit {
return !('children' in testletOrUnit);
}
interface FeaturedUnitContext {
unit: Unit,
parent: Testlet,
indexInBooklet: number,
indexInTestlet: number
}
@Component({
selector: 'test-view',
templateUrl: './test-view.component.html',
......@@ -19,13 +26,11 @@ function isUnit(testletOrUnit: Testlet|Unit): testletOrUnit is Unit {
export class TestViewComponent implements OnInit, OnDestroy, OnChanges {
@Input() testStatus: TestSession;
@Input() displayOptions: TestViewDisplayOptions;
private testStatus$: Subject<TestSession>;
public booklet$: Observable<boolean|Booklet>;
public featuredUnit$: Observable<{
unit: Unit,
parent: Testlet
}|null>;
public featuredUnit$: Observable<FeaturedUnitContext|null>;
private childrenSubscription;
......@@ -72,6 +77,7 @@ export class TestViewComponent implements OnInit, OnDestroy, OnChanges {
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
console.log('CHANGE', changes);
this.testStatus$.next(this.testStatus);
this.maxTimeLeft = this.parseJsonState(this.testStatus.testState, 'MAXTIMELEFT');
}
......@@ -138,7 +144,7 @@ export class TestViewComponent implements OnInit, OnDestroy, OnChanges {
}
findUnitByName(testlet: Testlet, unitName: String): {unit: Unit|null, parent: Testlet} {
findUnitByName(testlet: Testlet, unitName: String, indexOffset: number = null): FeaturedUnitContext|null {
for (let i = 0; i < testlet.children.length; i++) {
......@@ -149,19 +155,44 @@ export class TestViewComponent implements OnInit, OnDestroy, OnChanges {
if (testletOrUnit.id === unitName) {
return {
parent: testlet,
unit: testletOrUnit
unit: testletOrUnit,
indexInBooklet: indexOffset + i,
indexInTestlet: i
}
}
} else {
const findInChildren = this.findUnitByName(testletOrUnit, unitName);
const findInChildren = this.findUnitByName(testletOrUnit, unitName, indexOffset + i);
if (findInChildren !== null) {
return findInChildren;
}
indexOffset += testletOrUnit.children.length;
}
}
return null;
}
countTestletChildren(testlet: Testlet): number {
let childrenCount = 1;
for (let i = 0; i < testlet.children.length; i++) {
const testletOrUnit = testlet.children[i];
if (isUnit(testletOrUnit)) {
childrenCount++;
} else {
childrenCount += this.countTestletChildren(testletOrUnit);
}
}
return childrenCount;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment