routing
This commit is contained in:
@@ -1,21 +1,21 @@
|
||||
import {SongsComponent} from './components/songs/songs.component';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'songs',
|
||||
component: SongsComponent
|
||||
},
|
||||
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'songs',
|
||||
pathMatch: 'full'
|
||||
pathMatch: 'full',
|
||||
redirectTo: 'songs'
|
||||
},
|
||||
{
|
||||
path: 'songs',
|
||||
loadChildren: () => import('./songs/songs.module').then(_ => _.SongsModule)
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
imports: [RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload'})],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
:root {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@@ -18,28 +18,11 @@ import {MatRadioModule} from '@angular/material/radio';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import {MatProgressBarModule} from '@angular/material/progress-bar';
|
||||
|
||||
import {TableComponent} from './components/songs/table/table.component';
|
||||
import {SongsComponent} from './components/songs/songs.component';
|
||||
import {SongComponent} from './components/songs/song/song.component';
|
||||
import {SongEditComponent} from './components/songs/song-edit/song-edit.component';
|
||||
import {SongNewComponent} from './components/songs/song-new/song-new.component';
|
||||
import {SongFormComponent} from './components/songs/song-form/song-form.component';
|
||||
import {SongFilesComponent} from './components/songs/song-files/song-files.component';
|
||||
import {FileUploadModule} from 'ng2-file-upload';
|
||||
import {SongFileEditComponent} from './components/songs/song-file-edit/song-file-edit.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
SongsComponent,
|
||||
TableComponent,
|
||||
SongComponent,
|
||||
SongEditComponent,
|
||||
SongNewComponent,
|
||||
SongFormComponent,
|
||||
SongFilesComponent,
|
||||
SongFileEditComponent
|
||||
AppComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
import {ChangeDetectorRef, Component} from '@angular/core';
|
||||
import {Song} from 'src/app/models/song.model';
|
||||
import {SongsService} from 'src/app/data/songs.service';
|
||||
import {DownloadService} from 'src/app/data/download.service';
|
||||
import {faDownload, faEdit, faFileUpload, faLongArrowAltLeft, faTrash} from '@fortawesome/free-solid-svg-icons';
|
||||
import {FileuploadFactory} from 'src/app/services/fileupload.factory';
|
||||
import {FileUploader} from 'ng2-file-upload';
|
||||
|
||||
@Component({
|
||||
selector: 'app-song-files',
|
||||
templateUrl: './song-files.component.html',
|
||||
styleUrls: ['./song-files.component.less']
|
||||
})
|
||||
export class SongFilesComponent {
|
||||
public song: Song;
|
||||
public selectedSongId = 0;
|
||||
public faFileUpload = faFileUpload;
|
||||
public faTrash = faTrash;
|
||||
public faArrow = faLongArrowAltLeft;
|
||||
public faDownload = faDownload;
|
||||
public faEdit = faEdit;
|
||||
public columns = ['name', 'action'];
|
||||
public newFileUploader: FileUploader;
|
||||
public fileEditId: number;
|
||||
|
||||
public fileOverNew = false;
|
||||
|
||||
constructor(
|
||||
private downloadService: DownloadService,
|
||||
private fileuploadFactory: FileuploadFactory,
|
||||
private songService: SongsService,
|
||||
change: ChangeDetectorRef
|
||||
) {
|
||||
songService.selectedSong.subscribe(_ => {
|
||||
if (_) {
|
||||
this.selectedSongId = _.ID;
|
||||
this.song = _;
|
||||
this.newFileUploader = FileuploadFactory.provideForNewFiles(_.ID);
|
||||
this.newFileUploader.onCompleteItem = () =>
|
||||
songService.selectSong(_.ID).subscribe();
|
||||
this.newFileUploader.onProgressItem = () => change.markForCheck;
|
||||
} else {
|
||||
this.selectedSongId = 0;
|
||||
this.song = null;
|
||||
this.newFileUploader = null;
|
||||
}
|
||||
change.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
public onClickDownload(fileId: number, filename): void {
|
||||
this.downloadService.get(this.selectedSongId, fileId, filename);
|
||||
}
|
||||
|
||||
public onFileOverNew(hover: boolean): void {
|
||||
this.fileOverNew = hover;
|
||||
}
|
||||
|
||||
public onClickEdit(fileId: number): void {
|
||||
this.fileEditId = fileId;
|
||||
}
|
||||
|
||||
public onClickDelete(fileId: number): void {
|
||||
const songId = this.song.ID;
|
||||
this.songService
|
||||
.deleteFile$(songId, fileId)
|
||||
.pipe(switchMap(() => this.songService.selectSong(songId)))
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import {SongsService} from 'src/app/data/songs.service';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component} from '@angular/core';
|
||||
import {faEdit, faLongArrowAltLeft} from '@fortawesome/free-solid-svg-icons';
|
||||
import {Song} from 'src/app/models/song.model';
|
||||
import {State} from 'src/app/data/state';
|
||||
|
||||
@Component({
|
||||
selector: 'app-song',
|
||||
templateUrl: './song.component.html',
|
||||
styleUrls: ['./song.component.less'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SongComponent {
|
||||
public song: Song;
|
||||
public faArrow = faLongArrowAltLeft;
|
||||
public faEdit = faEdit;
|
||||
public selectedSongId = 0;
|
||||
|
||||
constructor(
|
||||
private songService: SongsService,
|
||||
change: ChangeDetectorRef
|
||||
) {
|
||||
songService.selectedSong.subscribe(_ => {
|
||||
if (_) {
|
||||
this.selectedSongId = _.ID;
|
||||
this.song = _;
|
||||
} else {
|
||||
this.selectedSongId = 0;
|
||||
this.song = null;
|
||||
}
|
||||
change.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
public get text(): string[] {
|
||||
return this.song && this.song.Text ? this.song.Text.split(/\r?\n/) : [];
|
||||
}
|
||||
|
||||
public get comments(): string[] {
|
||||
return this.song && this.song.Comments ? this.song.Comments.split(/\r?\n/) : [];
|
||||
}
|
||||
|
||||
public onBack(): void {
|
||||
this.songService.resetSelectedSong();
|
||||
}
|
||||
|
||||
public onClickEdit(): void {
|
||||
this.songService.state.next(State.edit);
|
||||
}
|
||||
|
||||
public renderSongType(songType: string) {
|
||||
switch (songType) {
|
||||
case 'Praise':
|
||||
return {name: 'Lobpreis', color: '#99FFB8'};
|
||||
case 'Worship':
|
||||
return {name: 'Anbetung', color: '#C999FF'};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<div class="songs-container">
|
||||
<app-table></app-table>
|
||||
<div class="song-container">
|
||||
<app-song-edit *ngIf="(songsService.state | async) === State.edit"></app-song-edit>
|
||||
<app-song-new *ngIf="(songsService.state | async) === State.new"></app-song-new>
|
||||
<app-song *ngIf="(songsService.state | async) === State.read"></app-song>
|
||||
<app-song-files *ngIf="(songsService.state | async) === State.read"></app-song-files>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,7 +3,7 @@ import {Observable} from 'rxjs';
|
||||
import {map, tap} from 'rxjs/operators';
|
||||
import {base} from './urls';
|
||||
|
||||
export class OdataService {
|
||||
export class ODataBaseService {
|
||||
private url: string;
|
||||
|
||||
constructor(private odataService: ODataService, private entity: string) {
|
||||
@@ -21,14 +21,14 @@ export class OdataService {
|
||||
|
||||
public get$<TResponse>(
|
||||
id: number,
|
||||
properties: string[],
|
||||
select: string[],
|
||||
expands: string[]
|
||||
): Observable<TResponse> {
|
||||
const query = new ODataQuery(this.odataService, this.url)
|
||||
.entitySet(this.entity)
|
||||
.entityKey(id)
|
||||
.expand(expands.map(_ => new Expand(_)))
|
||||
.select(properties);
|
||||
.select(select);
|
||||
const get = query.get().pipe(map(_ => _.toEntity<TResponse>()));
|
||||
|
||||
return get;
|
||||
@@ -2,19 +2,21 @@ import {SongsService} from 'src/app/data/songs.service';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {FormControl, FormGroup, Validators} from '@angular/forms';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
import {Song} from '../models/song.model';
|
||||
import {Song} from '../songs/models/song.model';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {File} from '../songs/models/file.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class EditSongService {
|
||||
|
||||
constructor(private songsService: SongsService) {
|
||||
}
|
||||
|
||||
public initSongEditForm(attachSync: boolean): FormGroup {
|
||||
public initSongEditForm(attachSync: boolean, _song: Song): FormGroup {
|
||||
const song = attachSync
|
||||
? this.songsService.selectedSong.value
|
||||
? _song
|
||||
: this.defaultValues();
|
||||
const form = new FormGroup({
|
||||
Number: new FormControl(song.Number, {
|
||||
@@ -45,10 +47,7 @@ export class EditSongService {
|
||||
return form;
|
||||
}
|
||||
|
||||
public initFileEditForm(songId: number, fileId: number): { form: FormGroup, changeSubscription: Subscription } {
|
||||
const file = this.songsService.selectedSong.value.Files.filter(
|
||||
_ => _.ID === fileId
|
||||
)[0];
|
||||
public initFileEditForm(songId: number, file: File): { form: FormGroup, changeSubscription: Subscription } {
|
||||
const form = new FormGroup({
|
||||
Name: new FormControl(file.Name, {
|
||||
updateOn: 'blur',
|
||||
@@ -60,8 +59,7 @@ export class EditSongService {
|
||||
});
|
||||
|
||||
const changeSubscription = form.valueChanges.pipe(
|
||||
switchMap(_ => this.songsService.updateFile$(songId, fileId, _.Name, _.FileType)),
|
||||
switchMap(() => this.songsService.selectSong(songId))
|
||||
switchMap(_ => this.songsService.updateFile$(songId, file.ID, _.Name, _.FileType)),
|
||||
).subscribe();
|
||||
|
||||
return {form: form, changeSubscription: changeSubscription};
|
||||
@@ -73,7 +71,6 @@ export class EditSongService {
|
||||
form.controls[control].valueChanges
|
||||
.pipe(
|
||||
switchMap(value => this.songsService.patch$(song.ID, control, value)),
|
||||
switchMap(() => this.songsService.selectSong(song.ID))
|
||||
)
|
||||
.subscribe();
|
||||
});
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {FileType} from '../models/files-types.model.ts';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {ODataService} from 'odata-v4-ng';
|
||||
import {OdataService} from './odata.service';
|
||||
import {Song} from '../models/song.model';
|
||||
import {ODataBaseService} from './ODataBaseService';
|
||||
import {Song} from '../songs/models/song.model';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {switchMap, tap} from 'rxjs/operators';
|
||||
import {tap} from 'rxjs/operators';
|
||||
import {State} from './state';
|
||||
import {base} from './urls';
|
||||
import {FileType} from '../songs/models/files-types.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SongsService extends OdataService {
|
||||
export class SongsService extends ODataBaseService {
|
||||
public state = new BehaviorSubject<State>(State.list);
|
||||
|
||||
public songs: BehaviorSubject<Song[]> = new BehaviorSubject<Song[]>([]);
|
||||
public selectedSong: BehaviorSubject<Song> = new BehaviorSubject<Song>(null);
|
||||
|
||||
constructor(odataService: ODataService, private httpClient: HttpClient) {
|
||||
super(odataService, 'songs');
|
||||
@@ -30,40 +29,6 @@ export class SongsService extends OdataService {
|
||||
return list;
|
||||
}
|
||||
|
||||
public loadSongListAndGoTo$(id: number): Observable<Song> {
|
||||
const properties = ['ID', 'Name', 'Number', 'SongType', 'Key', 'Tempo'];
|
||||
const list = this.list$<Song>(properties).pipe(
|
||||
tap(_ => {
|
||||
this.songs.next(_);
|
||||
}),
|
||||
switchMap(() => this.selectSong(id))
|
||||
);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public selectSong(id: number): Observable<Song> {
|
||||
this.state.next(State.read);
|
||||
const filter = this.songs.value.filter(_ => _.ID === id);
|
||||
const song = filter.length === 1 ? filter[0] : null;
|
||||
if (!song) {
|
||||
return;
|
||||
}
|
||||
|
||||
const get = this.get$<Song>(id, ['Text', 'Comments'], ['Files']).pipe(tap(_ => {
|
||||
song.Text = _.Text;
|
||||
song.Comments = _.Comments;
|
||||
song.Files = _.Files;
|
||||
this.selectedSong.next(song);
|
||||
}));
|
||||
|
||||
return get;
|
||||
}
|
||||
|
||||
public resetSelectedSong() {
|
||||
this.state.next(State.list);
|
||||
this.selectedSong.next(null);
|
||||
}
|
||||
|
||||
public patch$(id: number, control: string, value: any): Observable<boolean> {
|
||||
const patch = super.patch$(id, control, value).pipe(
|
||||
@@ -72,7 +37,6 @@ export class SongsService extends OdataService {
|
||||
const song = songs.filter(_ => _.ID === id)[0];
|
||||
song[control] = value;
|
||||
this.songs.next(songs);
|
||||
this.selectedSong.next(song);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -80,9 +44,7 @@ export class SongsService extends OdataService {
|
||||
}
|
||||
|
||||
public saveNewSong$(values: any): Observable<Song> {
|
||||
const newSong = super
|
||||
.post$<Song>(values)
|
||||
.pipe(switchMap(_ => this.loadSongListAndGoTo$(_.ID)));
|
||||
const newSong = super.post$<Song>(values);
|
||||
|
||||
return newSong;
|
||||
}
|
||||
@@ -93,16 +55,7 @@ export class SongsService extends OdataService {
|
||||
name: string,
|
||||
fileType: FileType
|
||||
): Observable<any> {
|
||||
const url =
|
||||
base +
|
||||
'/api/songs/' +
|
||||
songId +
|
||||
'/files/' +
|
||||
fileId +
|
||||
'/edit?Name=' +
|
||||
name +
|
||||
'&FileType=' +
|
||||
fileType;
|
||||
const url = `${base}/api/songs/${songId}/files/${fileId}/edit?Name=${name}&FileType=${fileType}`;
|
||||
const get = this.httpClient.get(url);
|
||||
return get;
|
||||
}
|
||||
@@ -111,13 +64,7 @@ export class SongsService extends OdataService {
|
||||
songId: number,
|
||||
fileId: number
|
||||
): Observable<any> {
|
||||
const url =
|
||||
base +
|
||||
'/api/songs/' +
|
||||
songId +
|
||||
'/files/' +
|
||||
fileId +
|
||||
'/delete';
|
||||
const url = `${base}/api/songs/${songId}/files/${fileId}/delete`;
|
||||
const get = this.httpClient.get(url);
|
||||
return get;
|
||||
}
|
||||
|
||||
6
WEB/src/app/songs/components/songs/songs.component.html
Normal file
6
WEB/src/app/songs/components/songs/songs.component.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<div class="songs-container">
|
||||
<app-table></app-table>
|
||||
<div class="song-container">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,6 +3,5 @@
|
||||
}
|
||||
|
||||
.song-container {
|
||||
padding: 10px;
|
||||
flex-flow: column;
|
||||
}
|
||||
@@ -4,11 +4,11 @@
|
||||
>
|
||||
<div class="table-container">
|
||||
<button
|
||||
(click)="onClickNew()"
|
||||
class="button-new"
|
||||
mat-icon-button
|
||||
matTooltip="neuen Titel anlegen"
|
||||
matTooltipPosition="left"
|
||||
[routerLink]="'/songs/new'"
|
||||
>
|
||||
<fa-icon [icon]="faNew"></fa-icon>
|
||||
</button>
|
||||
@@ -65,10 +65,10 @@
|
||||
|
||||
<tr *matHeaderRowDef="columns | async; sticky: true" mat-header-row></tr>
|
||||
<tr
|
||||
(click)="onClick(row.ID)"
|
||||
*matRowDef="let row; columns: columns | async"
|
||||
[class.selected]="selectedSongId === row.ID"
|
||||
[routerLink]="'/songs/' + row.ID"
|
||||
mat-row
|
||||
routerLinkActive="selected"
|
||||
></tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1,10 +1,26 @@
|
||||
table {
|
||||
border-radius: 8px;
|
||||
background: #fffe;
|
||||
|
||||
tr.selected {
|
||||
background-color: #0002;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
cursor: pointer;
|
||||
background-color: #0001;
|
||||
|
||||
td {
|
||||
color: #ff9900;
|
||||
}
|
||||
}
|
||||
|
||||
td.mat-cell {
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
//height: 100%;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
@@ -23,3 +39,4 @@ table {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {SongsService} from '../../../data/songs.service';
|
||||
import {SongsService} from '../../../../data/songs.service';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component} from '@angular/core';
|
||||
import {State} from 'src/app/data/state';
|
||||
import {faFileMedical} from '@fortawesome/free-solid-svg-icons';
|
||||
@@ -12,7 +12,6 @@ import {map} from 'rxjs/operators';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class TableComponent {
|
||||
public selectedSongId = 0;
|
||||
public State = State;
|
||||
public faNew = faFileMedical;
|
||||
public columnsFull = ['Number', 'Name', 'Key', 'SongType', 'Tempo'];
|
||||
@@ -22,11 +21,6 @@ export class TableComponent {
|
||||
public songsService: SongsService,
|
||||
private change: ChangeDetectorRef
|
||||
) {
|
||||
songsService.selectedSong.subscribe(_ => {
|
||||
this.selectedSongId = _ ? _.ID : 0;
|
||||
this.change.markForCheck();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public get columns(): Observable<string[]> {
|
||||
@@ -43,16 +37,4 @@ export class TableComponent {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public onClick(id: number): void {
|
||||
this.songsService.selectSong(id).subscribe();
|
||||
this.change.detectChanges();
|
||||
}
|
||||
|
||||
public onClickNew(): void {
|
||||
this.songsService.selectSong(null).subscribe();
|
||||
this.songsService.state.next(State.new);
|
||||
this.change.detectChanges();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {FileType} from './files-types.model.ts';
|
||||
import {FileType} from './files-types.model';
|
||||
|
||||
export interface File {
|
||||
ID: number;
|
||||
@@ -2,7 +2,7 @@
|
||||
<mat-card *ngIf="form" class="mat-elevation-z8">
|
||||
<mat-card-header>
|
||||
<div mat-card-avatar>
|
||||
<button (click)="onBack()" color="warn" mat-icon-button>
|
||||
<button [routerLink]="'/songs/'+ songId +'/read'" color="warn" mat-icon-button>
|
||||
<fa-icon [icon]="faArrow"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
@@ -14,11 +14,5 @@
|
||||
<mat-card-content>
|
||||
<app-song-form [form]="form"></app-song-form>
|
||||
</mat-card-content>
|
||||
<!-- <mat-card-actions>
|
||||
<button mat-button (click)="onClickDownload()">Herunterladen</button>
|
||||
<button mat-button (click)="onClickEdit()">
|
||||
<fa-icon [icon]="faEdit"></fa-icon> Bearbeiten
|
||||
</button>
|
||||
</mat-card-actions> -->
|
||||
</mat-card>
|
||||
</div>
|
||||
@@ -1,9 +1,9 @@
|
||||
import {SongsService} from 'src/app/data/songs.service';
|
||||
import {FormGroup} from '@angular/forms';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
|
||||
import {EditSongService} from 'src/app/data/edit-song.service';
|
||||
import {faLongArrowAltLeft} from '@fortawesome/free-solid-svg-icons';
|
||||
import {State} from 'src/app/data/state';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Song} from '../../../models/song.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-song-edit',
|
||||
@@ -14,21 +14,16 @@ import {State} from 'src/app/data/state';
|
||||
export class SongEditComponent implements OnInit {
|
||||
public form: FormGroup = null;
|
||||
public faArrow = faLongArrowAltLeft;
|
||||
|
||||
public songId: number;
|
||||
|
||||
constructor(
|
||||
private editSongService: EditSongService,
|
||||
private songsService: SongsService,
|
||||
private change: ChangeDetectorRef
|
||||
) {
|
||||
private editSongService: EditSongService, private route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.form = this.editSongService.initSongEditForm(true);
|
||||
this.change.markForCheck();
|
||||
}
|
||||
|
||||
public onBack(): void {
|
||||
this.songsService.state.next(State.read);
|
||||
this.route.data.subscribe((data: { song: Song }) => {
|
||||
this.songId = data.song.ID;
|
||||
this.form = this.editSongService.initSongEditForm(true, data.song);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import {EditSongService} from '../../../data/edit-song.service';
|
||||
import {EditSongService} from '../../../../data/edit-song.service';
|
||||
import {FormGroup} from '@angular/forms';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
|
||||
import {faLongArrowAltLeft, faSave} from '@fortawesome/free-solid-svg-icons';
|
||||
import {State} from 'src/app/data/state';
|
||||
import {SongsService} from 'src/app/data/songs.service';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-song-new',
|
||||
@@ -19,22 +19,18 @@ export class SongNewComponent implements OnInit {
|
||||
constructor(
|
||||
private editSongService: EditSongService,
|
||||
private songsService: SongsService,
|
||||
private change: ChangeDetectorRef
|
||||
private router: Router
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.form = this.editSongService.initSongEditForm(false);
|
||||
this.change.markForCheck();
|
||||
}
|
||||
|
||||
public onBack(): void {
|
||||
this.songsService.state.next(State.list);
|
||||
this.songsService.resetSelectedSong();
|
||||
this.form = this.editSongService.initSongEditForm(false, null);
|
||||
}
|
||||
|
||||
public onClickAdd(): void {
|
||||
this.songsService.saveNewSong$(this.form.value).subscribe();
|
||||
this.songsService.saveNewSong$(this.form.value).subscribe(song => {
|
||||
this.router.navigateByUrl('/songs/' + song.ID + '/read');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
import {SongsService} from '../../../data/songs.service';
|
||||
import {FileType} from '../../../models/files-types.model.ts';
|
||||
import {FormGroup} from '@angular/forms';
|
||||
import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
|
||||
import {EditSongService} from 'src/app/data/edit-song.service';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {FileType} from '../../../../models/files-types.model';
|
||||
import {File} from '../../../../models/file.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-song-file-edit',
|
||||
templateUrl: './song-file-edit.component.html',
|
||||
styleUrls: ['./song-file-edit.component.less']
|
||||
templateUrl: './song-files-edit.component.html',
|
||||
styleUrls: ['./song-files-edit.component.less']
|
||||
})
|
||||
export class SongFileEditComponent implements OnInit, OnDestroy {
|
||||
@Input() fileId: number;
|
||||
export class SongFilesEditComponent implements OnInit, OnDestroy {
|
||||
@Input() file: File;
|
||||
@Input() songId: number;
|
||||
@Output() back = new EventEmitter();
|
||||
public form: FormGroup;
|
||||
public subscription: Subscription;
|
||||
@@ -23,15 +24,13 @@ export class SongFileEditComponent implements OnInit, OnDestroy {
|
||||
];
|
||||
|
||||
constructor(
|
||||
private editSongService: EditSongService,
|
||||
private songService: SongsService
|
||||
) {
|
||||
private editSongService: EditSongService) {
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
const form = this.editSongService.initFileEditForm(
|
||||
this.songService.selectedSong.value.ID,
|
||||
this.fileId
|
||||
this.songId,
|
||||
this.file,
|
||||
);
|
||||
this.form = form.form;
|
||||
this.subscription = form.changeSubscription;
|
||||
@@ -1,4 +1,4 @@
|
||||
<div *ngIf="song" class="song-detail-container files">
|
||||
<div class="song-detail-container files">
|
||||
<mat-card class="mat-elevation-z8">
|
||||
<mat-card-content>
|
||||
<table [dataSource]="song.Files" class="mat-elevation-z8" mat-table>
|
||||
@@ -11,7 +11,8 @@
|
||||
<app-song-file-edit
|
||||
(back)="onClickEdit(null)"
|
||||
*ngIf="fileEditId === element.ID"
|
||||
[fileId]="element.ID"
|
||||
[file]="element"
|
||||
[songId]="song.ID"
|
||||
></app-song-file-edit>
|
||||
</td>
|
||||
</ng-container>
|
||||
56
WEB/src/app/songs/modules/read/files/files.component.ts
Normal file
56
WEB/src/app/songs/modules/read/files/files.component.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Song} from 'src/app/songs/models/song.model';
|
||||
import {SongsService} from 'src/app/data/songs.service';
|
||||
import {DownloadService} from 'src/app/data/download.service';
|
||||
import {faDownload, faEdit, faFileUpload, faLongArrowAltLeft, faTrash} from '@fortawesome/free-solid-svg-icons';
|
||||
import {FileuploadFactory} from 'src/app/songs/services/fileupload.factory';
|
||||
import {FileUploader} from 'ng2-file-upload';
|
||||
|
||||
@Component({
|
||||
selector: 'app-song-files',
|
||||
templateUrl: './files.component.html',
|
||||
styleUrls: ['./files.component.less']
|
||||
})
|
||||
export class SongFilesComponent implements OnInit {
|
||||
@Input() public song: Song;
|
||||
public faFileUpload = faFileUpload;
|
||||
public faTrash = faTrash;
|
||||
public faArrow = faLongArrowAltLeft;
|
||||
public faDownload = faDownload;
|
||||
public faEdit = faEdit;
|
||||
public columns = ['name', 'action'];
|
||||
public newFileUploader: FileUploader;
|
||||
public fileEditId: number;
|
||||
|
||||
public fileOverNew = false;
|
||||
|
||||
constructor(
|
||||
private downloadService: DownloadService,
|
||||
private fileuploadFactory: FileuploadFactory,
|
||||
private songService: SongsService
|
||||
) {
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.newFileUploader = FileuploadFactory.provideForNewFiles(this.song.ID);
|
||||
}
|
||||
|
||||
public onClickDownload(fileId: number, filename): void {
|
||||
this.downloadService.get(this.song.ID, fileId, filename);
|
||||
}
|
||||
|
||||
public onFileOverNew(hover: boolean): void {
|
||||
this.fileOverNew = hover;
|
||||
}
|
||||
|
||||
public onClickEdit(fileId: number): void {
|
||||
this.fileEditId = fileId;
|
||||
}
|
||||
|
||||
public onClickDelete(fileId: number): void {
|
||||
const songId = this.song.ID;
|
||||
this.songService
|
||||
.deleteFile$(songId, fileId)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
<div *ngIf="song" class="song-detail-container">
|
||||
<div class="song-detail-container">
|
||||
<mat-card class="mat-elevation-z8">
|
||||
<mat-card-header>
|
||||
<div mat-card-avatar>
|
||||
<button (click)="onBack()" color="warn" mat-icon-button>
|
||||
<button color="warn" mat-icon-button routerLink="/songs">
|
||||
<fa-icon [icon]="faArrow"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
@@ -23,7 +23,7 @@
|
||||
<p *ngFor="let line of comments">{{ line }}</p>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button (click)="onClickEdit()" mat-button>
|
||||
<button [routerLink]="'/songs/' + song.ID + '/edit'" mat-button>
|
||||
<fa-icon [icon]="faEdit"></fa-icon>
|
||||
bearbeiten
|
||||
</button>
|
||||
34
WEB/src/app/songs/modules/read/read/read.component.ts
Normal file
34
WEB/src/app/songs/modules/read/read/read.component.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
|
||||
import {faEdit, faLongArrowAltLeft} from '@fortawesome/free-solid-svg-icons';
|
||||
import {Song} from 'src/app/songs/models/song.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-song-read',
|
||||
templateUrl: './read.component.html',
|
||||
styleUrls: ['./read.component.less'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SongReadComponent {
|
||||
@Input() public song: Song;
|
||||
public faArrow = faLongArrowAltLeft;
|
||||
public faEdit = faEdit;
|
||||
|
||||
public get text(): string[] {
|
||||
return this.song && this.song.Text ? this.song.Text.split(/\r?\n/) : [];
|
||||
}
|
||||
|
||||
public get comments(): string[] {
|
||||
return this.song && this.song.Comments ? this.song.Comments.split(/\r?\n/) : [];
|
||||
}
|
||||
|
||||
public renderSongType(songType: string) {
|
||||
switch (songType) {
|
||||
case 'Praise':
|
||||
return {name: 'Lobpreis', color: '#99FFB8'};
|
||||
case 'Worship':
|
||||
return {name: 'Anbetung', color: '#C999FF'};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {base} from '../data/urls';
|
||||
import {base} from '../../data/urls';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {FileUploader} from 'ng2-file-upload';
|
||||
|
||||
@@ -8,7 +8,7 @@ import {FileUploader} from 'ng2-file-upload';
|
||||
export class FileuploadFactory {
|
||||
public static provideForNewFiles(songId: number): FileUploader {
|
||||
const uploader = new FileUploader({
|
||||
url: base + '/api/songs/' + songId + '/files',
|
||||
url: `${base}/api/songs/${songId}/files`,
|
||||
autoUpload: true,
|
||||
isHTML5: true
|
||||
});
|
||||
@@ -1,16 +1,20 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@import "~@angular/material/prebuilt-themes/indigo-pink.css";
|
||||
|
||||
body {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
html {
|
||||
background-image: url(https://images.unsplash.com/photo-1476136236990-838240be4859?ixlib=rb-1.2.1&auto=format&fit=crop&w=2167&q=80);
|
||||
}
|
||||
|
||||
.page-container {
|
||||
padding: 20px;
|
||||
body {
|
||||
margin: 0;
|
||||
|
||||
app-root {
|
||||
padding: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
@@ -32,24 +36,6 @@ html {
|
||||
border-top-left-radius: 8px;
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr.selected {
|
||||
background-color: #0002;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
cursor: pointer;
|
||||
background-color: #0001;
|
||||
|
||||
td {
|
||||
color: #ff9900;
|
||||
}
|
||||
}
|
||||
|
||||
td.mat-cell {
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&.pinned {
|
||||
padding: 0;
|
||||
@@ -57,40 +43,40 @@ html {
|
||||
border-radius: 0;
|
||||
|
||||
th.mat-header-cell:first-of-type {
|
||||
border-top-left-radius: 0px;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
th.mat-header-cell:last-of-type {
|
||||
border-top-right-radius: 0px;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.mat-table thead {
|
||||
border-top-right-radius: 0px;
|
||||
border-top-left-radius: 0px;
|
||||
border-top-right-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mat-card {
|
||||
.mat-card {
|
||||
width: 600px;
|
||||
border-radius: 8px;
|
||||
background: #fffd;
|
||||
margin: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-card-title {
|
||||
.mat-card-title {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 520px;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-card-content {
|
||||
.mat-card-content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.song-detail-container {
|
||||
.song-detail-container {
|
||||
|
||||
|
||||
.mat-form-field-infix {
|
||||
@@ -117,6 +103,6 @@ html {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user