validate chords #3

This commit is contained in:
2026-03-11 17:13:17 +01:00
parent 9f47f259c0
commit 0452ec55b2
11 changed files with 624 additions and 462 deletions

View File

@@ -7,7 +7,12 @@
@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 class="title">
<span>{{ song.title }}</span>
@if (song.hasChordValidationIssues) {
<span class="validation-star" title="Akkord-Validierungsfehler vorhanden">*</span>
}
</div>
<div>
<ng-container *appRole="['contributor']">
@if (song.status === 'draft') {

View File

@@ -27,6 +27,15 @@
text-align: right;
}
.title {
gap: 6px;
}
.validation-star {
color: var(--danger);
font-weight: bold;
}
.neutral, .warning, .success {
width: 30px;
}

View File

@@ -9,6 +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 {TextRenderingService} from '../services/text-rendering.service';
import {AsyncPipe} from '@angular/common';
import {ListHeaderComponent} from '../../../widget-modules/components/list-header/list-header.component';
import {FilterComponent} from './filter/filter.component';
@@ -16,6 +17,10 @@ import {CardComponent} from '../../../widget-modules/components/card/card.compon
import {RoleDirective} from '../../../services/user/role.directive';
import {FaIconComponent} from '@fortawesome/angular-fontawesome';
interface SongListItem extends Song {
hasChordValidationIssues: boolean;
}
@Component({
selector: 'app-songs',
templateUrl: './song-list.component.html',
@@ -28,15 +33,22 @@ export class SongListComponent implements OnInit, OnDestroy {
private songService = inject(SongService);
private activatedRoute = inject(ActivatedRoute);
private scrollService = inject(ScrollService);
private textRenderingService = inject(TextRenderingService);
public anyFilterActive = false;
public songs$: Observable<Song[]> = combineLatest([
public songs$: Observable<SongListItem[]> = combineLatest([
this.activatedRoute.queryParams.pipe(map(_ => _ as FilterValues)),
this.songService.list$().pipe(map(songs => [...songs].sort((a, b) => a.number - b.number))),
]).pipe(
map(([filter, songs]) => {
this.anyFilterActive = this.checkIfFilterActive(filter);
return songs.filter(song => this.filter(song, filter)).sort((a, b) => a.title?.localeCompare(b.title));
return songs
.filter(song => this.filter(song, filter))
.map(song => ({
...song,
hasChordValidationIssues: this.textRenderingService.validateChordNotation(song.text ?? '').length > 0,
}))
.sort((a, b) => a.title?.localeCompare(b.title));
})
);
public faLegal = faBalanceScaleRight;
@@ -52,7 +64,7 @@ export class SongListComponent implements OnInit, OnDestroy {
this.scrollService.storeScrollPositionFor('songlist');
}
public trackBy = (index: number, show: Song) => show.id;
public trackBy = (index: number, show: SongListItem) => show.id;
private filter(song: Song, filter: FilterValues): boolean {
let baseFilter = filterSong(song, filter.q);