ui enhancements and song state
This commit is contained in:
@@ -2,7 +2,7 @@ import {Injectable} from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {Song} from './song';
|
||||
import {SongDataService} from './song-data.service';
|
||||
import {tap} from 'rxjs/operators';
|
||||
import {first, tap} from 'rxjs/operators';
|
||||
|
||||
declare var importCCLI: any;
|
||||
|
||||
@@ -12,6 +12,7 @@ declare var importCCLI: any;
|
||||
export class SongService {
|
||||
|
||||
public static TYPES = ['Praise', 'Worship'];
|
||||
public static STATUS = ['draft', 'set', 'final'];
|
||||
|
||||
public static LEGAL_OWNER = ['CCLI', 'other'];
|
||||
public static LEGAL_TYPE = ['open', 'allowed'];
|
||||
@@ -24,7 +25,8 @@ export class SongService {
|
||||
}
|
||||
|
||||
public list$ = (): Observable<Song[]> => this.songDataService.list$().pipe(tap(_ => this.list = _));
|
||||
public read = (songId: string): Observable<Song | undefined> => this.songDataService.read$(songId);
|
||||
public read$ = (songId: string): Observable<Song | undefined> => this.songDataService.read$(songId);
|
||||
public read = (songId: string): Promise<Song | undefined> => this.read$(songId).pipe(first()).toPromise();
|
||||
|
||||
public async update$(songId: string, data: Partial<Song>): Promise<void> {
|
||||
await this.songDataService.update$(songId, data);
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface Song {
|
||||
title: string;
|
||||
type: string;
|
||||
flags: string;
|
||||
status: string;
|
||||
|
||||
legalType: string;
|
||||
legalLink: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<app-card *ngIf="song" [heading]="song.number + ' bearbeiten'">
|
||||
<app-card *ngIf="song" [heading]="song.number + ' bearbeiten'" closeLink="../">
|
||||
|
||||
<form [formGroup]="form" class="form">
|
||||
<mat-form-field appearance="outline">
|
||||
@@ -6,7 +6,7 @@
|
||||
<input formControlName="title" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="third">
|
||||
<div class="fourth">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Typ</mat-label>
|
||||
<mat-select formControlName="type">
|
||||
@@ -23,6 +23,12 @@
|
||||
<mat-label>Tempo</mat-label>
|
||||
<input formControlName="tempo" matInput>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Status</mat-label>
|
||||
<mat-select formControlName="status">
|
||||
<mat-option *ngFor="let status of status" [value]="status">{{status | status}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
@@ -50,60 +56,61 @@
|
||||
</mat-chip-list>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechtlicher Status</mat-label>
|
||||
<mat-select formControlName="legalType">
|
||||
<mat-option *ngFor="let key of legalType" [value]="key">{{key|legalType}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div class="half">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechtlicher Status</mat-label>
|
||||
<mat-select formControlName="legalType">
|
||||
<mat-option *ngFor="let key of legalType" [value]="key">{{key|legalType}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechteinhaber</mat-label>
|
||||
<mat-select formControlName="legalOwner">
|
||||
<mat-option *ngFor="let key of legalOwner" [value]="key">{{key|legalOwner}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechteinhaber</mat-label>
|
||||
<mat-select formControlName="legalOwner">
|
||||
<mat-option *ngFor="let key of legalOwner" [value]="key">{{key|legalOwner}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechteinhaber Link</mat-label>
|
||||
<input formControlName="legalLink" matInput>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechteinhaber Link</mat-label>
|
||||
<input formControlName="legalLink" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechteinhaber ID (z.B. CCLI Liednummer)</mat-label>
|
||||
<input formControlName="legalOwnerId" matInput>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechteinhaber ID (z.B. CCLI Liednummer)</mat-label>
|
||||
<input formControlName="legalOwnerId" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Lizenznummer</mat-label>
|
||||
<input formControlName="legalLicenseId" matInput>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Lizenznummer</mat-label>
|
||||
<input formControlName="legalLicenseId" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Künstler</mat-label>
|
||||
<input formControlName="artist" matInput>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Künstler</mat-label>
|
||||
<input formControlName="artist" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Verlag</mat-label>
|
||||
<input formControlName="label" matInput>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Verlag</mat-label>
|
||||
<input formControlName="label" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Nutzungsbedingungen</mat-label>
|
||||
<input formControlName="termsOfUse" matInput>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Nutzungsbedingungen</mat-label>
|
||||
<input formControlName="termsOfUse" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>abweichende Quelle</mat-label>
|
||||
<input formControlName="origin" matInput>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>abweichende Quelle</mat-label>
|
||||
<input formControlName="origin" matInput>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<app-button-row>
|
||||
<button (click)="onSave()" color="primary" mat-flat-button>Speichern</button>
|
||||
<button mat-stroked-button routerLink="../">Abbrechen</button>
|
||||
<button (click)="onSave()" mat-button>Speichern</button>
|
||||
</app-button-row>
|
||||
</app-card>
|
||||
|
||||
|
||||
@@ -6,9 +6,18 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.third {
|
||||
.fourth {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
@media screen and (max-width: 860px) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
column-gap: 20px;
|
||||
}
|
||||
|
||||
.half {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 20px;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ export class EditSongComponent implements OnInit {
|
||||
public form: FormGroup;
|
||||
public keys = KEYS;
|
||||
public types = SongService.TYPES;
|
||||
public status = SongService.STATUS;
|
||||
public legalOwner = SongService.LEGAL_OWNER;
|
||||
public legalType = SongService.LEGAL_TYPE;
|
||||
public flags: string[] = [];
|
||||
@@ -37,7 +38,7 @@ export class EditSongComponent implements OnInit {
|
||||
public ngOnInit(): void {
|
||||
this.activatedRoute.params.pipe(
|
||||
map(param => param.songId),
|
||||
switchMap(songId => this.songService.read(songId)),
|
||||
switchMap(songId => this.songService.read$(songId)),
|
||||
first()
|
||||
).subscribe(song => {
|
||||
this.song = song;
|
||||
@@ -55,7 +56,7 @@ export class EditSongComponent implements OnInit {
|
||||
|
||||
public removeFlag(flag: string): void {
|
||||
const flags = this.flags.filter(_ => _ !== flag);
|
||||
this.form.controls.flags.setValue(flags.reduce((a, b) => `${a};${b}`, ''));
|
||||
this.form.controls.flags.setValue(flags.join(';'));
|
||||
}
|
||||
|
||||
public addFlag(event: MatChipInputEvent): void {
|
||||
@@ -65,14 +66,13 @@ export class EditSongComponent implements OnInit {
|
||||
// Add our fruit
|
||||
if ((value || '').trim()) {
|
||||
const flags = [...this.flags, value.trim()];
|
||||
this.form.controls.flags.setValue(flags.reduce((a, b) => `${a};${b}`, ''));
|
||||
this.form.controls.flags.setValue(flags.join(';'));
|
||||
}
|
||||
|
||||
if (input) input.value = '';
|
||||
}
|
||||
|
||||
private onFlagsChanged(flagArray: string): void {
|
||||
console.log(flagArray);
|
||||
if (!flagArray) {
|
||||
this.flags = [];
|
||||
return;
|
||||
|
||||
@@ -19,6 +19,7 @@ import {LegalTypeTranslatorModule} from '../../../../widget-modules/pipes/legal-
|
||||
import {KeyTranslatorModule} from '../../../../widget-modules/pipes/key-translator/key-translator.module';
|
||||
import {MatChipsModule} from '@angular/material/chips';
|
||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||
import {StatusTranslaterModule} from '../../../../widget-modules/pipes/status-translater/status-translater.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -43,6 +44,7 @@ import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||
KeyTranslatorModule,
|
||||
MatChipsModule,
|
||||
FontAwesomeModule,
|
||||
StatusTranslaterModule,
|
||||
|
||||
]
|
||||
})
|
||||
|
||||
@@ -19,6 +19,7 @@ export class EditService {
|
||||
key: new FormControl(song.key),
|
||||
tempo: new FormControl(song.tempo),
|
||||
type: new FormControl(song.type),
|
||||
status: new FormControl(song.status ?? 'draft'),
|
||||
|
||||
legalType: new FormControl(song.legalType),
|
||||
legalLink: new FormControl(song.legalLink),
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
<div class="split">
|
||||
<app-card *ngIf="song$ | async as song" [heading]="song.number + ' - ' + song.title">
|
||||
<app-card *ngIf="song$ | async as song" [heading]="song.number + ' - ' + song.title" closeLink="../">
|
||||
<div class="song">
|
||||
<div>
|
||||
<div class="detail">
|
||||
<div *appRole="['leader', 'contributor']" class="detail">
|
||||
<div>Typ: {{song.type | songType}}</div>
|
||||
<div>Tonart: {{song.key}}</div>
|
||||
<div>Tempo: {{song.tempo}}</div>
|
||||
<div>Status: {{(song.status|status) || 'entwurf'}}</div>
|
||||
<div *ngIf="song.legalOwner">Rechteinhaber: {{song.legalOwner|legalOwner}}</div>
|
||||
<div *ngIf="song.legalOwnerId">Rechteinhaber ID: {{song.legalOwnerId}}</div>
|
||||
<div *ngIf="song.legalLicenseId">Lizenznummer: {{song.legalLicenseId}}</div>
|
||||
<div *ngIf="song.artist">Künstler: {{song.artist}}</div>
|
||||
<div *ngIf="song.label">Verlag: {{song.label}}</div>
|
||||
<div *ngIf="song.origin">Quelle: {{song.origin}}</div>
|
||||
<div *ngIf="song.origin">Quelle: {{song.origin}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,24 +21,20 @@
|
||||
<app-song-text *ngIf="user$|async as user" [chordMode]="user.chordMode" [showSwitch]="true"
|
||||
[text]="song.text"></app-song-text>
|
||||
|
||||
<mat-chip-list aria-label="Attribute">
|
||||
<mat-chip-list *appRole="['leader', 'contributor']" aria-label="Attribute">
|
||||
<mat-chip *ngFor="let flag of getFlags(song.flags)">{{flag}}</mat-chip>
|
||||
</mat-chip-list>
|
||||
|
||||
<div class="text">{{song.comment}}</div>
|
||||
<div>
|
||||
<h3>Anhänge</h3>
|
||||
<div *ngFor="let file of (files$|async)">{{file.name}}</div>
|
||||
</div>
|
||||
<div *appRole="['leader', 'contributor']" class="text">{{song.comment}}</div>
|
||||
|
||||
</div>
|
||||
|
||||
<app-button-row>
|
||||
<button color="primary" mat-flat-button routerLink="edit">Bearbeiten</button>
|
||||
<button mat-button routerLink="../">Schließen</button>
|
||||
<button *appRole="['contributor']" mat-button routerLink="edit">Bearbeiten</button>
|
||||
</app-button-row>
|
||||
|
||||
</app-card>
|
||||
<app-card>
|
||||
|
||||
<app-card *ngIf="!!(files$|async)" heading="Anhänge">
|
||||
<div *ngFor="let file of (files$|async)">{{file.name}}</div>
|
||||
</app-card>
|
||||
</div>
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
.text {
|
||||
white-space: pre-wrap;
|
||||
font-family: 'Ubuntu Mono', monospace;
|
||||
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.detail {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export class SongComponent implements OnInit {
|
||||
public ngOnInit(): void {
|
||||
this.song$ = this.activatedRoute.params.pipe(
|
||||
map(param => param.songId),
|
||||
switchMap(songId => this.songService.read(songId))
|
||||
switchMap(songId => this.songService.read$(songId))
|
||||
);
|
||||
|
||||
this.files$ = this.activatedRoute.params.pipe(
|
||||
|
||||
@@ -9,6 +9,8 @@ import {RouterModule} from '@angular/router';
|
||||
import {LegalOwnerTranslatorModule} from '../../../widget-modules/pipes/legal-owner-translator/legal-owner-translator.module';
|
||||
import {SongTextModule} from '../../../widget-modules/components/song-text/song-text.module';
|
||||
import {MatChipsModule} from '@angular/material/chips';
|
||||
import {RoleModule} from '../../../services/user/role.module';
|
||||
import {StatusTranslaterModule} from '../../../widget-modules/pipes/status-translater/status-translater.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -25,6 +27,8 @@ import {MatChipsModule} from '@angular/material/chips';
|
||||
LegalOwnerTranslatorModule,
|
||||
SongTextModule,
|
||||
MatChipsModule,
|
||||
RoleModule,
|
||||
StatusTranslaterModule,
|
||||
]
|
||||
})
|
||||
export class SongModule {
|
||||
|
||||
Reference in New Issue
Block a user