ui enhancements and song state
This commit is contained in:
@@ -32,7 +32,7 @@ export class MonitorComponent implements OnInit {
|
|||||||
switchMap(_ => this.showService.read$(_)),
|
switchMap(_ => this.showService.read$(_)),
|
||||||
tap(_ => this.index = _.presentationSection),
|
tap(_ => this.index = _.presentationSection),
|
||||||
tap(_ => this.zoom = _.presentationZoom ?? 30),
|
tap(_ => this.zoom = _.presentationZoom ?? 30),
|
||||||
switchMap(_ => this.songService.read(_.presentationSongId))
|
switchMap(_ => this.songService.read$(_.presentationSongId))
|
||||||
).subscribe(_ => {
|
).subscribe(_ => {
|
||||||
this.song = _;
|
this.song = _;
|
||||||
this.sections = this.textRenderingService.parse(_.text);
|
this.sections = this.textRenderingService.parse(_.text);
|
||||||
|
|||||||
@@ -141,11 +141,11 @@ export class DocxService {
|
|||||||
|
|
||||||
private async prepareData(showId: string): Promise<{ songs: ({ showSong: ShowSong, song: Song, sections: Section[] })[]; show: Show, user: User }> {
|
private async prepareData(showId: string): Promise<{ songs: ({ showSong: ShowSong, song: Song, sections: Section[] })[]; show: Show, user: User }> {
|
||||||
const show = await this.showService.read$(showId).pipe(first()).toPromise();
|
const show = await this.showService.read$(showId).pipe(first()).toPromise();
|
||||||
const user = await this.userService.getUserbyId$(show.owner).pipe(first()).toPromise();
|
const user = await this.userService.getUserbyId(show.owner);
|
||||||
|
|
||||||
const showSongs = await this.showSongService.list$(showId).pipe(first()).toPromise();
|
const showSongs = await this.showSongService.list(showId);
|
||||||
const songsAsync = await showSongs.map(async showSong => {
|
const songsAsync = await showSongs.map(async showSong => {
|
||||||
const song = await this.songService.read(showSong.songId).pipe(first()).toPromise();
|
const song = await this.songService.read(showSong.songId);
|
||||||
const sections = this.textRenderingService.parse(song.text);
|
const sections = this.textRenderingService.parse(song.text);
|
||||||
return {
|
return {
|
||||||
showSong,
|
showSong,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {ShowSongDataService} from './show-song-data.service';
|
|||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {ShowSong} from './show-song';
|
import {ShowSong} from './show-song';
|
||||||
import {SongDataService} from '../../songs/services/song-data.service';
|
import {SongDataService} from '../../songs/services/song-data.service';
|
||||||
import {take} from 'rxjs/operators';
|
import {first, take} from 'rxjs/operators';
|
||||||
import {UserService} from '../../../services/user/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@@ -33,6 +33,7 @@ export class ShowSongService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public list$ = (showId: string): Observable<ShowSong[]> => this.showSongDataService.list$(showId, _ => _.orderBy('order'));
|
public list$ = (showId: string): Observable<ShowSong[]> => this.showSongDataService.list$(showId, _ => _.orderBy('order'));
|
||||||
|
public list = (showId: string): Promise<ShowSong[]> => this.list$(showId).pipe(first()).toPromise();
|
||||||
public delete$ = (showId: string, songId: string): Promise<void> => this.showSongDataService.delete(showId, songId);
|
public delete$ = (showId: string, songId: string): Promise<void> => this.showSongDataService.delete(showId, songId);
|
||||||
public update$ = async (showId: string, songId: string, data: Partial<ShowSong>): Promise<void> => await this.showSongDataService.update$(showId, songId, data);
|
public update$ = async (showId: string, songId: string, data: Partial<ShowSong>): Promise<void> => await this.showSongDataService.update$(showId, songId, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<div *ngIf="(show$|async) as show">
|
<div *ngIf="(show$|async) as show">
|
||||||
<app-card
|
<app-card
|
||||||
|
closeLink="../"
|
||||||
heading="{{show.showType|showType}}, {{show.date.toDate()|date:'dd.MM.yyyy'}} - {{getStatus(show)}}">
|
heading="{{show.showType|showType}}, {{show.date.toDate()|date:'dd.MM.yyyy'}} - {{getStatus(show)}}">
|
||||||
<i *ngIf="show.public">öffentliche Veranstaltung</i>
|
<i *ngIf="show.public">öffentliche Veranstaltung</i>
|
||||||
<i *ngIf="!show.public">geschlossene Veranstaltung</i>
|
<i *ngIf="!show.public">geschlossene Veranstaltung</i>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {Injectable} from '@angular/core';
|
|||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {Song} from './song';
|
import {Song} from './song';
|
||||||
import {SongDataService} from './song-data.service';
|
import {SongDataService} from './song-data.service';
|
||||||
import {tap} from 'rxjs/operators';
|
import {first, tap} from 'rxjs/operators';
|
||||||
|
|
||||||
declare var importCCLI: any;
|
declare var importCCLI: any;
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ declare var importCCLI: any;
|
|||||||
export class SongService {
|
export class SongService {
|
||||||
|
|
||||||
public static TYPES = ['Praise', 'Worship'];
|
public static TYPES = ['Praise', 'Worship'];
|
||||||
|
public static STATUS = ['draft', 'set', 'final'];
|
||||||
|
|
||||||
public static LEGAL_OWNER = ['CCLI', 'other'];
|
public static LEGAL_OWNER = ['CCLI', 'other'];
|
||||||
public static LEGAL_TYPE = ['open', 'allowed'];
|
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 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> {
|
public async update$(songId: string, data: Partial<Song>): Promise<void> {
|
||||||
await this.songDataService.update$(songId, data);
|
await this.songDataService.update$(songId, data);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export interface Song {
|
|||||||
title: string;
|
title: string;
|
||||||
type: string;
|
type: string;
|
||||||
flags: string;
|
flags: string;
|
||||||
|
status: string;
|
||||||
|
|
||||||
legalType: string;
|
legalType: string;
|
||||||
legalLink: 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">
|
<form [formGroup]="form" class="form">
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<input formControlName="title" matInput>
|
<input formControlName="title" matInput>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<div class="third">
|
<div class="fourth">
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Typ</mat-label>
|
<mat-label>Typ</mat-label>
|
||||||
<mat-select formControlName="type">
|
<mat-select formControlName="type">
|
||||||
@@ -23,6 +23,12 @@
|
|||||||
<mat-label>Tempo</mat-label>
|
<mat-label>Tempo</mat-label>
|
||||||
<input formControlName="tempo" matInput>
|
<input formControlName="tempo" matInput>
|
||||||
</mat-form-field>
|
</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>
|
</div>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
@@ -50,60 +56,61 @@
|
|||||||
</mat-chip-list>
|
</mat-chip-list>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<div class="half">
|
||||||
<mat-label>Rechtlicher Status</mat-label>
|
<mat-form-field appearance="outline">
|
||||||
<mat-select formControlName="legalType">
|
<mat-label>Rechtlicher Status</mat-label>
|
||||||
<mat-option *ngFor="let key of legalType" [value]="key">{{key|legalType}}</mat-option>
|
<mat-select formControlName="legalType">
|
||||||
</mat-select>
|
<mat-option *ngFor="let key of legalType" [value]="key">{{key|legalType}}</mat-option>
|
||||||
</mat-form-field>
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Rechteinhaber</mat-label>
|
<mat-label>Rechteinhaber</mat-label>
|
||||||
<mat-select formControlName="legalOwner">
|
<mat-select formControlName="legalOwner">
|
||||||
<mat-option *ngFor="let key of legalOwner" [value]="key">{{key|legalOwner}}</mat-option>
|
<mat-option *ngFor="let key of legalOwner" [value]="key">{{key|legalOwner}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Rechteinhaber Link</mat-label>
|
<mat-label>Rechteinhaber Link</mat-label>
|
||||||
<input formControlName="legalLink" matInput>
|
<input formControlName="legalLink" matInput>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Rechteinhaber ID (z.B. CCLI Liednummer)</mat-label>
|
<mat-label>Rechteinhaber ID (z.B. CCLI Liednummer)</mat-label>
|
||||||
<input formControlName="legalOwnerId" matInput>
|
<input formControlName="legalOwnerId" matInput>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Lizenznummer</mat-label>
|
<mat-label>Lizenznummer</mat-label>
|
||||||
<input formControlName="legalLicenseId" matInput>
|
<input formControlName="legalLicenseId" matInput>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Künstler</mat-label>
|
<mat-label>Künstler</mat-label>
|
||||||
<input formControlName="artist" matInput>
|
<input formControlName="artist" matInput>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Verlag</mat-label>
|
<mat-label>Verlag</mat-label>
|
||||||
<input formControlName="label" matInput>
|
<input formControlName="label" matInput>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Nutzungsbedingungen</mat-label>
|
<mat-label>Nutzungsbedingungen</mat-label>
|
||||||
<input formControlName="termsOfUse" matInput>
|
<input formControlName="termsOfUse" matInput>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>abweichende Quelle</mat-label>
|
<mat-label>abweichende Quelle</mat-label>
|
||||||
<input formControlName="origin" matInput>
|
<input formControlName="origin" matInput>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<app-button-row>
|
<app-button-row>
|
||||||
<button (click)="onSave()" color="primary" mat-flat-button>Speichern</button>
|
<button (click)="onSave()" mat-button>Speichern</button>
|
||||||
<button mat-stroked-button routerLink="../">Abbrechen</button>
|
|
||||||
</app-button-row>
|
</app-button-row>
|
||||||
</app-card>
|
</app-card>
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,18 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.third {
|
.fourth {
|
||||||
display: grid;
|
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;
|
column-gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export class EditSongComponent implements OnInit {
|
|||||||
public form: FormGroup;
|
public form: FormGroup;
|
||||||
public keys = KEYS;
|
public keys = KEYS;
|
||||||
public types = SongService.TYPES;
|
public types = SongService.TYPES;
|
||||||
|
public status = SongService.STATUS;
|
||||||
public legalOwner = SongService.LEGAL_OWNER;
|
public legalOwner = SongService.LEGAL_OWNER;
|
||||||
public legalType = SongService.LEGAL_TYPE;
|
public legalType = SongService.LEGAL_TYPE;
|
||||||
public flags: string[] = [];
|
public flags: string[] = [];
|
||||||
@@ -37,7 +38,7 @@ export class EditSongComponent implements OnInit {
|
|||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.activatedRoute.params.pipe(
|
this.activatedRoute.params.pipe(
|
||||||
map(param => param.songId),
|
map(param => param.songId),
|
||||||
switchMap(songId => this.songService.read(songId)),
|
switchMap(songId => this.songService.read$(songId)),
|
||||||
first()
|
first()
|
||||||
).subscribe(song => {
|
).subscribe(song => {
|
||||||
this.song = song;
|
this.song = song;
|
||||||
@@ -55,7 +56,7 @@ export class EditSongComponent implements OnInit {
|
|||||||
|
|
||||||
public removeFlag(flag: string): void {
|
public removeFlag(flag: string): void {
|
||||||
const flags = this.flags.filter(_ => _ !== flag);
|
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 {
|
public addFlag(event: MatChipInputEvent): void {
|
||||||
@@ -65,14 +66,13 @@ export class EditSongComponent implements OnInit {
|
|||||||
// Add our fruit
|
// Add our fruit
|
||||||
if ((value || '').trim()) {
|
if ((value || '').trim()) {
|
||||||
const flags = [...this.flags, 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 = '';
|
if (input) input.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
private onFlagsChanged(flagArray: string): void {
|
private onFlagsChanged(flagArray: string): void {
|
||||||
console.log(flagArray);
|
|
||||||
if (!flagArray) {
|
if (!flagArray) {
|
||||||
this.flags = [];
|
this.flags = [];
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {LegalTypeTranslatorModule} from '../../../../widget-modules/pipes/legal-
|
|||||||
import {KeyTranslatorModule} from '../../../../widget-modules/pipes/key-translator/key-translator.module';
|
import {KeyTranslatorModule} from '../../../../widget-modules/pipes/key-translator/key-translator.module';
|
||||||
import {MatChipsModule} from '@angular/material/chips';
|
import {MatChipsModule} from '@angular/material/chips';
|
||||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||||
|
import {StatusTranslaterModule} from '../../../../widget-modules/pipes/status-translater/status-translater.module';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -43,6 +44,7 @@ import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
|||||||
KeyTranslatorModule,
|
KeyTranslatorModule,
|
||||||
MatChipsModule,
|
MatChipsModule,
|
||||||
FontAwesomeModule,
|
FontAwesomeModule,
|
||||||
|
StatusTranslaterModule,
|
||||||
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export class EditService {
|
|||||||
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),
|
||||||
|
status: new FormControl(song.status ?? 'draft'),
|
||||||
|
|
||||||
legalType: new FormControl(song.legalType),
|
legalType: new FormControl(song.legalType),
|
||||||
legalLink: new FormControl(song.legalLink),
|
legalLink: new FormControl(song.legalLink),
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
<div class="split">
|
<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 class="song">
|
||||||
<div>
|
<div>
|
||||||
<div class="detail">
|
<div *appRole="['leader', 'contributor']" class="detail">
|
||||||
<div>Typ: {{song.type | songType}}</div>
|
<div>Typ: {{song.type | songType}}</div>
|
||||||
<div>Tonart: {{song.key}}</div>
|
<div>Tonart: {{song.key}}</div>
|
||||||
<div>Tempo: {{song.tempo}}</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.legalOwner">Rechteinhaber: {{song.legalOwner|legalOwner}}</div>
|
||||||
<div *ngIf="song.legalOwnerId">Rechteinhaber ID: {{song.legalOwnerId}}</div>
|
<div *ngIf="song.legalOwnerId">Rechteinhaber ID: {{song.legalOwnerId}}</div>
|
||||||
<div *ngIf="song.legalLicenseId">Lizenznummer: {{song.legalLicenseId}}</div>
|
<div *ngIf="song.legalLicenseId">Lizenznummer: {{song.legalLicenseId}}</div>
|
||||||
<div *ngIf="song.artist">Künstler: {{song.artist}}</div>
|
<div *ngIf="song.artist">Künstler: {{song.artist}}</div>
|
||||||
<div *ngIf="song.label">Verlag: {{song.label}}</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 *ngIf="song.origin">Quelle: {{song.origin}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -19,24 +21,20 @@
|
|||||||
<app-song-text *ngIf="user$|async as user" [chordMode]="user.chordMode" [showSwitch]="true"
|
<app-song-text *ngIf="user$|async as user" [chordMode]="user.chordMode" [showSwitch]="true"
|
||||||
[text]="song.text"></app-song-text>
|
[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 *ngFor="let flag of getFlags(song.flags)">{{flag}}</mat-chip>
|
||||||
</mat-chip-list>
|
</mat-chip-list>
|
||||||
|
|
||||||
<div class="text">{{song.comment}}</div>
|
<div *appRole="['leader', 'contributor']" 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 *appRole="['contributor']" mat-button routerLink="edit">Bearbeiten</button>
|
||||||
<button mat-button routerLink="../">Schließen</button>
|
|
||||||
</app-button-row>
|
</app-button-row>
|
||||||
|
|
||||||
</app-card>
|
</app-card>
|
||||||
<app-card>
|
<app-card *ngIf="!!(files$|async)" heading="Anhänge">
|
||||||
|
<div *ngFor="let file of (files$|async)">{{file.name}}</div>
|
||||||
</app-card>
|
</app-card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,10 +8,11 @@
|
|||||||
.text {
|
.text {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
font-family: 'Ubuntu Mono', monospace;
|
font-family: 'Ubuntu Mono', monospace;
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail {
|
.detail {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export class SongComponent implements OnInit {
|
|||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.song$ = this.activatedRoute.params.pipe(
|
this.song$ = this.activatedRoute.params.pipe(
|
||||||
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(
|
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 {LegalOwnerTranslatorModule} from '../../../widget-modules/pipes/legal-owner-translator/legal-owner-translator.module';
|
||||||
import {SongTextModule} from '../../../widget-modules/components/song-text/song-text.module';
|
import {SongTextModule} from '../../../widget-modules/components/song-text/song-text.module';
|
||||||
import {MatChipsModule} from '@angular/material/chips';
|
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({
|
@NgModule({
|
||||||
@@ -25,6 +27,8 @@ import {MatChipsModule} from '@angular/material/chips';
|
|||||||
LegalOwnerTranslatorModule,
|
LegalOwnerTranslatorModule,
|
||||||
SongTextModule,
|
SongTextModule,
|
||||||
MatChipsModule,
|
MatChipsModule,
|
||||||
|
RoleModule,
|
||||||
|
StatusTranslaterModule,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SongModule {
|
export class SongModule {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<app-card *ngIf="user$|async as user">
|
<app-card *ngIf="user$|async as user" heading="Hallo {{user.name}}">
|
||||||
<h2>Hallo {{user.name}}</h2>
|
|
||||||
<p>{{user.role|role}}</p>
|
<p>{{user.role|role}}</p>
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class RolePipe implements PipeTransform {
|
|||||||
|
|
||||||
transform(role: roles): string {
|
transform(role: roles): string {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case 'distributor':
|
case 'contributor':
|
||||||
return 'Mitarbeiter';
|
return 'Mitarbeiter';
|
||||||
case 'none':
|
case 'none':
|
||||||
return 'keine Berechtigung';
|
return 'keine Berechtigung';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, Input} from '@angular/core';
|
||||||
import {User} from '../../../../../services/user/user';
|
import {User} from '../../../../../services/user/user';
|
||||||
import {UserService} from '../../../../../services/user/user.service';
|
import {UserService} from '../../../../../services/user/user.service';
|
||||||
import {ROLE_TYPES} from '../../../../../services/user/roles';
|
import {ROLE_TYPES} from '../../../../../services/user/roles';
|
||||||
@@ -8,7 +8,7 @@ import {ROLE_TYPES} from '../../../../../services/user/roles';
|
|||||||
templateUrl: './user.component.html',
|
templateUrl: './user.component.html',
|
||||||
styleUrls: ['./user.component.less']
|
styleUrls: ['./user.component.less']
|
||||||
})
|
})
|
||||||
export class UserComponent implements OnInit {
|
export class UserComponent {
|
||||||
public id: string;
|
public id: string;
|
||||||
public name: string
|
public name: string
|
||||||
public roles: string[];
|
public roles: string[];
|
||||||
@@ -23,10 +23,6 @@ export class UserComponent implements OnInit {
|
|||||||
this.roles = this.getRoleArray(value.role);
|
this.roles = this.getRoleArray(value.role);
|
||||||
};
|
};
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async onRoleChanged(id: string, roles: string[]): Promise<void> {
|
public async onRoleChanged(id: string, roles: string[]): Promise<void> {
|
||||||
const role = roles.join(';');
|
const role = roles.join(';');
|
||||||
await this.userService.update$(id, {role});
|
await this.userService.update$(id, {role});
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<app-button-row>
|
<app-button-row>
|
||||||
<button (click)="onLogin()" mat-button>Anmelden</button>
|
<button (click)="onLogin()" mat-button>Anmelden</button>
|
||||||
<button mat-button routerLink="/user/password">neues Passwort anfordern</button>
|
<button mat-button routerLink="/user/password">Passwort zurücksetzen</button>
|
||||||
|
<button mat-button routerLink="/user/new">neuen Benutzer anlegen</button>
|
||||||
<p *ngIf="errorMessage" class="error">{{errorMessage|authMessage}}</p>
|
<p *ngIf="errorMessage" class="error">{{errorMessage|authMessage}}</p>
|
||||||
</app-button-row>
|
</app-button-row>
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<p>logout works!</p>
|
|
||||||
|
|||||||
18
src/app/modules/user/new/new.component.html
Normal file
18
src/app/modules/user/new/new.component.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<app-card [formGroup]="form" closeLink="../" heading="neuen Benutzer anlegen">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Name</mat-label>
|
||||||
|
<input formControlName="name" matInput>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>E-Mail Adresse</mat-label>
|
||||||
|
<input formControlName="email" matInput>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Passwort</mat-label>
|
||||||
|
<input formControlName="password" matInput>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<app-button-row>
|
||||||
|
<button (click)="onCreate()" mat-button>Benutzer anlegen</button>
|
||||||
|
</app-button-row>
|
||||||
|
</app-card>
|
||||||
5
src/app/modules/user/new/new.component.less
Normal file
5
src/app/modules/user/new/new.component.less
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.users {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-gap: 20px;
|
||||||
|
}
|
||||||
25
src/app/modules/user/new/new.component.spec.ts
Normal file
25
src/app/modules/user/new/new.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {NewComponent} from './new.component';
|
||||||
|
|
||||||
|
describe('NewComponent', () => {
|
||||||
|
let component: NewComponent;
|
||||||
|
let fixture: ComponentFixture<NewComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [NewComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(NewComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
35
src/app/modules/user/new/new.component.ts
Normal file
35
src/app/modules/user/new/new.component.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms';
|
||||||
|
import {UserService} from '../../../services/user/user.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-new',
|
||||||
|
templateUrl: './new.component.html',
|
||||||
|
styleUrls: ['./new.component.less']
|
||||||
|
})
|
||||||
|
export class NewComponent implements OnInit {
|
||||||
|
public form: FormGroup;
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder, private userService: UserService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.form = this.fb.group({
|
||||||
|
email: new FormControl(null, [Validators.required, Validators.email]),
|
||||||
|
name: new FormControl(null, [Validators.required]),
|
||||||
|
password: new FormControl(null, [Validators.required, Validators.minLength(6)]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onCreate(): Promise<void> {
|
||||||
|
this.form.updateValueAndValidity();
|
||||||
|
console.log(this.form);
|
||||||
|
if (this.form.valid) {
|
||||||
|
try {
|
||||||
|
await this.userService.createNewUser(this.form.value.email, this.form.value.name, this.form.value.password);
|
||||||
|
} catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<app-card>
|
<app-card closeLink="../" heading="Passwort zurücksetzen">
|
||||||
<div [formGroup]="form" class="form">
|
<div [formGroup]="form" class="form">
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>E-Mail Addresse</mat-label>
|
<mat-label>E-Mail Addresse</mat-label>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {LogoutComponent} from './logout/logout.component';
|
|||||||
import {AngularFireAuthGuard, redirectUnauthorizedTo} from '@angular/fire/auth-guard';
|
import {AngularFireAuthGuard, redirectUnauthorizedTo} from '@angular/fire/auth-guard';
|
||||||
import {PasswordComponent} from './password/password.component';
|
import {PasswordComponent} from './password/password.component';
|
||||||
import {PasswordSendComponent} from './password-send/password-send.component';
|
import {PasswordSendComponent} from './password-send/password-send.component';
|
||||||
|
import {NewComponent} from './new/new.component';
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
@@ -26,6 +27,10 @@ const routes: Routes = [
|
|||||||
path: 'password',
|
path: 'password',
|
||||||
component: PasswordComponent
|
component: PasswordComponent
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'new',
|
||||||
|
component: NewComponent
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'password-send',
|
path: 'password-send',
|
||||||
component: PasswordSendComponent
|
component: PasswordSendComponent
|
||||||
|
|||||||
@@ -18,10 +18,11 @@ import {PasswordSendComponent} from './password-send/password-send.component';
|
|||||||
import {UsersComponent} from './info/users/users.component';
|
import {UsersComponent} from './info/users/users.component';
|
||||||
import {RoleModule} from '../../services/user/role.module';
|
import {RoleModule} from '../../services/user/role.module';
|
||||||
import {UserComponent} from './info/users/user/user.component';
|
import {UserComponent} from './info/users/user/user.component';
|
||||||
|
import {NewComponent} from './new/new.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [LoginComponent, AuthMessagePipe, InfoComponent, LogoutComponent, RolePipe, PasswordComponent, PasswordSendComponent, UsersComponent, UserComponent],
|
declarations: [LoginComponent, AuthMessagePipe, InfoComponent, LogoutComponent, RolePipe, PasswordComponent, PasswordSendComponent, UsersComponent, UserComponent, NewComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
UserRoutingModule,
|
UserRoutingModule,
|
||||||
|
|||||||
1
src/app/services/delay.ts
Normal file
1
src/app/services/delay.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const delay = (ms: number): Promise<any> => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import {RoleDirective} from './role.directive';
|
|
||||||
|
|
||||||
describe('RoleDirective', () => {
|
|
||||||
it('should create an instance', () => {
|
|
||||||
const directive = new RoleDirective();
|
|
||||||
expect(directive).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -33,10 +33,9 @@ export class RoleDirective implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateView() {
|
private updateView() {
|
||||||
|
this.viewContainer.clear();
|
||||||
if (this.loggedIn && this.checkPermission()) {
|
if (this.loggedIn && this.checkPermission()) {
|
||||||
this.viewContainer.createEmbeddedView(this.templateRef);
|
this.viewContainer.createEmbeddedView(this.templateRef);
|
||||||
} else {
|
|
||||||
this.viewContainer.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export type roles = 'none' | 'admin' | 'user' | 'leader' | 'presenter' | 'distributor';
|
export type roles = 'none' | 'admin' | 'user' | 'leader' | 'presenter' | 'contributor';
|
||||||
export const ROLE_TYPES: roles[] = ['admin', 'user', 'leader', 'presenter', 'distributor'];
|
export const ROLE_TYPES: roles[] = ['admin', 'user', 'leader', 'presenter', 'contributor'];
|
||||||
|
|||||||
@@ -1,21 +1,33 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {AngularFireAuth} from '@angular/fire/auth';
|
import {AngularFireAuth} from '@angular/fire/auth';
|
||||||
import {BehaviorSubject, Observable} from 'rxjs';
|
import {BehaviorSubject, Observable} from 'rxjs';
|
||||||
import {filter, switchMap} from 'rxjs/operators';
|
import {filter, first, switchMap} from 'rxjs/operators';
|
||||||
import {User} from './user';
|
import {User} from './user';
|
||||||
import {DbService} from '../db.service';
|
import {DbService} from '../db.service';
|
||||||
|
import {environment} from '../../../environments/environment';
|
||||||
|
import {Router} from '@angular/router';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class UserService {
|
export class UserService {
|
||||||
constructor(private afAuth: AngularFireAuth, private db: DbService) {
|
constructor(private afAuth: AngularFireAuth, private db: DbService, private router: Router) {
|
||||||
this.afAuth.authState.pipe(
|
this.afAuth.authState.pipe(
|
||||||
filter(_ => !!_),
|
filter(_ => !!_),
|
||||||
switchMap(auth => this.db.doc$<User>('users/' + auth.uid)),
|
switchMap(auth => this.readUser$(auth.uid)),
|
||||||
).subscribe(_ => this._user$.next(_));
|
).subscribe(_ => this._user$.next(_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getUserbyId(userId: string): Promise<User> {
|
||||||
|
return this.db.doc$<User>('users/' + userId).pipe(first()).toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async login(user: string, password: string): Promise<any> {
|
||||||
|
const aUser = await this.afAuth.auth.signInWithEmailAndPassword(user, password);
|
||||||
|
const dUser = await this.readUser(aUser.user.uid);
|
||||||
|
this._user$.next(dUser);
|
||||||
|
}
|
||||||
|
|
||||||
private _user$ = new BehaviorSubject<User>(null);
|
private _user$ = new BehaviorSubject<User>(null);
|
||||||
|
|
||||||
public get user$(): Observable<User> {
|
public get user$(): Observable<User> {
|
||||||
@@ -26,23 +38,32 @@ export class UserService {
|
|||||||
|
|
||||||
public list$ = (): Observable<User[]> => this.db.col$('users');
|
public list$ = (): Observable<User[]> => this.db.col$('users');
|
||||||
|
|
||||||
public getUserbyId$(userId: string): Observable<User> {
|
public async logout(): Promise<any> {
|
||||||
return this.db.doc$<User>('users/' + userId);
|
await this.afAuth.auth.signOut();
|
||||||
|
this._user$.next(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async update$(uid: string, data: Partial<User>): Promise<void> {
|
public async update$(uid: string, data: Partial<User>): Promise<void> {
|
||||||
await this.db.doc<User>('users/' + uid).update(data);
|
await this.db.doc<User>('users/' + uid).update(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async login(user: string, password: string): Promise<any> {
|
public async changePassword(user: string): Promise<any> {
|
||||||
await this.afAuth.auth.signInWithEmailAndPassword(user, password);
|
const url = environment.url;
|
||||||
|
await this.afAuth.auth.sendPasswordResetEmail(user, {url});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async logout(): Promise<any> {
|
public async createNewUser(user: string, name: string, password: string): Promise<any> {
|
||||||
await this.afAuth.auth.signOut();
|
const aUser = await this.afAuth.auth.createUserWithEmailAndPassword(user, password);
|
||||||
|
const userId = aUser.user.uid;
|
||||||
|
await this.db.doc('users/' + userId).set({name, chordMode: 'onlyFirst'});
|
||||||
|
const dUser = await this.readUser(aUser.user.uid);
|
||||||
|
this._user$.next(dUser);
|
||||||
|
await this.router.navigateByUrl('/user/info');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async changePassword(email: string): Promise<any> {
|
private readUser$ = (uid) => this.db.doc$<User>('users/' + uid);
|
||||||
await this.afAuth.auth.sendPasswordResetEmail(email);
|
|
||||||
|
private async readUser(uid): Promise<User> {
|
||||||
|
return await this.readUser$(uid).pipe(first()).toPromise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import {ChordMode} from '../../widget-modules/components/song-text/song-text.component';
|
import {ChordMode} from '../../widget-modules/components/song-text/song-text.component';
|
||||||
|
import {roles} from './roles';
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
role: string;
|
role: roles;
|
||||||
chordMode: ChordMode
|
chordMode: ChordMode
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<div [class.padding]="padding" class="card">
|
<div [class.padding]="padding" class="card">
|
||||||
|
<button *ngIf="closeLink" [routerLink]="closeLink" class="btn-close" mat-icon-button>
|
||||||
|
<fa-icon [icon]="faClose"></fa-icon>
|
||||||
|
</button>
|
||||||
<div *ngIf="heading" class="heading">{{heading}}</div>
|
<div *ngIf="heading" class="heading">{{heading}}</div>
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
background: #fffb;
|
background: #fffb;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 800px;
|
width: 800px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
@media screen and (max-width: 860px) {
|
@media screen and (max-width: 860px) {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
@@ -25,4 +26,14 @@
|
|||||||
.heading {
|
.heading {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-right: 20px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 15px;
|
||||||
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {faTimes} from '@fortawesome/free-solid-svg-icons/faTimes';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-card',
|
selector: 'app-card',
|
||||||
templateUrl: './card.component.html',
|
templateUrl: './card.component.html',
|
||||||
styleUrls: ['./card.component.less']
|
styleUrls: ['./card.component.less']
|
||||||
})
|
})
|
||||||
export class CardComponent implements OnInit {
|
export class CardComponent {
|
||||||
@Input() padding = true;
|
@Input() padding = true;
|
||||||
@Input() heading: string;
|
@Input() heading: string;
|
||||||
|
@Input() closeLink: string;
|
||||||
|
|
||||||
constructor() {
|
public faClose = faTimes;
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {CardComponent} from './card.component';
|
import {CardComponent} from './card.component';
|
||||||
|
import {MatButtonModule} from '@angular/material/button';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [CardComponent],
|
declarations: [CardComponent],
|
||||||
exports: [CardComponent],
|
exports: [CardComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule
|
CommonModule,
|
||||||
|
MatButtonModule,
|
||||||
|
RouterModule,
|
||||||
|
FontAwesomeModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CardModule {
|
export class CardModule {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<div (click)="onClick()" *ngIf="sections" [@songSwitch]="sections" [class.chords]="_chordMode!=='hide'"
|
<div (click)="onClick()" *ngIf="sections" [@songSwitch]="sections" [class.chords]="_chordMode!=='hide'"
|
||||||
class="song-text">
|
class="song-text">
|
||||||
|
|
||||||
<div (click)="onChordClick()" *ngIf="showSwitch" class="menu">
|
<button (click)="onChordClick()" *ngIf="showSwitch" class="menu" mat-icon-button>
|
||||||
<fa-icon [icon]="faLines"></fa-icon>
|
<fa-icon [icon]="faLines"></fa-icon>
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
|
|
||||||
<div [class.offset]="fullscreen" [style.top.px]="offset + 50">
|
<div [class.offset]="fullscreen" [style.top.px]="offset + 50">
|
||||||
<div #section *ngFor="let section of sections; let i = index" [class.chorus]="section.type===1" class="section">
|
<div #section *ngFor="let section of sections; let i = index" [class.chorus]="section.type===1" class="section">
|
||||||
|
|||||||
@@ -36,27 +36,8 @@
|
|||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: -10px;
|
||||||
background: #eee;
|
top: -10px;
|
||||||
border-radius: 50%;
|
|
||||||
padding: 5px;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
transition: 300ms all ease-in-out;
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.1;
|
|
||||||
|
|
||||||
@media screen and (max-width: 860px) {
|
|
||||||
background: #0001;
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #ddd;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {NgModule} from '@angular/core';
|
|||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {SongTextComponent} from './song-text.component';
|
import {SongTextComponent} from './song-text.component';
|
||||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||||
|
import {MatButtonModule} from '@angular/material/button';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -9,7 +10,8 @@ import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
|||||||
exports: [SongTextComponent],
|
exports: [SongTextComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FontAwesomeModule
|
FontAwesomeModule,
|
||||||
|
MatButtonModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SongTextModule {
|
export class SongTextModule {
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {StatusPipe} from './status.pipe';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [StatusPipe],
|
||||||
|
exports: [
|
||||||
|
StatusPipe
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class StatusTranslaterModule {
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import {firebase} from './firebase';
|
|||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
|
url: 'https://worshipgenerator.web.app',
|
||||||
firebase
|
firebase
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -87,3 +87,16 @@ perfect-scrollbar.scroll > .ps .ps__rail-y:hover {
|
|||||||
.mat-chip.mat-standard-chip {
|
.mat-chip.mat-standard-chip {
|
||||||
background: #fffa;
|
background: #fffa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:-webkit-autofill,
|
||||||
|
input:-webkit-autofill:hover,
|
||||||
|
input:-webkit-autofill:focus,
|
||||||
|
textarea:-webkit-autofill,
|
||||||
|
textarea:-webkit-autofill:hover,
|
||||||
|
textarea:-webkit-autofill:focus,
|
||||||
|
select:-webkit-autofill,
|
||||||
|
select:-webkit-autofill:hover,
|
||||||
|
select:-webkit-autofill:focus {
|
||||||
|
-webkit-box-shadow: 0 0 0px 1000px #0000 inset;
|
||||||
|
transition: background-color 5000s ease-in-out 0s;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user