Some checks failed
Architecture Governance / architecture (push) Has been cancelled
MediaLibraryPageComponent replaces the coming-soon placeholder at /backoffice/media. Built entirely on Sprint 6 Design System primitives (app-card, app-button, app-input, app-empty-state, app-dialog, app-pagination, app-skeleton) and Sprint 4 Task 2's MediaRepository/MockMediaRepository. - Grid view with per-tile filename/size and delete action - Hidden native file input triggered by an app-button, uploads via MediaLibraryFacade -> MediaRepository.upload() - Delete requires confirmation through app-dialog (destructive action) - Search + pagination wired to MockMediaRepository's list() params - Loading state shows app-skeleton tiles; empty state shows app-empty-state - New mediaLibrary.* translation namespace across en/ru/hy Verified in browser (via a temporary unguarded route, reverted before commit - /backoffice/media itself requires Telegram QR admin auth not available in this session): empty state renders correctly with translated copy, search input and upload button present, no console errors.
72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
import { Injectable, inject, signal } from '@angular/core';
|
|
import { MediaRepository } from '../../../../core/media/media-repository';
|
|
import { MediaAsset } from '../../../../core/media/models/media-asset.model';
|
|
|
|
const PAGE_SIZE = 24;
|
|
|
|
@Injectable({ providedIn: 'root' })
|
|
export class MediaLibraryFacade {
|
|
private readonly repository = inject(MediaRepository);
|
|
|
|
readonly items = signal<MediaAsset[]>([]);
|
|
readonly total = signal(0);
|
|
readonly loading = signal(false);
|
|
readonly uploading = signal(false);
|
|
readonly error = signal<string | null>(null);
|
|
readonly search = signal('');
|
|
readonly page = signal(1);
|
|
|
|
async load(): Promise<void> {
|
|
this.loading.set(true);
|
|
this.error.set(null);
|
|
try {
|
|
const result = await this.repository.list({
|
|
page: this.page(),
|
|
pageSize: PAGE_SIZE,
|
|
search: this.search(),
|
|
});
|
|
this.items.set(result.items);
|
|
this.total.set(result.total);
|
|
} catch {
|
|
this.error.set('Failed to load media assets.');
|
|
} finally {
|
|
this.loading.set(false);
|
|
}
|
|
}
|
|
|
|
setSearch(value: string): void {
|
|
this.search.set(value);
|
|
this.page.set(1);
|
|
void this.load();
|
|
}
|
|
|
|
setPage(page: number): void {
|
|
this.page.set(page);
|
|
void this.load();
|
|
}
|
|
|
|
async upload(file: File): Promise<void> {
|
|
this.uploading.set(true);
|
|
this.error.set(null);
|
|
try {
|
|
await this.repository.upload(file);
|
|
this.page.set(1);
|
|
await this.load();
|
|
} catch {
|
|
this.error.set('Failed to upload file.');
|
|
} finally {
|
|
this.uploading.set(false);
|
|
}
|
|
}
|
|
|
|
async remove(id: string): Promise<void> {
|
|
this.error.set(null);
|
|
try {
|
|
await this.repository.remove(id);
|
|
await this.load();
|
|
} catch {
|
|
this.error.set('Failed to delete file.');
|
|
}
|
|
}
|
|
}
|