Merge pull request #4472 from tsiegleauq/countdown-control-component
Add Countdowns in projector detail
This commit is contained in:
commit
5fbf682754
@ -1,5 +1,9 @@
|
|||||||
<div id="countdown" [ngClass]="{
|
<div
|
||||||
'negative': seconds <= 0,
|
id="countdown"
|
||||||
'warning_time': seconds <= warningTime && seconds > 0 }">
|
[ngClass]="{
|
||||||
|
negative: seconds <= 0,
|
||||||
|
warning_time: seconds <= warningTime && seconds > 0
|
||||||
|
}"
|
||||||
|
>
|
||||||
{{ time }}
|
{{ time }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,9 +10,4 @@
|
|||||||
color: mat-color($contrast, A200) !important;
|
color: mat-color($contrast, A200) !important;
|
||||||
background-color: mat-color($contrast, 500) !important;
|
background-color: mat-color($contrast, 500) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-menu-item.projector-active,
|
|
||||||
.projector-active > .mat-icon {
|
|
||||||
color: mat-color($primary) !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
isProjectable,
|
isProjectable,
|
||||||
isProjectorElementBuildDeskriptor
|
isProjectorElementBuildDeskriptor
|
||||||
} from 'app/site/base/projectable';
|
} from 'app/site/base/projectable';
|
||||||
|
import { Projector } from 'app/shared/models/core/projector';
|
||||||
import { ProjectorService } from 'app/core/core-services/projector.service';
|
import { ProjectorService } from 'app/core/core-services/projector.service';
|
||||||
import { ProjectionDialogService } from 'app/core/ui-services/projection-dialog.service';
|
import { ProjectionDialogService } from 'app/core/ui-services/projection-dialog.service';
|
||||||
|
|
||||||
@ -45,6 +46,12 @@ export class ProjectorButtonComponent implements OnInit {
|
|||||||
@Input()
|
@Input()
|
||||||
public menuItem = false;
|
public menuItem = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-define projection target
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
public projector: Projector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The constructor
|
* The constructor
|
||||||
*/
|
*/
|
||||||
@ -68,9 +75,21 @@ export class ProjectorButtonComponent implements OnInit {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
if (this.object) {
|
if (this.object) {
|
||||||
|
if (this.projector) {
|
||||||
|
// if the projection target was defines before
|
||||||
|
if (this.isProjected()) {
|
||||||
|
// remove the projected object
|
||||||
|
this.projectorService.removeFrom(this.projector, this.object);
|
||||||
|
} else {
|
||||||
|
// instantly project the object
|
||||||
|
this.projectorService.projectOn(this.projector, this.object);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// open the projection dialog
|
||||||
this.projectionDialogService.openProjectDialogFor(this.object);
|
this.projectionDialogService.openProjectDialogFor(this.object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -1,12 +1,71 @@
|
|||||||
<div *ngIf="countdown">
|
<mat-card *ngIf="countdown">
|
||||||
<os-countdown-time [countdown]="countdown" [warningTime]="warningTime"></os-countdown-time>
|
<div class="grid-wrapper">
|
||||||
<button type="button" [disabled]="!canStop()" mat-icon-button (click)="stop($event)">
|
<div class="projector-button">
|
||||||
|
<os-projector-button [object]="countdown" [projector]="projector"></os-projector-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="title">{{ countdown.getTitle() | translate }}</div>
|
||||||
|
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="small-mat-icon-button"
|
||||||
|
mat-icon-button
|
||||||
|
(click)="onBringDialog()"
|
||||||
|
matTooltip="{{ 'Open projection dialog' | translate }}"
|
||||||
|
>
|
||||||
|
<mat-icon>
|
||||||
|
open_in_new
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="small-mat-icon-button" mat-icon-button (click)="onEdit()">
|
||||||
|
<mat-icon>
|
||||||
|
edit
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="small-mat-icon-button" mat-icon-button (click)="onDelete()">
|
||||||
|
<mat-icon>
|
||||||
|
close
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="timer">
|
||||||
|
<os-countdown-time
|
||||||
|
class="larger-countdown"
|
||||||
|
[countdown]="countdown"
|
||||||
|
[warningTime]="warningTime"
|
||||||
|
></os-countdown-time>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="small-mat-icon-button"
|
||||||
|
mat-icon-button
|
||||||
|
[disabled]="!canStop()"
|
||||||
|
(click)="stop($event)"
|
||||||
|
>
|
||||||
<mat-icon>stop</mat-icon>
|
<mat-icon>stop</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="!countdown.running" type="button" mat-icon-button (click)="start($event)">
|
<button
|
||||||
|
type="button"
|
||||||
|
class="small-mat-icon-button"
|
||||||
|
mat-icon-button
|
||||||
|
(click)="start($event)"
|
||||||
|
*ngIf="!countdown.running"
|
||||||
|
>
|
||||||
<mat-icon>play_arrow</mat-icon>
|
<mat-icon>play_arrow</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="countdown.running" type="button" mat-icon-button (click)="pause($event)">
|
<button
|
||||||
|
type="button"
|
||||||
|
class="small-mat-icon-button"
|
||||||
|
mat-icon-button
|
||||||
|
(click)="pause($event)"
|
||||||
|
*ngIf="countdown.running"
|
||||||
|
>
|
||||||
<mat-icon>pause</mat-icon>
|
<mat-icon>pause</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-card>
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
.grid-wrapper {
|
||||||
|
display: grid;
|
||||||
|
width: 100%;
|
||||||
|
grid-template-areas:
|
||||||
|
'project title buttons'
|
||||||
|
'project controls controls';
|
||||||
|
grid-gap: 10px;
|
||||||
|
grid-template-columns: min-content auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projector-button {
|
||||||
|
grid-area: project;
|
||||||
|
margin: auto 10px auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
grid-area: title;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
grid-area: buttons;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timer {
|
||||||
|
grid-area: controls;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.larger-countdown {
|
||||||
|
font-size: 150%;
|
||||||
|
min-width: 60px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// smaller and closer mat icon buttons.
|
||||||
|
// use on button-tags
|
||||||
|
// TODO: Somehow does not apply when in style.scss
|
||||||
|
.small-mat-icon-button {
|
||||||
|
$size: 20px;
|
||||||
|
margin: 0 5px;
|
||||||
|
position: relative;
|
||||||
|
width: $size;
|
||||||
|
height: $size;
|
||||||
|
|
||||||
|
::ng-deep .mat-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
line-height: normal;
|
||||||
|
width: $size;
|
||||||
|
height: $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
font-size: $size;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { MatSnackBar } from '@angular/material';
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
@ -8,26 +8,60 @@ import { BaseViewComponent } from '../../../base/base-view';
|
|||||||
import { ViewCountdown } from '../../models/view-countdown';
|
import { ViewCountdown } from '../../models/view-countdown';
|
||||||
import { CountdownRepositoryService } from 'app/core/repositories/projector/countdown-repository.service';
|
import { CountdownRepositoryService } from 'app/core/repositories/projector/countdown-repository.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
|
import { Projector } from 'app/shared/models/core/projector';
|
||||||
|
import { ProjectionDialogService } from 'app/core/ui-services/projection-dialog.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-countdown-controls',
|
selector: 'os-countdown-controls',
|
||||||
templateUrl: './countdown-controls.component.html'
|
templateUrl: './countdown-controls.component.html',
|
||||||
|
styleUrls: ['./countdown-controls.component.scss']
|
||||||
})
|
})
|
||||||
export class CountdownControlsComponent extends BaseViewComponent {
|
export class CountdownControlsComponent extends BaseViewComponent {
|
||||||
|
/**
|
||||||
|
* Countdown as input
|
||||||
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
public countdown: ViewCountdown;
|
public countdown: ViewCountdown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit event
|
||||||
|
*/
|
||||||
|
@Output()
|
||||||
|
public editEvent = new EventEmitter<ViewCountdown>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre defined projection target (if any)
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
public projector: Projector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time in seconds to make the countdown orange, is the countdown is below this value.
|
* The time in seconds to make the countdown orange, is the countdown is below this value.
|
||||||
*/
|
*/
|
||||||
public warningTime: number;
|
public warningTime: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param titleService
|
||||||
|
* @param translate
|
||||||
|
* @param matSnackBar
|
||||||
|
* @param repo
|
||||||
|
* @param configService
|
||||||
|
* @param promptService
|
||||||
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
titleService: Title,
|
titleService: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
matSnackBar: MatSnackBar,
|
matSnackBar: MatSnackBar,
|
||||||
private repo: CountdownRepositoryService,
|
private repo: CountdownRepositoryService,
|
||||||
private configService: ConfigService
|
private configService: ConfigService,
|
||||||
|
private promptService: PromptService,
|
||||||
|
private projectionDialogService: ProjectionDialogService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar);
|
super(titleService, translate, matSnackBar);
|
||||||
|
|
||||||
@ -64,4 +98,29 @@ export class CountdownControlsComponent extends BaseViewComponent {
|
|||||||
public canStop(): boolean {
|
public canStop(): boolean {
|
||||||
return this.countdown.running || this.countdown.countdown_time !== this.countdown.default_time;
|
return this.countdown.running || this.countdown.countdown_time !== this.countdown.default_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires an edit event
|
||||||
|
*/
|
||||||
|
public onEdit(): void {
|
||||||
|
this.editEvent.next(this.countdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Brings the projection dialog
|
||||||
|
*/
|
||||||
|
public onBringDialog(): void {
|
||||||
|
this.projectionDialogService.openProjectDialogFor(this.countdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On delete button
|
||||||
|
*/
|
||||||
|
public async onDelete(): Promise<void> {
|
||||||
|
const content =
|
||||||
|
this.translate.instant('Delete countdown') + ` ${this.translate.instant(this.countdown.title)}?`;
|
||||||
|
if (await this.promptService.open('Are you sure?', content)) {
|
||||||
|
this.repo.delete(this.countdown).then(() => {}, this.raiseError);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
<h1 mat-dialog-title>
|
||||||
|
<span translate>Countdown</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<form [formGroup]="countdownForm">
|
||||||
|
<div mat-dialog-content>
|
||||||
|
<!-- Title field -->
|
||||||
|
<mat-form-field>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
osAutofocus
|
||||||
|
placeholder="{{ 'Countdown title' | translate }}"
|
||||||
|
formControlName="title"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Description field -->
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="{{ 'Countdown description' | translate }}" formControlName="description" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Time field -->
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="{{ 'Countdown time' | translate }}" formControlName="duration" required />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions>
|
||||||
|
<!-- Submit countdown button -->
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
mat-button
|
||||||
|
color="primary"
|
||||||
|
[mat-dialog-close]="countdownForm.value"
|
||||||
|
[disabled]="!countdownForm.valid"
|
||||||
|
>
|
||||||
|
<span translate>Save</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Cancel Countdown button -->
|
||||||
|
<button type="button" mat-button [mat-dialog-close]="null">
|
||||||
|
<span translate>Cancel</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -0,0 +1,3 @@
|
|||||||
|
.mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CountdownDialogComponent, CountdownData } from './countdown-dialog.component';
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||||
|
|
||||||
|
describe('CountdownDialogComponent', () => {
|
||||||
|
let component: CountdownDialogComponent;
|
||||||
|
let fixture: ComponentFixture<CountdownDialogComponent>;
|
||||||
|
|
||||||
|
const dialogData: CountdownData = {
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
duration: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule],
|
||||||
|
declarations: [CountdownDialogComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: MatDialogRef, useValue: {} },
|
||||||
|
{
|
||||||
|
provide: MAT_DIALOG_DATA,
|
||||||
|
useValue: dialogData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CountdownDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,79 @@
|
|||||||
|
import { Component, OnInit, Inject } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { MatSnackBar, MAT_DIALOG_DATA } from '@angular/material';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Countdown data for the form
|
||||||
|
*/
|
||||||
|
export interface CountdownData {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
duration: string;
|
||||||
|
count?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog component for countdowns
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'os-countdown-dialog',
|
||||||
|
templateUrl: './countdown-dialog.component.html',
|
||||||
|
styleUrls: ['./countdown-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class CountdownDialogComponent extends BaseViewComponent implements OnInit {
|
||||||
|
/**
|
||||||
|
* The form data
|
||||||
|
*/
|
||||||
|
public countdownForm: FormGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the default time which was set in the config
|
||||||
|
*/
|
||||||
|
private defaultTime: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param title Title service. Required by parent
|
||||||
|
* @param matSnackBar Required by parent
|
||||||
|
* @param configService Read out config variables
|
||||||
|
* @param translate Required by parent
|
||||||
|
* @param formBuilder To build the form
|
||||||
|
* @param durationService Converts duration numbers to string
|
||||||
|
* @param data The mat dialog data, contains the values to display (if any)
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
title: Title,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
|
configService: ConfigService,
|
||||||
|
translate: TranslateService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private durationService: DurationService,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: CountdownData
|
||||||
|
) {
|
||||||
|
super(title, translate, matSnackBar);
|
||||||
|
this.defaultTime = configService.instant<number>('projector_default_countdown');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init. Creates the form
|
||||||
|
*/
|
||||||
|
public ngOnInit(): void {
|
||||||
|
const time = this.data.duration || this.durationService.durationToString(this.defaultTime, 'm');
|
||||||
|
const title = this.data.title || `${this.translate.instant('Countdown')} ${this.data.count + 1}`;
|
||||||
|
|
||||||
|
this.countdownForm = this.formBuilder.group({
|
||||||
|
title: [title, Validators.required],
|
||||||
|
description: [this.data.description],
|
||||||
|
// TODO: custom form validation. This needs to be a valid minute duration
|
||||||
|
duration: [time, Validators.required]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,137 +0,0 @@
|
|||||||
<os-head-bar [nav]="false" [goBack]="true" [mainButton]="true" (mainEvent)="onPlusButton()">
|
|
||||||
<!-- Title -->
|
|
||||||
<div class="title-slot">
|
|
||||||
<h2 translate>Countdowns</h2>
|
|
||||||
</div>
|
|
||||||
</os-head-bar>
|
|
||||||
|
|
||||||
<div class="head-spacer"></div>
|
|
||||||
<mat-card *ngIf="countdownToCreate">
|
|
||||||
<mat-card-title translate>New countdown</mat-card-title>
|
|
||||||
<mat-card-content>
|
|
||||||
<form [formGroup]="createForm" (keydown)="onKeyDownCreate($event)">
|
|
||||||
<p>
|
|
||||||
<mat-form-field>
|
|
||||||
<input formControlName="title" matInput placeholder="{{ 'Title' | translate }}" required />
|
|
||||||
<mat-hint *ngIf="!createForm.controls.title.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<mat-form-field>
|
|
||||||
<input formControlName="description" matInput placeholder="{{ 'Description' | translate }}" />
|
|
||||||
</mat-form-field>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<mat-form-field>
|
|
||||||
<input formControlName="default_time" matInput placeholder="{{ 'Time' | translate }}" required />
|
|
||||||
<mat-hint *ngIf="!createForm.controls.default_time.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</mat-card-content>
|
|
||||||
<mat-card-actions>
|
|
||||||
<button mat-button (click)="create()">
|
|
||||||
<span translate>Save</span>
|
|
||||||
</button>
|
|
||||||
<button mat-button (click)="onCancelCreate()">
|
|
||||||
<span translate>Cancel</span>
|
|
||||||
</button>
|
|
||||||
</mat-card-actions>
|
|
||||||
</mat-card>
|
|
||||||
|
|
||||||
<mat-accordion class="os-card">
|
|
||||||
<mat-expansion-panel
|
|
||||||
*ngFor="let countdown of countdowns"
|
|
||||||
(opened)="openId = countdown.id"
|
|
||||||
(closed)="panelClosed(countdown)"
|
|
||||||
[expanded]="openId === countdown.id"
|
|
||||||
multiple="false"
|
|
||||||
>
|
|
||||||
<!-- Projector button and countdown description-->
|
|
||||||
<mat-expansion-panel-header>
|
|
||||||
<mat-panel-title>
|
|
||||||
<div class="header-container">
|
|
||||||
<div class="header-projector-button">
|
|
||||||
<os-projector-button [object]="countdown"></os-projector-button>
|
|
||||||
</div>
|
|
||||||
<div class="header-name">
|
|
||||||
{{ countdown.getTitle() | translate }}
|
|
||||||
</div>
|
|
||||||
<div class="header-controls">
|
|
||||||
<os-countdown-controls [countdown]="countdown"></os-countdown-controls>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</mat-panel-title>
|
|
||||||
</mat-expansion-panel-header>
|
|
||||||
<form [formGroup]="updateForm" *ngIf="editId === countdown.id" (keydown)="onKeyDownUpdate($event)">
|
|
||||||
<h5 translate>Edit countdown</h5>
|
|
||||||
<p>
|
|
||||||
<mat-form-field>
|
|
||||||
<input formControlName="title" matInput placeholder="{{ 'Title' | translate }}" required />
|
|
||||||
<mat-hint *ngIf="!updateForm.controls.title.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<mat-form-field>
|
|
||||||
<input
|
|
||||||
formControlName="description"
|
|
||||||
matInput
|
|
||||||
placeholder="{{ 'Description' | translate }}"
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<mat-form-field>
|
|
||||||
<input formControlName="default_time" matInput placeholder="{{ 'Time' | translate }}" required />
|
|
||||||
<mat-hint *ngIf="!updateForm.controls.default_time.valid">
|
|
||||||
<span translate>Required</span>
|
|
||||||
</mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
<mat-action-row>
|
|
||||||
<button
|
|
||||||
*ngIf="editId !== countdown.id"
|
|
||||||
mat-button
|
|
||||||
class="on-transition-fade"
|
|
||||||
(click)="onEditButton(countdown)"
|
|
||||||
mat-icon-button
|
|
||||||
>
|
|
||||||
<mat-icon>edit</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
*ngIf="editId === countdown.id"
|
|
||||||
mat-button
|
|
||||||
class="on-transition-fade"
|
|
||||||
(click)="onCancelUpdate()"
|
|
||||||
mat-icon-button
|
|
||||||
>
|
|
||||||
<mat-icon>close</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
*ngIf="editId === countdown.id"
|
|
||||||
mat-button
|
|
||||||
class="on-transition-fade"
|
|
||||||
(click)="onSaveButton(countdown)"
|
|
||||||
mat-icon-button
|
|
||||||
>
|
|
||||||
<mat-icon>save</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button mat-button class="on-transition-fade" (click)="onDeleteButton(countdown)" mat-icon-button>
|
|
||||||
<mat-icon>delete</mat-icon>
|
|
||||||
</button>
|
|
||||||
</mat-action-row>
|
|
||||||
</mat-expansion-panel>
|
|
||||||
</mat-accordion>
|
|
||||||
|
|
||||||
<mat-card *ngIf="countdowns.length === 0">
|
|
||||||
<mat-card-content>
|
|
||||||
<div class="no-content" translate>No countdowns</div>
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
@ -1,41 +0,0 @@
|
|||||||
.head-spacer {
|
|
||||||
width: 100%;
|
|
||||||
height: 60px;
|
|
||||||
line-height: 60px;
|
|
||||||
text-align: right;
|
|
||||||
background: white; /* TODO: remove this and replace with theme */
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
mat-card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-container {
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: auto;
|
|
||||||
grid-template-columns: 40px 1fr 2fr;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
grid-row-start: 1;
|
|
||||||
grid-row-end: span 1;
|
|
||||||
grid-column-end: span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-projector-button {
|
|
||||||
grid-column-start: 1;
|
|
||||||
grid-column-end: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-name {
|
|
||||||
grid-column-start: 2;
|
|
||||||
grid-column-end: 3;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-controls {
|
|
||||||
grid-column-start: 3;
|
|
||||||
grid-column-end: 4;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { CountdownListComponent } from './countdown-list.component';
|
|
||||||
import { E2EImportsModule } from 'e2e-imports.module';
|
|
||||||
import { CountdownControlsComponent } from '../countdown-controls/countdown-controls.component';
|
|
||||||
|
|
||||||
describe('CountdownListComponent', () => {
|
|
||||||
let component: CountdownListComponent;
|
|
||||||
let fixture: ComponentFixture<CountdownListComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
imports: [E2EImportsModule],
|
|
||||||
declarations: [CountdownListComponent, CountdownControlsComponent]
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(CountdownListComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,218 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { Title } from '@angular/platform-browser';
|
|
||||||
import { MatSnackBar } from '@angular/material';
|
|
||||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
|
||||||
import { BaseViewComponent } from '../../../base/base-view';
|
|
||||||
import { ViewCountdown } from '../../models/view-countdown';
|
|
||||||
import { CountdownRepositoryService } from 'app/core/repositories/projector/countdown-repository.service';
|
|
||||||
import { Countdown } from 'app/shared/models/core/countdown';
|
|
||||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List view for countdowns.
|
|
||||||
*/
|
|
||||||
@Component({
|
|
||||||
selector: 'os-countdown-list',
|
|
||||||
templateUrl: './countdown-list.component.html',
|
|
||||||
styleUrls: ['./countdown-list.component.scss']
|
|
||||||
})
|
|
||||||
export class CountdownListComponent extends BaseViewComponent implements OnInit {
|
|
||||||
public countdownToCreate: Countdown | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source of the Data
|
|
||||||
*/
|
|
||||||
public countdowns: ViewCountdown[] = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current focussed formgroup
|
|
||||||
*/
|
|
||||||
public updateForm: FormGroup;
|
|
||||||
|
|
||||||
public createForm: FormGroup;
|
|
||||||
|
|
||||||
public openId: number | null;
|
|
||||||
public editId: number | null;
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
titleService: Title,
|
|
||||||
protected translate: TranslateService, // protected required for ng-translate-extract
|
|
||||||
matSnackBar: MatSnackBar,
|
|
||||||
private repo: CountdownRepositoryService,
|
|
||||||
private formBuilder: FormBuilder,
|
|
||||||
private promptService: PromptService,
|
|
||||||
private durationService: DurationService
|
|
||||||
) {
|
|
||||||
super(titleService, translate, matSnackBar);
|
|
||||||
|
|
||||||
const form = {
|
|
||||||
description: [''],
|
|
||||||
default_time: ['', Validators.required],
|
|
||||||
title: ['', Validators.required]
|
|
||||||
};
|
|
||||||
this.createForm = this.formBuilder.group(form);
|
|
||||||
this.updateForm = this.formBuilder.group(form);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init function.
|
|
||||||
*
|
|
||||||
* Sets the title and gets/observes countdowns from DataStore
|
|
||||||
*/
|
|
||||||
public ngOnInit(): void {
|
|
||||||
super.setTitle('Countdowns');
|
|
||||||
this.repo.getViewModelListObservable().subscribe(newCountdowns => {
|
|
||||||
this.countdowns = newCountdowns;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new countdown.
|
|
||||||
*/
|
|
||||||
public onPlusButton(): void {
|
|
||||||
if (!this.countdownToCreate) {
|
|
||||||
this.createForm.reset();
|
|
||||||
this.createForm.setValue({
|
|
||||||
description: '',
|
|
||||||
title: '',
|
|
||||||
default_time: '1:00 m'
|
|
||||||
});
|
|
||||||
this.countdownToCreate = new Countdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler when clicking on create to create a new statute paragraph
|
|
||||||
*/
|
|
||||||
public create(): void {
|
|
||||||
if (this.createForm.valid) {
|
|
||||||
let default_time = this.durationService.stringToDuration(this.createForm.value.default_time, 'm');
|
|
||||||
if (default_time === 0) {
|
|
||||||
default_time = 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newValues: Partial<Countdown> = {
|
|
||||||
description: this.createForm.value.description,
|
|
||||||
title: this.createForm.value.title,
|
|
||||||
default_time: default_time
|
|
||||||
};
|
|
||||||
newValues.countdown_time = default_time;
|
|
||||||
this.countdownToCreate.patchValues(newValues);
|
|
||||||
this.repo.create(this.countdownToCreate).then(() => {
|
|
||||||
this.countdownToCreate = null;
|
|
||||||
}, this.raiseError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executed on edit button
|
|
||||||
* @param countdown
|
|
||||||
*/
|
|
||||||
public onEditButton(countdown: ViewCountdown): void {
|
|
||||||
this.editId = countdown.id;
|
|
||||||
|
|
||||||
this.updateForm.setValue({
|
|
||||||
description: countdown.description,
|
|
||||||
title: this.translate.instant(countdown.title),
|
|
||||||
default_time: this.durationService.durationToString(countdown.default_time, 'm')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the countdown
|
|
||||||
* @param countdown The countdown to save
|
|
||||||
*/
|
|
||||||
public onSaveButton(countdown: ViewCountdown): void {
|
|
||||||
if (this.updateForm.valid) {
|
|
||||||
let default_time = this.durationService.stringToDuration(this.updateForm.value.default_time, 'm');
|
|
||||||
if (default_time === 0) {
|
|
||||||
default_time = 60;
|
|
||||||
}
|
|
||||||
const newValues: Partial<Countdown> = {
|
|
||||||
title: this.updateForm.value.title,
|
|
||||||
description: this.updateForm.value.description,
|
|
||||||
default_time: default_time
|
|
||||||
};
|
|
||||||
if (!countdown.running) {
|
|
||||||
newValues.countdown_time = default_time;
|
|
||||||
}
|
|
||||||
this.repo.update(newValues, countdown).then(() => {
|
|
||||||
this.openId = this.editId = null;
|
|
||||||
}, this.raiseError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is executed, when the delete button is pressed
|
|
||||||
*
|
|
||||||
* @param countdown The countdown to delete
|
|
||||||
*/
|
|
||||||
public async onDeleteButton(countdown: ViewCountdown): Promise<void> {
|
|
||||||
const title = this.translate.instant('Are you sure you want to delete this countdown?');
|
|
||||||
const content = countdown.title;
|
|
||||||
if (await this.promptService.open(title, content)) {
|
|
||||||
this.repo.delete(countdown).then(() => (this.openId = this.editId = null), this.raiseError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is executed when a mat-extension-panel is closed
|
|
||||||
*
|
|
||||||
* @param countdown the statute paragraph in the panel
|
|
||||||
*/
|
|
||||||
public panelClosed(countdown: ViewCountdown): void {
|
|
||||||
this.openId = null;
|
|
||||||
if (this.editId) {
|
|
||||||
this.onSaveButton(countdown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clicking Shift and Enter will save automatically
|
|
||||||
* clicking Escape will cancel the process
|
|
||||||
*
|
|
||||||
* @param event has the code
|
|
||||||
*/
|
|
||||||
public onKeyDownCreate(event: KeyboardEvent): void {
|
|
||||||
if (event.key === 'Enter' && event.shiftKey) {
|
|
||||||
this.create();
|
|
||||||
}
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
this.onCancelCreate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the current form action
|
|
||||||
*/
|
|
||||||
public onCancelCreate(): void {
|
|
||||||
this.countdownToCreate = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clicking Shift and Enter will save automatically
|
|
||||||
* clicking Escape will cancel the process
|
|
||||||
*
|
|
||||||
* @param event has the code
|
|
||||||
*/
|
|
||||||
public onKeyDownUpdate(event: KeyboardEvent): void {
|
|
||||||
if (event.key === 'Enter' && event.shiftKey) {
|
|
||||||
const countdown = this.countdowns.find(x => x.id === this.editId);
|
|
||||||
this.onSaveButton(countdown);
|
|
||||||
}
|
|
||||||
if (event.key === 'Escape') {
|
|
||||||
this.onCancelUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the current form action
|
|
||||||
*/
|
|
||||||
public onCancelUpdate(): void {
|
|
||||||
this.editId = null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -180,24 +180,28 @@
|
|||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|
||||||
<!-- countdowns -->
|
<!-- countdowns -->
|
||||||
<mat-expansion-panel *ngIf="countdowns.length">
|
<mat-expansion-panel>
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<span translate>Countdowns</span>
|
<span translate>Countdowns</span>
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
<mat-list>
|
<mat-list>
|
||||||
<mat-list-item
|
<mat-list-item
|
||||||
*ngFor="let countdown of countdowns"
|
*ngFor="let countdown of countdowns"
|
||||||
|
class="larger-mat-list-item"
|
||||||
[ngClass]="{ projected: isProjected(countdown) }"
|
[ngClass]="{ projected: isProjected(countdown) }"
|
||||||
>
|
>
|
||||||
<button type="button" mat-icon-button (click)="project(countdown)">
|
<os-countdown-controls
|
||||||
<mat-icon>videocam</mat-icon>
|
class="dynamic-list-entry"
|
||||||
</button>
|
[countdown]="countdown"
|
||||||
{{ countdown.getTitle() | translate }}
|
[projector]="projector.projector"
|
||||||
|
(editEvent)="openCountdownDialog($event)"
|
||||||
|
></os-countdown-controls>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
</mat-list>
|
</mat-list>
|
||||||
<mat-action-row>
|
<mat-action-row>
|
||||||
<button type="button" mat-icon-button routerLink="/projectors/countdowns">
|
<button type="button" mat-button (click)="openCountdownDialog()">
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
|
<span translate>Add countdown</span>
|
||||||
</button>
|
</button>
|
||||||
</mat-action-row>
|
</mat-action-row>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
@ -121,3 +121,15 @@
|
|||||||
.drop-list.cdk-drop-list-dragging .drop-list-entry:not(.cdk-drag-placeholder) {
|
.drop-list.cdk-drop-list-dragging .drop-list-entry:not(.cdk-drag-placeholder) {
|
||||||
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
|
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.larger-mat-list-item {
|
||||||
|
height: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.larger-mat-list-item + .larger-mat-list-item {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamic-list-entry {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { MatSnackBar } from '@angular/material';
|
import { MatSnackBar, MatDialog } from '@angular/material';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
@ -11,6 +11,11 @@ import {
|
|||||||
} from 'app/core/repositories/projector/projector-repository.service';
|
} from 'app/core/repositories/projector/projector-repository.service';
|
||||||
import { ViewProjector } from '../../models/view-projector';
|
import { ViewProjector } from '../../models/view-projector';
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
|
import { Countdown } from 'app/shared/models/core/countdown';
|
||||||
|
import { CountdownDialogComponent, CountdownData } from '../countdown-dialog/countdown-dialog.component';
|
||||||
|
import { CurrentListOfSpeakersSlideService } from '../../services/current-list-of-of-speakers-slide.service';
|
||||||
|
import { CurrentSpeakerChyronSlideService } from '../../services/current-speaker-chyron-slide.service';
|
||||||
|
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||||
import { ProjectorService } from 'app/core/core-services/projector.service';
|
import { ProjectorService } from 'app/core/core-services/projector.service';
|
||||||
import { moveItemInArray, CdkDragDrop } from '@angular/cdk/drag-drop';
|
import { moveItemInArray, CdkDragDrop } from '@angular/cdk/drag-drop';
|
||||||
import { ProjectorElement } from 'app/shared/models/core/projector';
|
import { ProjectorElement } from 'app/shared/models/core/projector';
|
||||||
@ -20,8 +25,6 @@ import { ProjectorMessageRepositoryService } from 'app/core/repositories/project
|
|||||||
import { ViewProjectorMessage } from 'app/site/projector/models/view-projector-message';
|
import { ViewProjectorMessage } from 'app/site/projector/models/view-projector-message';
|
||||||
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
||||||
import { Projectable } from 'app/site/base/projectable';
|
import { Projectable } from 'app/site/base/projectable';
|
||||||
import { CurrentListOfSpeakersSlideService } from '../../services/current-list-of-of-speakers-slide.service';
|
|
||||||
import { CurrentSpeakerChyronSlideService } from '../../services/current-speaker-chyron-slide.service';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The projector detail view.
|
* The projector detail view.
|
||||||
@ -61,6 +64,7 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
|||||||
titleService: Title,
|
titleService: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
matSnackBar: MatSnackBar,
|
matSnackBar: MatSnackBar,
|
||||||
|
private dialog: MatDialog,
|
||||||
private repo: ProjectorRepositoryService,
|
private repo: ProjectorRepositoryService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private projectorService: ProjectorService,
|
private projectorService: ProjectorService,
|
||||||
@ -68,7 +72,8 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
|||||||
private countdownRepo: CountdownRepositoryService,
|
private countdownRepo: CountdownRepositoryService,
|
||||||
private messageRepo: ProjectorMessageRepositoryService,
|
private messageRepo: ProjectorMessageRepositoryService,
|
||||||
private currentListOfSpeakersSlideService: CurrentListOfSpeakersSlideService,
|
private currentListOfSpeakersSlideService: CurrentListOfSpeakersSlideService,
|
||||||
private currentSpeakerChyronService: CurrentSpeakerChyronSlideService
|
private currentSpeakerChyronService: CurrentSpeakerChyronSlideService,
|
||||||
|
private durationService: DurationService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar);
|
super(titleService, translate, matSnackBar);
|
||||||
|
|
||||||
@ -170,4 +175,63 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
|||||||
public toggleChyron(): void {
|
public toggleChyron(): void {
|
||||||
this.currentSpeakerChyronService.toggleOn(this.projector);
|
this.currentSpeakerChyronService.toggleOn(this.projector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the countdown dialog
|
||||||
|
*
|
||||||
|
* @param viewCountdown optional existing countdown to edit
|
||||||
|
*/
|
||||||
|
public openCountdownDialog(viewCountdown?: ViewCountdown): void {
|
||||||
|
let countdownData: CountdownData = {
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
duration: '',
|
||||||
|
count: this.countdowns.length
|
||||||
|
};
|
||||||
|
|
||||||
|
if (viewCountdown) {
|
||||||
|
countdownData = {
|
||||||
|
title: viewCountdown.title,
|
||||||
|
description: viewCountdown.description,
|
||||||
|
duration: this.durationService.durationToString(viewCountdown.default_time, 'm')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogRef = this.dialog.open(CountdownDialogComponent, {
|
||||||
|
data: countdownData,
|
||||||
|
maxHeight: '90vh',
|
||||||
|
width: '400px',
|
||||||
|
maxWidth: '90vw',
|
||||||
|
disableClose: true
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if (result) {
|
||||||
|
this.submitCountdown(result, viewCountdown);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to send a countdown
|
||||||
|
*
|
||||||
|
* @param data the countdown data to send
|
||||||
|
* @param viewCountdown optional existing countdown to update
|
||||||
|
*/
|
||||||
|
public submitCountdown(data: CountdownData, viewCountdown?: ViewCountdown): void {
|
||||||
|
const defaultTime = this.durationService.stringToDuration(data.duration, 'm');
|
||||||
|
|
||||||
|
const sendData = new Countdown({
|
||||||
|
title: data.title,
|
||||||
|
description: data.description,
|
||||||
|
default_time: defaultTime,
|
||||||
|
countdown_time: viewCountdown && viewCountdown.running ? null : defaultTime
|
||||||
|
});
|
||||||
|
|
||||||
|
if (viewCountdown) {
|
||||||
|
this.countdownRepo.update(sendData, viewCountdown).then(() => {}, this.raiseError);
|
||||||
|
} else {
|
||||||
|
this.countdownRepo.create(sendData).then(() => {}, this.raiseError);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,14 @@
|
|||||||
|
|
||||||
<mat-card *ngIf="!projectorToCreate && projectors && projectors.length > 1">
|
<mat-card *ngIf="!projectorToCreate && projectors && projectors.length > 1">
|
||||||
<span translate>
|
<span translate>
|
||||||
Reference projector for current list of speakers:
|
Reference projector for current list of speakers: </span
|
||||||
</span>
|
>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select [disabled]="!!editId" [value]="projectors.length ? projectors[0].reference_projector_id : null" (selectionChange)="onSelectReferenceProjector($event)">
|
<mat-select
|
||||||
|
[disabled]="!!editId"
|
||||||
|
[value]="projectors.length ? projectors[0].reference_projector_id : null"
|
||||||
|
(selectionChange)="onSelectReferenceProjector($event)"
|
||||||
|
>
|
||||||
<mat-option *ngFor="let projector of projectors" [value]="projector.id">
|
<mat-option *ngFor="let projector of projectors" [value]="projector.id">
|
||||||
{{ projector.getTitle() | translate }}
|
{{ projector.getTitle() | translate }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
@ -33,7 +37,7 @@
|
|||||||
<form [formGroup]="createForm" (keydown)="keyDownFunction($event)">
|
<form [formGroup]="createForm" (keydown)="keyDownFunction($event)">
|
||||||
<p>
|
<p>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input formControlName="name" matInput placeholder="{{'Name' | translate}}" required>
|
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
||||||
<mat-hint *ngIf="!createForm.controls.name.valid">
|
<mat-hint *ngIf="!createForm.controls.name.valid">
|
||||||
<span translate>Required</span>
|
<span translate>Required</span>
|
||||||
</mat-hint>
|
</mat-hint>
|
||||||
@ -58,16 +62,16 @@
|
|||||||
{{ projector.name | translate }}
|
{{ projector.name | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container class="meta-text-block-action-row" *ngIf="canManage">
|
<ng-container class="meta-text-block-action-row" *ngIf="canManage">
|
||||||
<button mat-icon-button *ngIf="editId !== projector.id" (click)=onEditButton(projector)>
|
<button mat-icon-button *ngIf="editId !== projector.id" (click)="onEditButton(projector)">
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>edit</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button *ngIf="editId === projector.id" (click)=onCancelButton(projector)>
|
<button mat-icon-button *ngIf="editId === projector.id" (click)="onCancelButton(projector)">
|
||||||
<mat-icon>close</mat-icon>
|
<mat-icon>close</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button *ngIf="editId === projector.id" (click)=onSaveButton(projector)>
|
<button mat-icon-button *ngIf="editId === projector.id" (click)="onSaveButton(projector)">
|
||||||
<mat-icon>save</mat-icon>
|
<mat-icon>save</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button color="warn" (click)=onDeleteButton(projector)>
|
<button mat-icon-button color="warn" (click)="onDeleteButton(projector)">
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -81,7 +85,7 @@
|
|||||||
<form [formGroup]="updateForm" (keydown)="keyDownFunction($event, projector)">
|
<form [formGroup]="updateForm" (keydown)="keyDownFunction($event, projector)">
|
||||||
<!-- Name field -->
|
<!-- Name field -->
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input formControlName="name" matInput placeholder="{{'Name' | translate}}" required>
|
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
||||||
<mat-hint *ngIf="!updateForm.controls.name.valid">
|
<mat-hint *ngIf="!updateForm.controls.name.valid">
|
||||||
<span translate>Required</span>
|
<span translate>Required</span>
|
||||||
</mat-hint>
|
</mat-hint>
|
||||||
@ -94,7 +98,13 @@
|
|||||||
{{ ratio }}
|
{{ ratio }}
|
||||||
</mat-radio-button>
|
</mat-radio-button>
|
||||||
</mat-radio-group>
|
</mat-radio-group>
|
||||||
<mat-slider [thumbLabel]="true" formControlName="width" min="800" max="3840" step="10"></mat-slider>
|
<mat-slider
|
||||||
|
[thumbLabel]="true"
|
||||||
|
formControlName="width"
|
||||||
|
min="800"
|
||||||
|
max="3840"
|
||||||
|
step="10"
|
||||||
|
></mat-slider>
|
||||||
{{ updateForm.value.width }}
|
{{ updateForm.value.width }}
|
||||||
|
|
||||||
<!-- colors -->
|
<!-- colors -->
|
||||||
@ -160,8 +170,4 @@
|
|||||||
<mat-icon>note</mat-icon>
|
<mat-icon>note</mat-icon>
|
||||||
<span translate>Projector messages</span>
|
<span translate>Projector messages</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" mat-menu-item routerLink="/projectors/countdowns">
|
|
||||||
<mat-icon>alarm</mat-icon>
|
|
||||||
<span translate>Countdowns</span>
|
|
||||||
</button>
|
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
@ -2,7 +2,6 @@ import { NgModule } from '@angular/core';
|
|||||||
import { Routes, RouterModule } from '@angular/router';
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
||||||
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
||||||
import { CountdownListComponent } from './components/countdown-list/countdown-list.component';
|
|
||||||
import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component';
|
import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
@ -15,10 +14,6 @@ const routes: Routes = [
|
|||||||
path: 'detail/:id',
|
path: 'detail/:id',
|
||||||
component: ProjectorDetailComponent
|
component: ProjectorDetailComponent
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'countdowns',
|
|
||||||
component: CountdownListComponent
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'messages',
|
path: 'messages',
|
||||||
component: ProjectorMessageListComponent
|
component: ProjectorMessageListComponent
|
||||||
|
@ -5,18 +5,19 @@ import { ProjectorRoutingModule } from './projector-routing.module';
|
|||||||
import { SharedModule } from '../../shared/shared.module';
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
import { ProjectorListComponent } from './components/projector-list/projector-list.component';
|
||||||
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
import { ProjectorDetailComponent } from './components/projector-detail/projector-detail.component';
|
||||||
import { CountdownListComponent } from './components/countdown-list/countdown-list.component';
|
|
||||||
import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component';
|
import { ProjectorMessageListComponent } from './components/projector-message-list/projector-message-list.component';
|
||||||
import { CountdownControlsComponent } from './components/countdown-controls/countdown-controls.component';
|
import { CountdownControlsComponent } from './components/countdown-controls/countdown-controls.component';
|
||||||
|
import { CountdownDialogComponent } from './components/countdown-dialog/countdown-dialog.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, ProjectorRoutingModule, SharedModule],
|
imports: [CommonModule, ProjectorRoutingModule, SharedModule],
|
||||||
declarations: [
|
declarations: [
|
||||||
ProjectorListComponent,
|
ProjectorListComponent,
|
||||||
ProjectorDetailComponent,
|
ProjectorDetailComponent,
|
||||||
CountdownListComponent,
|
|
||||||
ProjectorMessageListComponent,
|
ProjectorMessageListComponent,
|
||||||
CountdownControlsComponent
|
CountdownControlsComponent,
|
||||||
]
|
CountdownDialogComponent
|
||||||
|
],
|
||||||
|
entryComponents: [CountdownDialogComponent]
|
||||||
})
|
})
|
||||||
export class ProjectorModule {}
|
export class ProjectorModule {}
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
$foreground: map-get($theme, foreground);
|
$foreground: map-get($theme, foreground);
|
||||||
$background: map-get($theme, background);
|
$background: map-get($theme, background);
|
||||||
|
|
||||||
h1, h3.accent {
|
h1,
|
||||||
|
h3.accent {
|
||||||
color: mat-color($primary);
|
color: mat-color($primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,7 +20,8 @@
|
|||||||
color: mat-color($primary);
|
color: mat-color($primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.accent, .accent-text {
|
.accent,
|
||||||
|
.accent-text {
|
||||||
color: mat-color($accent);
|
color: mat-color($accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,10 +29,6 @@
|
|||||||
color: mat-color($primary);
|
color: mat-color($primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.projected .mat-icon {
|
|
||||||
color: mat-color($primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
//custom table header for search button, filtering and more. Used in ListViews
|
//custom table header for search button, filtering and more. Used in ListViews
|
||||||
.custom-table-header {
|
.custom-table-header {
|
||||||
background: mat-color($background, background);
|
background: mat-color($background, background);
|
||||||
@ -42,7 +40,9 @@
|
|||||||
font-size: 75%;
|
font-size: 75%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.error, .warn {
|
|
||||||
|
.error,
|
||||||
|
.warn {
|
||||||
color: mat-color($warn);
|
color: mat-color($warn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,8 +35,4 @@ $openslides-primary: mat-palette($openslides-green);
|
|||||||
$openslides-accent: mat-palette($mat-amber);
|
$openslides-accent: mat-palette($mat-amber);
|
||||||
$openslides-warn: mat-palette($mat-red);
|
$openslides-warn: mat-palette($mat-red);
|
||||||
|
|
||||||
$openslides-green-theme: mat-light-theme(
|
$openslides-green-theme: mat-light-theme($openslides-primary, $openslides-accent, $openslides-warn);
|
||||||
$openslides-primary,
|
|
||||||
$openslides-accent,
|
|
||||||
$openslides-warn
|
|
||||||
)
|
|
||||||
|
@ -651,7 +651,6 @@ button.mat-menu-item.selected {
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.queue {
|
.queue {
|
||||||
.mat-expansion-panel-body {
|
.mat-expansion-panel-body {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
Loading…
Reference in New Issue
Block a user