migrate angular 21

This commit is contained in:
2026-03-09 22:43:40 +01:00
parent 0203d4ea9d
commit 26c99a0dae
65 changed files with 19188 additions and 16946 deletions

View File

@@ -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>

View File

@@ -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;

View File

@@ -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 }}&nbsp;
<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 }}&nbsp;
<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>
}

View File

@@ -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,

View File

@@ -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>
}

View File

@@ -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;