Zusätzliche Felder
This commit is contained in:
@@ -130,5 +130,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultProject": "wgenerator"
|
"defaultProject": "wgenerator",
|
||||||
}
|
"cli": {
|
||||||
|
"analytics": "4047dcd7-89f4-402f-958e-e365a5505c55"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ import {AngularFireDatabaseModule} from '@angular/fire/database';
|
|||||||
|
|
||||||
|
|
||||||
AngularFireModule.initializeApp(environment.firebase),
|
AngularFireModule.initializeApp(environment.firebase),
|
||||||
AngularFirestoreModule.enablePersistence(),
|
AngularFirestoreModule.enablePersistence({synchronizeTabs: true}),
|
||||||
AngularFireStorageModule,
|
AngularFireStorageModule,
|
||||||
AngularFireDatabaseModule
|
AngularFireDatabaseModule
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,15 @@ export interface Song {
|
|||||||
text: string;
|
text: string;
|
||||||
title: string;
|
title: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
|
||||||
|
legalType: string;
|
||||||
|
legalLink: string;
|
||||||
|
legalOwner: string;
|
||||||
|
legalOwnerId: string;
|
||||||
|
legalLicenseId: string;
|
||||||
|
|
||||||
|
artist: string;
|
||||||
|
label: string;
|
||||||
|
termsOfUse: string;
|
||||||
|
origin: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { FileDataService } from './file-data.service';
|
import {FileDataService} from './file-data.service';
|
||||||
|
|
||||||
describe('FileDataService', () => {
|
describe('FileDataService', () => {
|
||||||
beforeEach(() => TestBed.configureTestingModule({}));
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {SongDataService} from './song-data.service';
|
import {SongDataService} from './song-data.service';
|
||||||
import {File} from './file';
|
import {File} from './file';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
import {map} from 'rxjs/operators';
|
||||||
|
import {FileServer} from './fileServer';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -10,10 +13,22 @@ export class FileDataService {
|
|||||||
constructor(private songDataService: SongDataService) {
|
constructor(private songDataService: SongDataService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async put(songId: string, file: File): Promise<string> {
|
public async put(songId: string, file: FileServer): Promise<string> {
|
||||||
const songRef = this.songDataService.getSongRef(songId);
|
const songRef = this.songDataService.getSongRef(songId);
|
||||||
const fileCollection = songRef.collection('files');
|
const fileCollection = songRef.collection('files');
|
||||||
const id = await fileCollection.add(file);
|
const id = await fileCollection.add(file);
|
||||||
return id.id;
|
return id.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get$(songId: string): Observable<File[]> {
|
||||||
|
const songRef = this.songDataService.getSongRef(songId);
|
||||||
|
return songRef.collection<File>('files').snapshotChanges().pipe(map(actions => {
|
||||||
|
return actions.map(a => ({
|
||||||
|
...a.payload.doc.data(),
|
||||||
|
id: a.payload.doc.id
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export interface File {
|
import {FileServer} from './fileServer';
|
||||||
name: string;
|
|
||||||
path: string;
|
export interface File extends FileServer {
|
||||||
createdAt: Date;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/app/songs/services/fileBase.ts
Normal file
5
src/app/songs/services/fileBase.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export class FileBase {
|
||||||
|
|
||||||
|
protected basePath = '/attachments';
|
||||||
|
protected directory = (songId: string) => `${this.basePath}/${songId}`;
|
||||||
|
}
|
||||||
5
src/app/songs/services/fileServer.ts
Normal file
5
src/app/songs/services/fileServer.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface FileServer {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
@@ -32,9 +32,9 @@ describe('SongDataService', () => {
|
|||||||
it('should list songs', async(() => {
|
it('should list songs', async(() => {
|
||||||
const service: SongDataService = TestBed.get(SongDataService);
|
const service: SongDataService = TestBed.get(SongDataService);
|
||||||
service.list().subscribe(s => {
|
service.list().subscribe(s => {
|
||||||
expect(s).toEqual([
|
expect(s).toEqual([
|
||||||
{title: 'title1'}
|
{title: 'title1'}
|
||||||
] as any);
|
] as any);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import {Injectable} from '@angular/core';
|
|||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {Song} from '../models/song';
|
import {Song} from '../models/song';
|
||||||
import {SongDataService} from './song-data.service';
|
import {SongDataService} from './song-data.service';
|
||||||
|
import {tap} from 'rxjs/operators';
|
||||||
|
|
||||||
|
declare var importCCLI: any;
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -10,19 +13,49 @@ export class SongService {
|
|||||||
|
|
||||||
public TYPES = ['Praise', 'Worship'];
|
public TYPES = ['Praise', 'Worship'];
|
||||||
|
|
||||||
|
public LEGAL_OWNER = ['CCLI', 'other'];
|
||||||
|
public LEGAL_TYPE = ['open', 'allowed'];
|
||||||
|
|
||||||
public KEYS = [
|
public KEYS = [
|
||||||
'C', 'C#', 'Db', 'D', 'D#', 'Eb', 'E', 'F', 'F#', 'Gb', 'G', 'G#', 'Ab', 'A', 'A#', 'B', 'H',
|
'C', 'C#', 'Db', 'D', 'D#', 'Eb', 'E', 'F', 'F#', 'Gb', 'G', 'G#', 'Ab', 'A', 'A#', 'B', 'H',
|
||||||
'c', 'c#', 'db', 'd', 'd#', 'eb', 'e', 'f', 'f#', 'gb', 'g', 'g#', 'ab', 'a', 'a#', 'b', 'h'
|
'c', 'c#', 'db', 'd', 'd#', 'eb', 'e', 'f', 'f#', 'gb', 'g', 'g#', 'ab', 'a', 'a#', 'b', 'h'
|
||||||
];
|
];
|
||||||
|
private list: Song[];
|
||||||
|
|
||||||
constructor(private songDataService: SongDataService) {
|
constructor(private songDataService: SongDataService) {
|
||||||
|
importCCLI = (songs: Song[]) => this.updateFromCLI(songs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public list$ = (): Observable<Song[]> => this.songDataService.list();
|
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 async update(songId: string, data: any): Promise<void> {
|
public async update(songId: string, data: any): Promise<void> {
|
||||||
await this.songDataService.update(songId, data);
|
await this.songDataService.update(songId, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.csvjson.com/csv2json
|
||||||
|
private async updateFromCLI(songs: Song[]) {
|
||||||
|
const mapped = songs.map(_ => ({
|
||||||
|
number: _.number,
|
||||||
|
legalType: _.legalType === 'ja' ? 'allowed' : 'open',
|
||||||
|
legalOwner: _.legalOwner === 'ja' ? 'CCLI' : 'other',
|
||||||
|
title: _.title,
|
||||||
|
legalOwnerId: _.legalOwnerId,
|
||||||
|
origin: _.origin,
|
||||||
|
artist: _.artist,
|
||||||
|
comment: _.comment
|
||||||
|
}));
|
||||||
|
const promises = this.list.map(async _ => {
|
||||||
|
// tslint:disable-next-line:triple-equals
|
||||||
|
const mappedSongs = mapped.filter(f => f.number == _.number);
|
||||||
|
if (mappedSongs.length === 1) {
|
||||||
|
const mappedSong = mappedSongs[0];
|
||||||
|
const id = _.id;
|
||||||
|
return await this.update(id, mappedSong);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/app/songs/services/text-rendering.service.spec.ts
Normal file
12
src/app/songs/services/text-rendering.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { TextRenderingService } from './text-rendering.service';
|
||||||
|
|
||||||
|
describe('TextRenderingService', () => {
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: TextRenderingService = TestBed.get(TextRenderingService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
44
src/app/songs/services/text-rendering.service.ts
Normal file
44
src/app/songs/services/text-rendering.service.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
export enum SectionType {
|
||||||
|
Verse,
|
||||||
|
Chorus,
|
||||||
|
Bridge,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum LineType {
|
||||||
|
title,
|
||||||
|
chrod,
|
||||||
|
text,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Line {
|
||||||
|
type: LineType;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface Section {
|
||||||
|
type: SectionType;
|
||||||
|
number: number;
|
||||||
|
lines: Line[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class TextRenderingService {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
public render(text: string): Section[] {
|
||||||
|
const lines = text.match(/[^\r\n]+/g);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private findSection(line: string) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { UploadService } from './upload.service';
|
import {UploadService} from './upload.service';
|
||||||
|
|
||||||
describe('UploadServiceService', () => {
|
describe('UploadServiceService', () => {
|
||||||
beforeEach(() => TestBed.configureTestingModule({}));
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|||||||
@@ -4,20 +4,23 @@ import {FileDataService} from './file-data.service';
|
|||||||
import {AngularFireStorage} from '@angular/fire/storage';
|
import {AngularFireStorage} from '@angular/fire/storage';
|
||||||
import {finalize} from 'rxjs/operators';
|
import {finalize} from 'rxjs/operators';
|
||||||
import {File} from './file';
|
import {File} from './file';
|
||||||
|
import {FileBase} from './fileBase';
|
||||||
|
import {FileServer} from './fileServer';
|
||||||
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class UploadService {
|
export class UploadService extends FileBase {
|
||||||
private basePath = '/attachments';
|
|
||||||
|
|
||||||
constructor(private fileDataService: FileDataService, private angularFireStorage: AngularFireStorage) {
|
constructor(private fileDataService: FileDataService, private angularFireStorage: AngularFireStorage) {
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async pushUpload(songId: string, upload: Upload) {
|
public async pushUpload(songId: string, upload: Upload) {
|
||||||
const path = `${this.basePath}/${songId}`;
|
const directory = this.directory(songId);
|
||||||
const filePath = `${path}/${upload.file.name}`;
|
const filePath = `${directory}/${upload.file.name}`;
|
||||||
upload.path = path;
|
upload.path = directory;
|
||||||
|
|
||||||
const ref = this.angularFireStorage.ref(filePath);
|
const ref = this.angularFireStorage.ref(filePath);
|
||||||
const task = ref.put(upload.file);
|
const task = ref.put(upload.file);
|
||||||
@@ -48,7 +51,7 @@ export class UploadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async saveFileData(songId: string, upload: Upload) {
|
private async saveFileData(songId: string, upload: Upload) {
|
||||||
const file: File = {
|
const file: FileServer = {
|
||||||
name: upload.file.name,
|
name: upload.file.name,
|
||||||
path: upload.path,
|
path: upload.path,
|
||||||
createdAt: new Date()
|
createdAt: new Date()
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
<div class="number">{{song.number}}</div>
|
<div class="number">{{song.number}}</div>
|
||||||
<div>{{song.title}}</div>
|
<div>{{song.title}}</div>
|
||||||
<div>{{song.key}}</div>
|
<div>{{song.key}}</div>
|
||||||
<div>{{song.type | songType}}</div>
|
<div>{{song.legalType | legalType}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ import {ActivatedRoute} from '@angular/router';
|
|||||||
})
|
})
|
||||||
export class SongListComponent implements OnInit {
|
export class SongListComponent implements OnInit {
|
||||||
|
|
||||||
|
public songs$: Observable<Song[]>;
|
||||||
|
|
||||||
constructor(private songService: SongService, private activatedRoute: ActivatedRoute) {
|
constructor(private songService: SongService, private activatedRoute: ActivatedRoute) {
|
||||||
}
|
}
|
||||||
public songs$: Observable<Song[]>;
|
|
||||||
|
|
||||||
private static filter(song: Song, filterValue: string): boolean {
|
private static filter(song: Song, filterValue: string): boolean {
|
||||||
if (!filterValue) {
|
if (!filterValue) {
|
||||||
|
|||||||
@@ -3,20 +3,20 @@ import {CommonModule} from '@angular/common';
|
|||||||
import {SongListComponent} from './song-list.component';
|
import {SongListComponent} from './song-list.component';
|
||||||
import {ListItemComponent} from './list-item/list-item.component';
|
import {ListItemComponent} from './list-item/list-item.component';
|
||||||
import {CardModule} from '../../widget-modules/components/card/card.module';
|
import {CardModule} from '../../widget-modules/components/card/card.module';
|
||||||
import {SongTypeTranslaterModule} from '../../widget-modules/pipes/song-type-translater/song-type-translater.module';
|
|
||||||
import {RouterModule} from '@angular/router';
|
import {RouterModule} from '@angular/router';
|
||||||
|
import {LegalTypeTranslatorModule} from '../../widget-modules/pipes/legal-type-translator/legal-type-translator.module';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [SongListComponent, ListItemComponent],
|
declarations: [SongListComponent, ListItemComponent],
|
||||||
exports: [SongListComponent],
|
exports: [SongListComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
RouterModule,
|
RouterModule,
|
||||||
|
|
||||||
CardModule,
|
CardModule,
|
||||||
SongTypeTranslaterModule
|
LegalTypeTranslatorModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SongListModule {
|
export class SongListModule {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,18 @@
|
|||||||
|
|
||||||
<div *ngIf="currentUpload">
|
<div *ngIf="currentUpload">
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar progress-bar-animated" [ngStyle]="{ 'width': currentUpload?.progress + '%' }"></div>
|
<div [ngStyle]="{ 'width': currentUpload?.progress + '%' }" class="progress-bar progress-bar-animated"></div>
|
||||||
</div>
|
</div>
|
||||||
Progress: {{currentUpload?.name}} | {{currentUpload?.progress}}% Complete
|
Progress: {{currentUpload?.name}} | {{currentUpload?.progress}}% Complete
|
||||||
</div>
|
</div>
|
||||||
<div class="upload">
|
<div class="upload">
|
||||||
<label>
|
<label>
|
||||||
<input type="file" (change)="detectFiles($event)">
|
<input (change)="detectFiles($event)" type="file">
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<button mat-icon-button
|
<button (click)="uploadSingle()"
|
||||||
[disabled]="!selectedFiles"
|
[disabled]="!selectedFiles"
|
||||||
(click)="uploadSingle()">
|
mat-icon-button>
|
||||||
<mat-icon>cloud_upload</mat-icon>
|
<mat-icon>cloud_upload</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { EditFileComponent } from './edit-file.component';
|
import {EditFileComponent} from './edit-file.component';
|
||||||
|
|
||||||
describe('EditFileComponent', () => {
|
describe('EditFileComponent', () => {
|
||||||
let component: EditFileComponent;
|
let component: EditFileComponent;
|
||||||
@@ -8,9 +8,9 @@ describe('EditFileComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ EditFileComponent ]
|
declarations: [EditFileComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { FileComponent } from './file.component';
|
import {FileComponent} from './file.component';
|
||||||
|
|
||||||
describe('FileComponent', () => {
|
describe('FileComponent', () => {
|
||||||
let component: FileComponent;
|
let component: FileComponent;
|
||||||
@@ -8,9 +8,9 @@ describe('FileComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ FileComponent ]
|
declarations: [FileComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-file',
|
selector: 'app-file',
|
||||||
@@ -7,7 +7,8 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
})
|
})
|
||||||
export class FileComponent implements OnInit {
|
export class FileComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,61 @@
|
|||||||
<textarea [mat-autosize]="true" formControlName="text" matInput></textarea>
|
<textarea [mat-autosize]="true" formControlName="text" matInput></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Kommentar</mat-label>
|
||||||
|
<textarea [mat-autosize]="true" formControlName="comment" matInput></textarea>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<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 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>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>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>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|||||||
@@ -11,4 +11,8 @@
|
|||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
column-gap: 20px;
|
column-gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
font-family: 'Ubuntu Mono', monospace;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { EditSongComponent } from './edit-song.component';
|
import {EditSongComponent} from './edit-song.component';
|
||||||
|
|
||||||
describe('EditSongComponent', () => {
|
describe('EditSongComponent', () => {
|
||||||
let component: EditSongComponent;
|
let component: EditSongComponent;
|
||||||
@@ -8,9 +8,9 @@ describe('EditSongComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ EditSongComponent ]
|
declarations: [EditSongComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {Song} from '../../../models/song';
|
import {Song} from '../../../models/song';
|
||||||
import {FormGroup} from '@angular/forms';
|
import {FormGroup} from '@angular/forms';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
@@ -16,6 +16,8 @@ export class EditSongComponent implements OnInit {
|
|||||||
public form: FormGroup;
|
public form: FormGroup;
|
||||||
public keys = this.songService.KEYS;
|
public keys = this.songService.KEYS;
|
||||||
public types = this.songService.TYPES;
|
public types = this.songService.TYPES;
|
||||||
|
public legalOwner = this.songService.LEGAL_OWNER;
|
||||||
|
public legalType = this.songService.LEGAL_TYPE;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ import {MatSelectModule} from '@angular/material/select';
|
|||||||
import {MatButtonModule} from '@angular/material/button';
|
import {MatButtonModule} from '@angular/material/button';
|
||||||
import {ButtonRowModule} from '../../../widget-modules/components/button-row/button-row.module';
|
import {ButtonRowModule} from '../../../widget-modules/components/button-row/button-row.module';
|
||||||
import {RouterModule} from '@angular/router';
|
import {RouterModule} from '@angular/router';
|
||||||
import { EditSongComponent } from './edit-song/edit-song.component';
|
import {EditSongComponent} from './edit-song/edit-song.component';
|
||||||
import { EditFileComponent } from './edit-file/edit-file.component';
|
import {EditFileComponent} from './edit-file/edit-file.component';
|
||||||
import {MatIconModule} from '@angular/material/icon';
|
import {MatIconModule} from '@angular/material/icon';
|
||||||
import { FileComponent } from './edit-file/file/file.component';
|
import {FileComponent} from './edit-file/file/file.component';
|
||||||
|
import {LegalOwnerTranslatorModule} from '../../../widget-modules/pipes/legal-owner-translator/legal-owner-translator.module';
|
||||||
|
import {LegalTypeTranslatorModule} from '../../../widget-modules/pipes/legal-type-translator/legal-type-translator.module';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -33,6 +35,8 @@ import { FileComponent } from './edit-file/file/file.component';
|
|||||||
ButtonRowModule,
|
ButtonRowModule,
|
||||||
|
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
|
LegalOwnerTranslatorModule,
|
||||||
|
LegalTypeTranslatorModule,
|
||||||
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -17,7 +17,18 @@ export class EditService {
|
|||||||
comment: new FormControl(song.comment),
|
comment: new FormControl(song.comment),
|
||||||
key: new FormControl(song.key),
|
key: new FormControl(song.key),
|
||||||
tempo: new FormControl(song.tempo),
|
tempo: new FormControl(song.tempo),
|
||||||
type: new FormControl(song.type)
|
type: new FormControl(song.type),
|
||||||
|
|
||||||
|
legalType: new FormControl(song.legalType),
|
||||||
|
legalLink: new FormControl(song.legalLink),
|
||||||
|
legalOwner: new FormControl(song.legalOwner),
|
||||||
|
legalOwnerId: new FormControl(song.legalOwnerId),
|
||||||
|
legalLicenseId: new FormControl(song.legalLicenseId),
|
||||||
|
|
||||||
|
artist: new FormControl(song.artist),
|
||||||
|
label: new FormControl(song.label),
|
||||||
|
termsOfUse: new FormControl(song.termsOfUse),
|
||||||
|
origin: new FormControl(song.origin),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,29 @@
|
|||||||
<app-card *ngIf="song$ | async as song" [heading]="song.number + ' - ' + song.title">
|
<app-card *ngIf="song$ | async as song" [heading]="song.number + ' - ' + song.title">
|
||||||
<div class="song">
|
<div class="song">
|
||||||
<div class="text">{{song.text}}</div>
|
|
||||||
<div>
|
<div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<div>Typ</div>
|
<div>Typ: {{song.type | songType}}</div>
|
||||||
<div>{{song.type | songType}}</div>
|
<div>Tonart: {{song.key}}</div>
|
||||||
<div>Tonart</div>
|
<div>Tempo: {{song.tempo}}</div>
|
||||||
<div>{{song.key}}</div>
|
<div *ngIf="song.legalOwner">Rechteinhaber: {{song.legalOwner|legalOwner}}</div>
|
||||||
<div>Tempo</div>
|
<div *ngIf="song.legalOwnerId">Rechteinhaber ID: {{song.legalOwnerId}}</div>
|
||||||
<div>{{song.tempo}}</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="text">{{song.text}}</div>
|
||||||
|
<div class="text">{{song.comment}}</div>
|
||||||
|
<div>
|
||||||
|
<h3>Anhänge</h3>
|
||||||
|
<div *ngFor="let file of (files$|async)">{{file.name}}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-button-row>
|
<app-button-row>
|
||||||
<button color="primary" mat-flat-button routerLink="edit">Bearbeiten</button>
|
<button color="primary" mat-flat-button routerLink="edit">Bearbeiten</button>
|
||||||
</app-button-row>
|
</app-button-row>
|
||||||
|
|
||||||
</app-card>
|
</app-card>
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
.song {
|
.song {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 2fr 1fr;
|
grid-template-columns: 1fr;
|
||||||
column-gap: 20px;
|
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
grid-gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
font-family: 'Ubuntu Mono', monospace;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail {
|
.detail {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 2fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import {SongService} from '../services/song.service';
|
|||||||
import {map, switchMap} from 'rxjs/operators';
|
import {map, switchMap} from 'rxjs/operators';
|
||||||
import {Song} from '../models/song';
|
import {Song} from '../models/song';
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
|
import {FileDataService} from '../services/file-data.service';
|
||||||
|
import {File} from '../services/file';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-song',
|
selector: 'app-song',
|
||||||
@@ -12,8 +14,13 @@ import {Observable} from 'rxjs';
|
|||||||
})
|
})
|
||||||
export class SongComponent implements OnInit {
|
export class SongComponent implements OnInit {
|
||||||
public song$: Observable<Song>;
|
public song$: Observable<Song>;
|
||||||
|
public files$: Observable<File[]>;
|
||||||
|
|
||||||
constructor(private activatedRoute: ActivatedRoute, private songService: SongService) {
|
constructor(
|
||||||
|
private activatedRoute: ActivatedRoute,
|
||||||
|
private songService: SongService,
|
||||||
|
private fileService: FileDataService,
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
@@ -21,6 +28,12 @@ export class SongComponent implements OnInit {
|
|||||||
map(param => param.songId),
|
map(param => param.songId),
|
||||||
switchMap(songId => this.songService.read(songId))
|
switchMap(songId => this.songService.read(songId))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.files$ = this.activatedRoute.params.pipe(
|
||||||
|
map(param => param.songId),
|
||||||
|
switchMap(songId => this.fileService.get$(songId))
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,20 +6,22 @@ import {SongTypeTranslaterModule} from '../../widget-modules/pipes/song-type-tra
|
|||||||
import {MatButtonModule} from '@angular/material/button';
|
import {MatButtonModule} from '@angular/material/button';
|
||||||
import {ButtonRowModule} from '../../widget-modules/components/button-row/button-row.module';
|
import {ButtonRowModule} from '../../widget-modules/components/button-row/button-row.module';
|
||||||
import {RouterModule} from '@angular/router';
|
import {RouterModule} from '@angular/router';
|
||||||
|
import {LegalOwnerTranslatorModule} from '../../widget-modules/pipes/legal-owner-translator/legal-owner-translator.module';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [SongComponent],
|
declarations: [SongComponent],
|
||||||
exports: [SongComponent],
|
exports: [SongComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
CardModule,
|
CardModule,
|
||||||
RouterModule,
|
RouterModule,
|
||||||
|
|
||||||
SongTypeTranslaterModule,
|
SongTypeTranslaterModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
ButtonRowModule,
|
ButtonRowModule,
|
||||||
]
|
LegalOwnerTranslatorModule,
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class SongModule {
|
export class SongModule {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { LegalTypePipe } from './legal-type.pipe';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [LegalTypePipe],
|
||||||
|
exports: [
|
||||||
|
LegalTypePipe
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class LegalTypeTranslatorModule { }
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { LegalTypePipe } from './legal-type.pipe';
|
||||||
|
|
||||||
|
describe('LegalTypePipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new LegalTypePipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'legalType'
|
||||||
|
})
|
||||||
|
export class LegalTypePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(legalTypeKey: string): string {
|
||||||
|
switch (legalTypeKey) {
|
||||||
|
case 'open':
|
||||||
|
return 'Klärung erforderlich ';
|
||||||
|
case 'allowed':
|
||||||
|
return 'OK';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Wgenerator</title>
|
<title>Worship Generator</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||||
<link href="favicon.ico" rel="icon" type="image/x-icon">
|
<link href="favicon.ico" rel="icon" type="image/x-icon">
|
||||||
@@ -10,9 +10,11 @@
|
|||||||
<meta content="#1976d2" name="theme-color">
|
<meta content="#1976d2" name="theme-color">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Ubuntu+Mono&display=swap" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
|
<script>importCCLI={}</script>
|
||||||
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"fullTemplateTypeCheck": true,
|
"fullTemplateTypeCheck": true,
|
||||||
"strictInjectionParameters": true,
|
"strictInjectionParameters": true,
|
||||||
"enableIvy": false
|
"strictTemplate": true,
|
||||||
|
"enableIvy": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user