migrate angular 21
This commit is contained in:
@@ -52,9 +52,7 @@ describe('TransposeService', () => {
|
||||
const line: Line = {
|
||||
type: LineType.chord,
|
||||
text: '',
|
||||
chords: [
|
||||
{chord: 'Q', add: 'sus4', slashChord: null, position: 0, length: 1},
|
||||
],
|
||||
chords: [{chord: 'Q', add: 'sus4', slashChord: null, position: 0, length: 1}],
|
||||
};
|
||||
|
||||
const rendered = service.renderChords(line);
|
||||
@@ -66,9 +64,7 @@ describe('TransposeService', () => {
|
||||
const line: Line = {
|
||||
type: LineType.chord,
|
||||
text: '',
|
||||
chords: [
|
||||
{chord: 'C', add: null, slashChord: 'Q', position: 0, length: 1},
|
||||
],
|
||||
chords: [{chord: 'C', add: null, slashChord: 'Q', position: 0, length: 1}],
|
||||
};
|
||||
|
||||
const rendered = service.renderChords(line);
|
||||
@@ -80,9 +76,7 @@ describe('TransposeService', () => {
|
||||
const line: Line = {
|
||||
type: LineType.chord,
|
||||
text: '',
|
||||
chords: [
|
||||
{chord: 'C', add: null, slashChord: null, position: 120, length: 1},
|
||||
],
|
||||
chords: [{chord: 'C', add: null, slashChord: null, position: 120, length: 1}],
|
||||
};
|
||||
|
||||
const rendered = service.renderChords(line);
|
||||
|
||||
@@ -111,7 +111,7 @@ export class TransposeService {
|
||||
|
||||
private transposeChord(chord: Chord, map: TransposeMap): Chord {
|
||||
const translatedChord = map[chord.chord] ?? 'X';
|
||||
const translatedSlashChord = chord.slashChord ? map[chord.slashChord] ?? 'X' : null;
|
||||
const translatedSlashChord = chord.slashChord ? (map[chord.slashChord] ?? 'X') : null;
|
||||
|
||||
return {
|
||||
...chord,
|
||||
@@ -147,7 +147,7 @@ export class TransposeService {
|
||||
|
||||
private renderChord(chord: Chord): string {
|
||||
const renderedChord = scaleMapping[chord.chord] ?? 'X';
|
||||
const renderedSlashChord = chord.slashChord ? scaleMapping[chord.slashChord] ?? 'X' : '';
|
||||
const renderedSlashChord = chord.slashChord ? (scaleMapping[chord.slashChord] ?? 'X') : '';
|
||||
|
||||
return renderedChord + (chord.add ?? '') + (renderedSlashChord ? '/' + renderedSlashChord : '');
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
<mat-label>Typ</mat-label>
|
||||
<mat-select formControlName="type">
|
||||
<mat-option [value]="null">- kein Filter -</mat-option>
|
||||
<mat-option *ngFor="let type of types" [value]="type">{{
|
||||
@for (type of types; track type) {
|
||||
<mat-option [value]="type">{{
|
||||
type | songType
|
||||
}}
|
||||
</mat-option>
|
||||
}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -20,10 +22,12 @@
|
||||
<mat-label>Tonart</mat-label>
|
||||
<mat-select formControlName="key">
|
||||
<mat-option [value]="null">- kein Filter -</mat-option>
|
||||
<mat-option *ngFor="let key of keys" [value]="key">{{
|
||||
@for (key of keys; track key) {
|
||||
<mat-option [value]="key">{{
|
||||
key | key
|
||||
}}
|
||||
</mat-option>
|
||||
}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -31,10 +35,12 @@
|
||||
<mat-label>Rechtlicher Status</mat-label>
|
||||
<mat-select formControlName="legalType">
|
||||
<mat-option [value]="null">- kein Filter -</mat-option>
|
||||
<mat-option *ngFor="let key of legalType" [value]="key">{{
|
||||
@for (key of legalType; track key) {
|
||||
<mat-option [value]="key">{{
|
||||
key | legalType
|
||||
}}
|
||||
</mat-option>
|
||||
}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -42,10 +48,12 @@
|
||||
<mat-label>Attribute</mat-label>
|
||||
<mat-select formControlName="flag">
|
||||
<mat-option [value]="null">- kein Filter -</mat-option>
|
||||
<mat-option *ngFor="let flag of getFlags()" [value]="flag">{{
|
||||
@for (flag of getFlags(); track flag) {
|
||||
<mat-option [value]="flag">{{
|
||||
flag
|
||||
}}
|
||||
</mat-option>
|
||||
}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {MatFormField, MatLabel} from '@angular/material/form-field';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
import {MatSelect} from '@angular/material/select';
|
||||
import {MatOption} from '@angular/material/core';
|
||||
import {NgFor} from '@angular/common';
|
||||
|
||||
import {LegalTypePipe} from '../../../../widget-modules/pipes/legal-type-translator/legal-type.pipe';
|
||||
import {KeyPipe} from '../../../../widget-modules/pipes/key-translator/key.pipe';
|
||||
import {SongTypePipe} from '../../../../widget-modules/pipes/song-type-translater/song-type.pipe';
|
||||
@@ -18,7 +18,7 @@ import {SongTypePipe} from '../../../../widget-modules/pipes/song-type-translate
|
||||
selector: 'app-filter',
|
||||
templateUrl: './filter.component.html',
|
||||
styleUrls: ['./filter.component.less'],
|
||||
imports: [ReactiveFormsModule, MatFormField, MatLabel, MatInput, MatSelect, MatOption, NgFor, LegalTypePipe, KeyPipe, SongTypePipe],
|
||||
imports: [ReactiveFormsModule, MatFormField, MatLabel, MatInput, MatSelect, MatOption, LegalTypePipe, KeyPipe, SongTypePipe],
|
||||
})
|
||||
export class FilterComponent {
|
||||
public filterFormGroup: UntypedFormGroup;
|
||||
|
||||
@@ -1,30 +1,40 @@
|
||||
<div *ngIf="songs$ | async as songs">
|
||||
<app-list-header [anyFilterActive]="anyFilterActive">
|
||||
<app-filter [songs]="songs" route="songs"></app-filter>
|
||||
</app-list-header>
|
||||
|
||||
<app-card [padding]="false">
|
||||
<div *ngFor="let song of songs; trackBy: trackBy" [routerLink]="song.id" class="list-item">
|
||||
<div class="number">{{ song.number }}</div>
|
||||
<div>{{ song.title }}</div>
|
||||
<div>
|
||||
<ng-container *appRole="['contributor']">
|
||||
<div *ngIf="song.status === 'draft'" class="warning">
|
||||
<fa-icon [icon]="faDraft"></fa-icon>
|
||||
@if (songs$ | async; as songs) {
|
||||
<div>
|
||||
<app-list-header [anyFilterActive]="anyFilterActive">
|
||||
<app-filter [songs]="songs" route="songs"></app-filter>
|
||||
</app-list-header>
|
||||
<app-card [padding]="false">
|
||||
@for (song of songs; track trackBy($index, song)) {
|
||||
<div [routerLink]="song.id" class="list-item">
|
||||
<div class="number">{{ song.number }}</div>
|
||||
<div>{{ song.title }}</div>
|
||||
<div>
|
||||
<ng-container *appRole="['contributor']">
|
||||
@if (song.status === 'draft') {
|
||||
<div class="warning">
|
||||
<fa-icon [icon]="faDraft"></fa-icon>
|
||||
</div>
|
||||
}
|
||||
@if (song.status === 'set') {
|
||||
<div class="neutral">
|
||||
<fa-icon [icon]="faDraft"></fa-icon>
|
||||
</div>
|
||||
}
|
||||
@if (song.status === 'final') {
|
||||
<div class="success">
|
||||
<fa-icon [icon]="faFinal"></fa-icon>
|
||||
</div>
|
||||
}
|
||||
</ng-container>
|
||||
@if (song.legalType === 'open') {
|
||||
<div class="warning">
|
||||
<fa-icon [icon]="faLegal"></fa-icon>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div *ngIf="song.status === 'set'" class="neutral">
|
||||
<fa-icon [icon]="faDraft"></fa-icon>
|
||||
</div>
|
||||
<div *ngIf="song.status === 'final'" class="success">
|
||||
<fa-icon [icon]="faFinal"></fa-icon>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div *ngIf="song.legalType === 'open'" class="warning">
|
||||
<fa-icon [icon]="faLegal"></fa-icon>
|
||||
<div>{{ song.key }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>{{ song.key }}</div>
|
||||
</div>
|
||||
|
||||
</app-card>
|
||||
</div>
|
||||
}
|
||||
</app-card>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {filterSong} from '../../../services/filter.helper';
|
||||
import {FilterValues} from './filter/filter-values';
|
||||
import {ScrollService} from '../../../services/scroll.service';
|
||||
import {faBalanceScaleRight, faCheck, faPencilRuler} from '@fortawesome/free-solid-svg-icons';
|
||||
import {AsyncPipe, NgFor, NgIf} from '@angular/common';
|
||||
import {AsyncPipe} from '@angular/common';
|
||||
import {ListHeaderComponent} from '../../../widget-modules/components/list-header/list-header.component';
|
||||
import {FilterComponent} from './filter/filter.component';
|
||||
import {CardComponent} from '../../../widget-modules/components/card/card.component';
|
||||
@@ -22,7 +22,7 @@ import {FaIconComponent} from '@fortawesome/angular-fontawesome';
|
||||
styleUrls: ['./song-list.component.less'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [fade],
|
||||
imports: [NgIf, ListHeaderComponent, FilterComponent, CardComponent, NgFor, RouterLink, RoleDirective, FaIconComponent, AsyncPipe],
|
||||
imports: [ListHeaderComponent, FilterComponent, CardComponent, RouterLink, RoleDirective, FaIconComponent, AsyncPipe],
|
||||
})
|
||||
export class SongListComponent implements OnInit, OnDestroy {
|
||||
public anyFilterActive = false;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<app-card heading="Angehängte Dateien">
|
||||
<div *ngIf="currentUpload">
|
||||
<div class="progress">
|
||||
<div
|
||||
[ngStyle]="{ width: currentUpload?.progress + '%' }"
|
||||
class="progress-bar progress-bar-animated"
|
||||
></div>
|
||||
@if (currentUpload) {
|
||||
<div>
|
||||
<div class="progress">
|
||||
<div
|
||||
[ngStyle]="{ width: currentUpload?.progress + '%' }"
|
||||
class="progress-bar progress-bar-animated"
|
||||
></div>
|
||||
</div>
|
||||
Progress: {{ currentUpload?.name }} | {{ currentUpload?.progress }}%
|
||||
Complete
|
||||
</div>
|
||||
Progress: {{ currentUpload?.name }} | {{ currentUpload?.progress }}%
|
||||
Complete
|
||||
</div>
|
||||
}
|
||||
<div class="upload">
|
||||
<label>
|
||||
<input (change)="detectFiles($event)" type="file" />
|
||||
@@ -18,12 +20,14 @@
|
||||
(click)="uploadSingle()"
|
||||
[disabled]="!selectedFiles"
|
||||
mat-icon-button
|
||||
>
|
||||
>
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p *ngFor="let file of files$ | async">
|
||||
<app-file [file]="file" [songId]="songId"></app-file>
|
||||
</p>
|
||||
@for (file of files$ | async; track file) {
|
||||
<p>
|
||||
<app-file [file]="file" [songId]="songId"></app-file>
|
||||
</p>
|
||||
}
|
||||
</app-card>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {FileDataService} from '../../../services/file-data.service';
|
||||
import {Observable} from 'rxjs';
|
||||
import {File} from '../../../services/file';
|
||||
import {CardComponent} from '../../../../../widget-modules/components/card/card.component';
|
||||
import {AsyncPipe, NgFor, NgIf, NgStyle} from '@angular/common';
|
||||
import {AsyncPipe, NgStyle} from '@angular/common';
|
||||
import {MatIconButton} from '@angular/material/button';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {FileComponent} from './file/file.component';
|
||||
@@ -16,7 +16,7 @@ import {FileComponent} from './file/file.component';
|
||||
selector: 'app-edit-file',
|
||||
templateUrl: './edit-file.component.html',
|
||||
styleUrls: ['./edit-file.component.less'],
|
||||
imports: [CardComponent, NgIf, NgStyle, MatIconButton, MatIcon, NgFor, FileComponent, AsyncPipe],
|
||||
imports: [CardComponent, NgStyle, MatIconButton, MatIcon, FileComponent, AsyncPipe],
|
||||
})
|
||||
export class EditFileComponent {
|
||||
public selectedFiles: FileList | null = null;
|
||||
|
||||
@@ -1,184 +1,186 @@
|
||||
<app-card *ngIf="song" [heading]="song.number + ' bearbeiten'" closeLink="../">
|
||||
<form [formGroup]="form" class="form">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Titel</mat-label>
|
||||
<input formControlName="title" matInput />
|
||||
</mat-form-field>
|
||||
|
||||
<div class="fourth">
|
||||
@if (song) {
|
||||
<app-card [heading]="song.number + ' bearbeiten'" closeLink="../">
|
||||
<form [formGroup]="form" class="form">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Typ</mat-label>
|
||||
<mat-select formControlName="type">
|
||||
<mat-option *ngFor="let type of types" [value]="type">{{
|
||||
type | songType
|
||||
}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-label>Titel</mat-label>
|
||||
<input formControlName="title" matInput />
|
||||
</mat-form-field>
|
||||
<div class="fourth">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Typ</mat-label>
|
||||
<mat-select formControlName="type">
|
||||
@for (type of types; track type) {
|
||||
<mat-option [value]="type">{{
|
||||
type | songType
|
||||
}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Tonart</mat-label>
|
||||
<mat-select formControlName="key">
|
||||
@for (key of keys; track key) {
|
||||
<mat-option [value]="key">{{
|
||||
key | key
|
||||
}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<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">
|
||||
@for (status of status; track status) {
|
||||
<mat-option [value]="status">{{
|
||||
status | status
|
||||
}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Songtext</mat-label>
|
||||
<textarea
|
||||
(focus)="songtextFocus = true"
|
||||
(focusout)="songtextFocus = false"
|
||||
[cdkTextareaAutosize]="true"
|
||||
formControlName="text"
|
||||
matInput
|
||||
></textarea>
|
||||
</mat-form-field>
|
||||
@if (songtextFocus) {
|
||||
<div class="song-text-help">
|
||||
<h3>Vorschau</h3>
|
||||
<app-song-text [text]="form.value.text" chordMode="show"></app-song-text>
|
||||
<h3>Hinweise zur Bearbeitung</h3>
|
||||
<h4>Aufbau</h4>
|
||||
Der Liedtext wird hintereinander weg geschrieben. Dabei werden Strophen,
|
||||
Refrain und Bridge jeweils durch eine zusätzliche Zeile Markiert. z.B.
|
||||
<pre>
|
||||
Strophe
|
||||
Text der ersten Strophe
|
||||
Strophe
|
||||
Text der zweiten Strophe
|
||||
Refrain
|
||||
Und hier der Refrain
|
||||
</pre>
|
||||
<h3>Akkorde</h3>
|
||||
Die Akktorde werden jeweils in der Zeile über dem jeweiligen Liedtext
|
||||
geschrieben. Sie werden jeweils durch Leerzeichen an die entsprechende
|
||||
Position gebracht. Bitte keine Tabulatoren verwenden! Folgende
|
||||
Schreibweisen sind erlaubt:
|
||||
<pre>
|
||||
Dur: C D E
|
||||
Moll: c d e
|
||||
Kreuz/B-Tonarten: C# f# Eb (Hb muss als B angegeben werden)
|
||||
Basstöne: C/E D/C
|
||||
Obertöne: c7 E9 f#maj7
|
||||
</pre>
|
||||
Beispiel:
|
||||
<pre>
|
||||
Strophe
|
||||
e C/E H7 a D C/E H7/E
|
||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr,
|
||||
F# B Eb Cmaj7 C9 e
|
||||
sed diam nonumy eirmod tempor invidunt ut labore et dolore
|
||||
</pre>
|
||||
</div>
|
||||
}
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Kommentar</mat-label>
|
||||
<textarea
|
||||
[cdkTextareaAutosize]="true"
|
||||
formControlName="comment"
|
||||
matInput
|
||||
></textarea>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Tonart</mat-label>
|
||||
<mat-select formControlName="key">
|
||||
<mat-option *ngFor="let key of keys" [value]="key">{{
|
||||
key | key
|
||||
}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-chip-grid #chipList>
|
||||
@for (flag of flags; track flag) {
|
||||
<mat-chip-row
|
||||
(removed)="removeFlag(flag)"
|
||||
[removable]="true"
|
||||
>
|
||||
{{ flag }}
|
||||
<fa-icon (click)="removeFlag(flag)" [icon]="faRemove"></fa-icon>
|
||||
</mat-chip-row>
|
||||
}
|
||||
<input
|
||||
(matChipInputTokenEnd)="addFlag($event)"
|
||||
[matChipInputAddOnBlur]="true"
|
||||
[matChipInputFor]="chipList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
placeholder="Attribute"
|
||||
/>
|
||||
</mat-chip-grid>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<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">
|
||||
<mat-label>Songtext</mat-label>
|
||||
<textarea
|
||||
(focus)="songtextFocus = true"
|
||||
(focusout)="songtextFocus = false"
|
||||
[cdkTextareaAutosize]="true"
|
||||
formControlName="text"
|
||||
matInput
|
||||
></textarea>
|
||||
</mat-form-field>
|
||||
<div *ngIf="songtextFocus" class="song-text-help">
|
||||
<h3>Vorschau</h3>
|
||||
<app-song-text [text]="form.value.text" chordMode="show"></app-song-text>
|
||||
<h3>Hinweise zur Bearbeitung</h3>
|
||||
<h4>Aufbau</h4>
|
||||
Der Liedtext wird hintereinander weg geschrieben. Dabei werden Strophen,
|
||||
Refrain und Bridge jeweils durch eine zusätzliche Zeile Markiert. z.B.
|
||||
<pre>
|
||||
Strophe
|
||||
Text der ersten Strophe
|
||||
|
||||
Strophe
|
||||
Text der zweiten Strophe
|
||||
|
||||
Refrain
|
||||
Und hier der Refrain
|
||||
</pre>
|
||||
<h3>Akkorde</h3>
|
||||
Die Akktorde werden jeweils in der Zeile über dem jeweiligen Liedtext
|
||||
geschrieben. Sie werden jeweils durch Leerzeichen an die entsprechende
|
||||
Position gebracht. Bitte keine Tabulatoren verwenden! Folgende
|
||||
Schreibweisen sind erlaubt:
|
||||
<pre>
|
||||
Dur: C D E
|
||||
Moll: c d e
|
||||
Kreuz/B-Tonarten: C# f# Eb (Hb muss als B angegeben werden)
|
||||
Basstöne: C/E D/C
|
||||
Obertöne: c7 E9 f#maj7
|
||||
</pre>
|
||||
Beispiel:
|
||||
<pre>
|
||||
Strophe
|
||||
e C/E H7 a D C/E H7/E
|
||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr,
|
||||
F# B Eb Cmaj7 C9 e
|
||||
sed diam nonumy eirmod tempor invidunt ut labore et dolore
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Kommentar</mat-label>
|
||||
<textarea
|
||||
[cdkTextareaAutosize]="true"
|
||||
formControlName="comment"
|
||||
matInput
|
||||
></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-chip-grid #chipList>
|
||||
<mat-chip-row
|
||||
(removed)="removeFlag(flag)"
|
||||
*ngFor="let flag of flags"
|
||||
[removable]="true"
|
||||
>
|
||||
{{ flag }}
|
||||
<fa-icon (click)="removeFlag(flag)" [icon]="faRemove"></fa-icon>
|
||||
</mat-chip-row>
|
||||
<input
|
||||
(matChipInputTokenEnd)="addFlag($event)"
|
||||
[matChipInputAddOnBlur]="true"
|
||||
[matChipInputFor]="chipList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
placeholder="Attribute"
|
||||
/>
|
||||
</mat-chip-grid>
|
||||
</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 ID (z.B. CCLI Liednummer)</mat-label>
|
||||
<input formControlName="legalOwnerId" matInput />
|
||||
<a
|
||||
*ngIf="form.value.legalOwner === 'CCLI'"
|
||||
class="link-ccli"
|
||||
href="https://songselect.ccli.com/Songs/{{ form.value.legalOwnerId }}"
|
||||
matSuffix
|
||||
<div class="half">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechtlicher Status</mat-label>
|
||||
<mat-select formControlName="legalType">
|
||||
@for (key of legalType; track key) {
|
||||
<mat-option [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">
|
||||
@for (key of legalOwner; track key) {
|
||||
<mat-option [value]="key">{{
|
||||
key | legalOwner
|
||||
}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechteinhaber ID (z.B. CCLI Liednummer)</mat-label>
|
||||
<input formControlName="legalOwnerId" matInput />
|
||||
@if (form.value.legalOwner === 'CCLI') {
|
||||
<a
|
||||
class="link-ccli"
|
||||
href="https://songselect.ccli.com/Songs/{{ form.value.legalOwnerId }}"
|
||||
matSuffix
|
||||
matTooltip="CCLI Link: https://songselect.ccli.com/Songs/{{
|
||||
form.value.legalOwnerId
|
||||
}}"
|
||||
matTooltipPosition="before"
|
||||
target="_blank"
|
||||
>
|
||||
<fa-icon [icon]="faLink"></fa-icon>
|
||||
</a>
|
||||
</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 / Copyright</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>abweichende Quelle</mat-label>
|
||||
<input formControlName="origin" matInput />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<app-button-row>
|
||||
<app-button (click)="onSave()" [icon]="faSave">Speichern</app-button>
|
||||
</app-button-row>
|
||||
</app-card>
|
||||
matTooltipPosition="before"
|
||||
target="_blank"
|
||||
>
|
||||
<fa-icon [icon]="faLink"></fa-icon>
|
||||
</a>
|
||||
}
|
||||
</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 / Copyright</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>abweichende Quelle</mat-label>
|
||||
<input formControlName="origin" matInput />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</form>
|
||||
<app-button-row>
|
||||
<app-button (click)="onSave()" [icon]="faSave">Speichern</app-button>
|
||||
</app-button-row>
|
||||
</app-card>
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRow} from '@angular
|
||||
import {faExternalLinkAlt, faSave, faTimesCircle} from '@fortawesome/free-solid-svg-icons';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {SaveDialogComponent} from './save-dialog/save-dialog.component';
|
||||
import {NgFor, NgIf} from '@angular/common';
|
||||
|
||||
import {CardComponent} from '../../../../../widget-modules/components/card/card.component';
|
||||
import {MatFormField, MatLabel, MatSuffix} from '@angular/material/form-field';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
@@ -34,14 +34,12 @@ import {StatusPipe} from '../../../../../widget-modules/pipes/status-translater/
|
||||
templateUrl: './edit-song.component.html',
|
||||
styleUrls: ['./edit-song.component.less'],
|
||||
imports: [
|
||||
NgIf,
|
||||
CardComponent,
|
||||
ReactiveFormsModule,
|
||||
MatFormField,
|
||||
MatLabel,
|
||||
MatInput,
|
||||
MatSelect,
|
||||
NgFor,
|
||||
MatOption,
|
||||
CdkTextareaAutosize,
|
||||
SongTextComponent,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<app-card *ngIf="song && song.edits" heading="letzte Änderungen">
|
||||
<div *ngFor="let edit of song.edits" class="list">
|
||||
<div>{{ edit.username }}</div>
|
||||
<div>{{ edit.timestamp.toDate() | date: "dd.MM.yyyy" }}</div>
|
||||
</div>
|
||||
</app-card>
|
||||
@if (song && song.edits) {
|
||||
<app-card heading="letzte Änderungen">
|
||||
@for (edit of song.edits; track edit) {
|
||||
<div class="list">
|
||||
<div>{{ edit.username }}</div>
|
||||
<div>{{ edit.timestamp.toDate() | date: "dd.MM.yyyy" }}</div>
|
||||
</div>
|
||||
}
|
||||
</app-card>
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ import {first, map, switchMap} from 'rxjs/operators';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {SongService} from '../../../services/song.service';
|
||||
import {Song} from '../../../services/song';
|
||||
import {DatePipe, NgFor, NgIf} from '@angular/common';
|
||||
import {DatePipe} from '@angular/common';
|
||||
import {CardComponent} from '../../../../../widget-modules/components/card/card.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-history',
|
||||
templateUrl: './history.component.html',
|
||||
styleUrls: ['./history.component.less'],
|
||||
imports: [NgIf, CardComponent, NgFor, DatePipe],
|
||||
imports: [CardComponent, DatePipe],
|
||||
})
|
||||
export class HistoryComponent implements OnInit {
|
||||
public song: Song | null = null;
|
||||
|
||||
@@ -1,86 +1,104 @@
|
||||
<div class="split">
|
||||
<app-card
|
||||
*ngIf="song$ | async as song"
|
||||
[heading]="song.number + ' - ' + song.title"
|
||||
closeLink="../"
|
||||
>
|
||||
<div class="song">
|
||||
<div>
|
||||
<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 }}
|
||||
@if (song$ | async; as song) {
|
||||
<app-card
|
||||
[heading]="song.number + ' - ' + song.title"
|
||||
closeLink="../"
|
||||
>
|
||||
<div class="song">
|
||||
<div>
|
||||
<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>
|
||||
@if (song.legalOwner) {
|
||||
<div>
|
||||
Rechteinhaber: {{ song.legalOwner | legalOwner }}
|
||||
</div>
|
||||
}
|
||||
@if (song.legalOwnerId && song.legalOwner === 'CCLI') {
|
||||
<div>
|
||||
<a
|
||||
href="https://songselect.ccli.com/Songs/{{ song.legalOwnerId }}"
|
||||
target="_blank"
|
||||
>
|
||||
CCLI Nummer: {{ song.legalOwnerId }}
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
@if (song.legalOwnerId && song.legalOwner !== 'CCLI') {
|
||||
<div>
|
||||
Rechteinhaber ID: {{ song.legalOwnerId }}
|
||||
</div>
|
||||
}
|
||||
@if (song.artist) {
|
||||
<div>Künstler: {{ song.artist }}</div>
|
||||
}
|
||||
@if (song.label) {
|
||||
<div>Verlag: {{ song.label }}</div>
|
||||
}
|
||||
@if (song.origin) {
|
||||
<div>Quelle: {{ song.origin }}</div>
|
||||
}
|
||||
<div>Wie oft verwendet: {{ songCount$ | async }}</div>
|
||||
</div>
|
||||
<div *ngIf="song.legalOwnerId && song.legalOwner === 'CCLI'">
|
||||
<a
|
||||
href="https://songselect.ccli.com/Songs/{{ song.legalOwnerId }}"
|
||||
target="_blank"
|
||||
>
|
||||
CCLI Nummer: {{ song.legalOwnerId }}
|
||||
</a>
|
||||
</div>
|
||||
<div *ngIf="song.legalOwnerId && song.legalOwner !== 'CCLI'">
|
||||
Rechteinhaber ID: {{ song.legalOwnerId }}
|
||||
</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>Wie oft verwendet: {{ songCount$ | async }}</div>
|
||||
</div>
|
||||
@if (user$ | async; as user) {
|
||||
<app-song-text
|
||||
[chordMode]="user.chordMode"
|
||||
[showSwitch]="true"
|
||||
[text]="song.text"
|
||||
></app-song-text>
|
||||
}
|
||||
<mat-chip-listbox
|
||||
*appRole="['leader', 'contributor']"
|
||||
aria-label="Attribute"
|
||||
>
|
||||
@for (flag of getFlags(song.flags); track flag) {
|
||||
<mat-chip-option>{{
|
||||
flag
|
||||
}}
|
||||
</mat-chip-option>
|
||||
}
|
||||
</mat-chip-listbox>
|
||||
<div *appRole="['leader', 'contributor']" class="text">
|
||||
{{ song.comment }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-song-text
|
||||
*ngIf="user$ | async as user"
|
||||
[chordMode]="user.chordMode"
|
||||
[showSwitch]="true"
|
||||
[text]="song.text"
|
||||
></app-song-text>
|
||||
|
||||
<mat-chip-listbox
|
||||
*appRole="['leader', 'contributor']"
|
||||
aria-label="Attribute"
|
||||
>
|
||||
<mat-chip-option *ngFor="let flag of getFlags(song.flags)">{{
|
||||
flag
|
||||
}}
|
||||
</mat-chip-option>
|
||||
</mat-chip-listbox>
|
||||
|
||||
<div *appRole="['leader', 'contributor']" class="text">
|
||||
{{ song.comment }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-button-row>
|
||||
<app-button
|
||||
(click)="onDelete(song.id)"
|
||||
*appRole="['admin']"
|
||||
[icon]="faDelete"
|
||||
>Löschen
|
||||
</app-button>
|
||||
<app-button *appRole="['contributor']" [icon]="faEdit" routerLink="edit"
|
||||
>Bearbeiten
|
||||
</app-button>
|
||||
<ng-container *appRole="['leader']">
|
||||
<app-button [icon]="faFileCirclePlus" [matMenuTriggerFor]="menu">
|
||||
Zu Veranstaltung hinzufügen
|
||||
<app-button-row>
|
||||
<app-button
|
||||
(click)="onDelete(song.id)"
|
||||
*appRole="['admin']"
|
||||
[icon]="faDelete"
|
||||
>Löschen
|
||||
</app-button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<app-button (click)="addSongToShow(show, song)" *ngFor="let show of privateShows$|async">
|
||||
{{ show.date.toDate() | date: "dd.MM.yyyy" }} {{ show.showType | showType }}
|
||||
<app-button *appRole="['contributor']" [icon]="faEdit" routerLink="edit"
|
||||
>Bearbeiten
|
||||
</app-button>
|
||||
<ng-container *appRole="['leader']">
|
||||
<app-button [icon]="faFileCirclePlus" [matMenuTriggerFor]="menu">
|
||||
Zu Veranstaltung hinzufügen
|
||||
</app-button>
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
</app-button-row>
|
||||
</app-card>
|
||||
<ng-container *ngIf="files$ | async as files">
|
||||
<app-card *ngIf="files.length > 0" heading="Anhänge">
|
||||
<p *ngFor="let file of files$ | async">
|
||||
<app-file [file]="file"></app-file>
|
||||
</p>
|
||||
<mat-menu #menu="matMenu">
|
||||
@for (show of privateShows$|async; track show) {
|
||||
<app-button (click)="addSongToShow(show, song)">
|
||||
{{ show.date.toDate() | date: "dd.MM.yyyy" }} {{ show.showType | showType }}
|
||||
</app-button>
|
||||
}
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
</app-button-row>
|
||||
</app-card>
|
||||
</ng-container>
|
||||
}
|
||||
@if (files$ | async; as files) {
|
||||
@if (files.length > 0) {
|
||||
<app-card heading="Anhänge">
|
||||
@for (file of files$ | async; track file) {
|
||||
<p>
|
||||
<app-file [file]="file"></app-file>
|
||||
</p>
|
||||
}
|
||||
</app-card>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ import {faEdit, faFileCirclePlus, faTrash} from '@fortawesome/free-solid-svg-ico
|
||||
import {ShowService} from '../../shows/services/show.service';
|
||||
import {Show} from '../../shows/services/show';
|
||||
import {ShowSongService} from '../../shows/services/show-song.service';
|
||||
import {AsyncPipe, DatePipe, NgFor, NgIf} from '@angular/common';
|
||||
import {AsyncPipe, DatePipe} from '@angular/common';
|
||||
import {CardComponent} from '../../../widget-modules/components/card/card.component';
|
||||
import {RoleDirective} from '../../../services/user/role.directive';
|
||||
import {SongTextComponent} from '../../../widget-modules/components/song-text/song-text.component';
|
||||
@@ -31,12 +31,10 @@ import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/s
|
||||
templateUrl: './song.component.html',
|
||||
styleUrls: ['./song.component.less'],
|
||||
imports: [
|
||||
NgIf,
|
||||
CardComponent,
|
||||
RoleDirective,
|
||||
SongTextComponent,
|
||||
MatChipListbox,
|
||||
NgFor,
|
||||
MatChipOption,
|
||||
ButtonRowComponent,
|
||||
ButtonComponent,
|
||||
|
||||
Reference in New Issue
Block a user