diff --git a/src/app/core/components/photo-table/photo-table.component.html b/src/app/core/components/photo-table/photo-table.component.html index eb0845d..9d2702e 100644 --- a/src/app/core/components/photo-table/photo-table.component.html +++ b/src/app/core/components/photo-table/photo-table.component.html @@ -6,16 +6,18 @@ Id Date img + {{ photo.id }} - {{ photo.date | date: 'dd-MM-yyyy - HH:mm' }} + {{ photo.date | date : 'dd-MM-yyyy - HH:mm' }} - + + diff --git a/src/app/core/components/photo-table/photo-table.component.ts b/src/app/core/components/photo-table/photo-table.component.ts index 689d4ac..12965be 100644 --- a/src/app/core/components/photo-table/photo-table.component.ts +++ b/src/app/core/components/photo-table/photo-table.component.ts @@ -1,28 +1,31 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; import { Photo } from '../../types/photo.type'; import { EnvironmentService } from '../../services/environment.service'; import { NgIf, NgFor, DatePipe } from '@angular/common'; +import { BootstrapIconComponent } from '../bootstrap-icon/bootstrap-icon.component'; @Component({ selector: 'photo-table', templateUrl: './photo-table.component.html', styleUrls: ['./photo-table.component.scss'], standalone: true, - imports: [NgIf, NgFor, DatePipe] + imports: [NgIf, NgFor, DatePipe, BootstrapIconComponent] }) -export class PhotoTableComponent implements OnInit { +export class PhotoTableComponent implements OnChanges { @Input() photos: Photo[]; @Input() showGenerateImageLinks: boolean = false; + @Input() showDeleteButton: boolean = false; @Output() imageHtml: EventEmitter = new EventEmitter(); + @Output() delete: EventEmitter = new EventEmitter(); tablePhotos: { id: number; date: Date; srcSmall: string; srcText: string; srcLarge: string }[]; constructor(private readonly _environmentService: EnvironmentService) {} - ngOnInit(): void { + ngOnChanges(): void { this.tablePhotos = this.photos.map(photo => this.mapPhoto(photo, this._environmentService.baseApiUrl)); } - generateImgHtml(srcText: string, srcLarge: string): void { + onGenerateImgHtml(srcText: string, srcLarge: string): void { this.imageHtml.emit( `` + `` + @@ -31,6 +34,10 @@ export class PhotoTableComponent implements OnInit { ); } + onDelete(photoId: number): void { + this.delete.emit(photoId); + } + private mapPhoto(photo: Photo, baseApiUrl: string): { id: number; date: Date; srcSmall: string; srcText: string; srcLarge: string } { const smallestImage = photo.images.sort((a, b) => a.widthPx - b.widthPx)[0]; const largestImage = photo.images.sort((a, b) => a.widthPx - b.widthPx).reverse()[0]; diff --git a/src/app/core/services/album.service.ts b/src/app/core/services/album.service.ts index 0cf692d..b2285da 100644 --- a/src/app/core/services/album.service.ts +++ b/src/app/core/services/album.service.ts @@ -22,6 +22,10 @@ export class AlbumService { return this._http.get(this._getUrl(`GetById?id=${id}`)); } + deletePhoto(albumId: number, photoId: number): Observable { + return this._http.delete(this._getUrl('DeletePhoto'), { body: { albumId, photoId } }); + } + private _getUrl(method: string): string { return this._urlBuilderHelper.constructUrlWithApiUrlPrefix('v1/Album/' + method); } diff --git a/src/app/pages/admin/albums/add-photo/add-photo.component.ts b/src/app/pages/admin/albums/add-photo/add-photo.component.ts index c767e92..a51170b 100644 --- a/src/app/pages/admin/albums/add-photo/add-photo.component.ts +++ b/src/app/pages/admin/albums/add-photo/add-photo.component.ts @@ -1,6 +1,5 @@ import { HttpEvent, HttpEventType } from '@angular/common/http'; import { Component } from '@angular/core'; -import { map } from 'rxjs'; import { AlbumService } from 'src/app/core/services/album.service'; import { PhotosService } from 'src/app/core/services/photos.service'; import { WindowService } from 'src/app/core/services/window.service'; @@ -33,10 +32,7 @@ export class AddPhotoComponent { private readonly _windowService: WindowService, private readonly _albumService: AlbumService ) { - this._albumService - .getAlbums() - .pipe(map(albums => [{ title: 'No album (homepage)', id: null }, ...albums])) - .subscribe(albums => (this.albums = albums)); + this._albumService.getAlbums().subscribe(albums => (this.albums = albums)); } onFileSelected(event: Event): void { diff --git a/src/app/pages/admin/albums/albums-overview/albums-overview.component.html b/src/app/pages/admin/albums/albums-overview/albums-overview.component.html index db92b7a..179ef3d 100644 --- a/src/app/pages/admin/albums/albums-overview/albums-overview.component.html +++ b/src/app/pages/admin/albums/albums-overview/albums-overview.component.html @@ -11,6 +11,29 @@
Album content
- +
+

You are about to delete the following location:

+
    +
  • AlbumId: {{ photoToDelete.albumId }}
  • +
  • Album name: {{ photoToDelete.albumName | nullableDisplay }}
  • +
  • PhotoId: {{ photoToDelete.photoId }}
  • +
+ +

Are you sure? This action cannot be undone.

+ + + +
+ Deleting location + An error occurred while deleting the location +
+
+ + diff --git a/src/app/pages/admin/albums/albums-overview/albums-overview.component.ts b/src/app/pages/admin/albums/albums-overview/albums-overview.component.ts index 5ad6145..3e688d5 100644 --- a/src/app/pages/admin/albums/albums-overview/albums-overview.component.ts +++ b/src/app/pages/admin/albums/albums-overview/albums-overview.component.ts @@ -2,27 +2,99 @@ import { Component } from '@angular/core'; import { AlbumService } from 'src/app/core/services/album.service'; import { Album, AlbumDetails } from 'src/app/core/types/album.type'; import { PhotoTableComponent } from '../../../../core/components/photo-table/photo-table.component'; -import { NgFor, NgIf } from '@angular/common'; +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { FormsModule } from '@angular/forms'; +import { Observable, ReplaySubject, of, switchMap, tap } from 'rxjs'; +import { DataStatus, StateStatus } from 'src/app/core/types/data-status.types'; +import { DataStatusPipesModule } from 'src/app/core/pipes/status/data-status-pipes.module'; +import { LoadingMessageComponent } from '../../../../core/components/loading-message/loading-message.component'; +import { ErrorMessageComponent } from '../../../../core/components/error-message/error-message.component'; +import { inspectStatus } from 'src/app/core/helpers/rxjs-operators'; +import { WindowService } from 'src/app/core/services/window.service'; +import { NullableDisplayPipe } from '../../../../core/pipes/nullable-display.pipe'; @Component({ selector: 'albums-overview', templateUrl: './albums-overview.component.html', styleUrls: ['./albums-overview.component.scss'], standalone: true, - imports: [FormsModule, NgFor, NgIf, PhotoTableComponent] + imports: [ + FormsModule, + NgFor, + NgIf, + PhotoTableComponent, + AsyncPipe, + DataStatusPipesModule, + LoadingMessageComponent, + ErrorMessageComponent, + NullableDisplayPipe + ] }) export class AlbumsOverviewComponent { albums: Album[]; - selectedAlbumId: number; + selectedAlbumId: string; selectedAlbumDetails: AlbumDetails | null; - constructor(private readonly _albumService: AlbumService) { + deleted$: ReplaySubject = new ReplaySubject(); + deleteResult$: Observable | null> = new Observable(); + numberOfItems: number = 50; + + photoToDelete: AlbumPhotoDelete; + showDeleteConfirmation: boolean; + + constructor(private readonly _albumService: AlbumService, windowService: WindowService) { this._albumService.getAlbums().subscribe(albums => (this.albums = albums)); + + this.deleteResult$ = this.deleted$.pipe( + switchMap(photoToDelete => + photoToDelete + ? this._albumService.deletePhoto(photoToDelete.albumId, photoToDelete.photoId).pipe( + inspectStatus(), + tap(dataStatus => { + if (dataStatus.status === StateStatus.valid) { + windowService.reload(); + } + }) + ) + : of(null) + ) + ); } onAlbumSelect(): void { this.selectedAlbumDetails = null; - this._albumService.getById(this.selectedAlbumId).subscribe(albumDetails => (this.selectedAlbumDetails = albumDetails)); + this.cancelDelete(); + this._albumService + .getById(this.getAlbumId(this.selectedAlbumId)) + .subscribe(albumDetails => (this.selectedAlbumDetails = albumDetails)); + } + + onPendingDelete(photoId: number): void { + const albumId = this.getAlbumId(this.selectedAlbumId); + this.photoToDelete = { + albumId: albumId, + albumName: this.albums.find(album => album.id === albumId)?.title ?? null, + photoId + }; + this.showDeleteConfirmation = true; + } + + confirmDelete(): void { + this.deleted$.next(this.photoToDelete); } + + cancelDelete(): void { + this.deleted$.next(null); + this.showDeleteConfirmation = false; + } + + private getAlbumId(albumId: string): number { + return parseInt(albumId, 10); + } +} + +export interface AlbumPhotoDelete { + albumId: number; + albumName: string | null; + photoId: number; } diff --git a/src/app/pages/admin/albums/albums.component.ts b/src/app/pages/admin/albums/albums.component.ts index 0c72a08..52e4dac 100644 --- a/src/app/pages/admin/albums/albums.component.ts +++ b/src/app/pages/admin/albums/albums.component.ts @@ -9,7 +9,6 @@ import { AlbumsPaths } from './albums.routes'; }) export class AlbumsComponent { readonly children: { route: string; title: string }[] = [ - { route: AlbumsPaths.homepage, title: 'Homepage' }, { route: AlbumsPaths.overview, title: 'Overview' }, { route: AlbumsPaths.add, title: 'Add album' }, { route: AlbumsPaths.uploadPhoto, title: 'Add photo' } diff --git a/src/app/pages/admin/albums/albums.routes.ts b/src/app/pages/admin/albums/albums.routes.ts index 5af4ed0..09f8e96 100644 --- a/src/app/pages/admin/albums/albums.routes.ts +++ b/src/app/pages/admin/albums/albums.routes.ts @@ -3,10 +3,8 @@ import { AddAlbumComponent } from './add-album/add-album.component'; import { AddPhotoComponent } from './add-photo/add-photo.component'; import { AlbumsOverviewComponent } from './albums-overview/albums-overview.component'; import { AlbumsComponent } from './albums.component'; -import { HomepagePhotosComponent } from './homepage-photos/homepage-photos.component'; export class AlbumsPaths { - static readonly homepage: string = 'homepage'; static readonly overview: string = 'overview'; static readonly add: string = 'add'; static readonly uploadPhoto: string = 'upload-photo'; @@ -17,8 +15,7 @@ export const ALBUMS_ROUTES: Routes = [ path: '', component: AlbumsComponent, children: [ - { path: '', redirectTo: AlbumsPaths.homepage, pathMatch: 'full' }, - { path: AlbumsPaths.homepage, component: HomepagePhotosComponent }, + { path: '', redirectTo: AlbumsPaths.overview, pathMatch: 'full' }, { path: AlbumsPaths.overview, component: AlbumsOverviewComponent }, { path: AlbumsPaths.add, component: AddAlbumComponent }, { path: AlbumsPaths.uploadPhoto, component: AddPhotoComponent } diff --git a/src/app/pages/admin/albums/homepage-photos/homepage-photos.component.html b/src/app/pages/admin/albums/homepage-photos/homepage-photos.component.html deleted file mode 100644 index 67ea92e..0000000 --- a/src/app/pages/admin/albums/homepage-photos/homepage-photos.component.html +++ /dev/null @@ -1,2 +0,0 @@ -

Photos overview

- diff --git a/src/app/pages/admin/albums/homepage-photos/homepage-photos.component.ts b/src/app/pages/admin/albums/homepage-photos/homepage-photos.component.ts deleted file mode 100644 index 3aab071..0000000 --- a/src/app/pages/admin/albums/homepage-photos/homepage-photos.component.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component } from '@angular/core'; -import { PhotosService } from 'src/app/core/services/photos.service'; -import { Photo } from 'src/app/core/types/photo.type'; -import { PhotoTableComponent } from '../../../../core/components/photo-table/photo-table.component'; -import { NgIf } from '@angular/common'; - -@Component({ - templateUrl: './homepage-photos.component.html', - standalone: true, - imports: [NgIf, PhotoTableComponent] -}) -export class HomepagePhotosComponent { - photos: Photo[]; - - constructor(photosService: PhotosService) { - photosService.getPhotos().subscribe(photos => (this.photos = photos)); - } -} diff --git a/src/app/pages/admin/updates/add-update/add-update.component.html b/src/app/pages/admin/updates/add-update/add-update.component.html index 2452b96..0a2a7bb 100644 --- a/src/app/pages/admin/updates/add-update/add-update.component.html +++ b/src/app/pages/admin/updates/add-update/add-update.component.html @@ -54,6 +54,14 @@
Text preview
Blog photos
+ +
+ + +
+ = new ReplaySubject(); blogPhotos: Photo[]; imageHtml: string; @@ -68,18 +71,25 @@ export class AddUpdateComponent { }); } - albumService - .getAlbums() - .pipe(map(albums => [{ title: 'No album', id: null }, ...albums])) - .subscribe(albums => (this.albums = albums)); + albumService.getAlbums().subscribe(albums => { + this.blogAlbums = albums; + this.albums = [{ title: 'No album', id: null }, ...albums]; + }); - albumService.getById(1).subscribe(albumDetails => (this.blogPhotos = albumDetails.photos)); + this.blogPhotosAlbumId$ + .pipe( + switchMap(albumId => { + const albumIdNumber = this.getAlbumId(albumId); + return albumIdNumber ? albumService.getById(albumIdNumber) : of({ photos: [] }); + }) + ) + .subscribe(albumDetails => (this.blogPhotos = albumDetails.photos)); placeService.getAll().subscribe(places => (this.places = places)); } get formInvalid(): boolean { - return !this.title || !this.type || (!this.getAlbumId() && !this.text); + return !this.title || !this.type || (!this.getAlbumId(this.albumId) && !this.text); } onPreview(): void { @@ -95,6 +105,10 @@ export class AddUpdateComponent { this.distance = this.places.find(place => place.id === parseInt(placeId, 10))?.distance ?? null; } + onBlogAlbumChange(): void { + this.blogPhotosAlbumId$.next(this.blogPhotosAlbumId); + } + reloadComponent(): void { this._windowService.reload(); } @@ -103,8 +117,8 @@ export class AddUpdateComponent { this.imageHtml = html; } - private getAlbumId(): number | null { - return this.albumId ? (isNaN(+this.albumId) ? null : parseInt(this.albumId, 10)) : null; + private getAlbumId(albumId: string | null): number | null { + return albumId ? (isNaN(+albumId) ? null : parseInt(albumId, 10)) : null; } private addUpdate(): void { @@ -116,7 +130,7 @@ export class AddUpdateComponent { text: this.text, distance: this.distance, placeId: this.placeId ? parseInt(this.placeId, 10) : null, - albumId: this.getAlbumId() + albumId: this.getAlbumId(this.albumId) }) .subscribe({ next: () => { @@ -137,7 +151,7 @@ export class AddUpdateComponent { text: this.text, distance: this.distance, placeId: this.placeId ? parseInt(this.placeId, 10) : null, - albumId: this.getAlbumId() + albumId: this.getAlbumId(this.albumId) }) .subscribe({ next: () => { diff --git a/src/assets/logos/riesj_logo.svg b/src/assets/logos/riesj_logo.svg index 8c99355..70fb734 100644 --- a/src/assets/logos/riesj_logo.svg +++ b/src/assets/logos/riesj_logo.svg @@ -4,8 +4,8 @@ viewBox="-15 25 600 150" style="enable-background:new 0 0 801 200;" xml:space="preserve">