import { Component, EventEmitter, inject, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { firstValueFrom } from 'rxjs'; import { Show } from '../interfaces/show'; import { ShowService, ShowScheduleCreate, ShowCreatePayload, ShowUpdatePayload } from '../services/show.service'; import { UploadService } from '../services/upload.service'; import { getAppConfig } from '../services/app-config.service'; @Component({ selector: 'app-admin-show-form', standalone: true, imports: [CommonModule, FormsModule], templateUrl: './admin-show-form.component.html', styleUrl: './admin-show-form.component.scss', }) export class AdminShowFormComponent implements OnChanges { private showService = inject(ShowService); private uploadService = inject(UploadService); @Input() editItem: Show | null = null; @Output() saved = new EventEmitter(); @Output() closed = new EventEmitter(); readonly days = [ { value: 1, label: 'Monday' }, { value: 2, label: 'Tuesday' }, { value: 3, label: 'Wednesday' }, { value: 4, label: 'Thursday' }, { value: 5, label: 'Friday' }, { value: 6, label: 'Saturday' }, { value: 7, label: 'Sunday' }, ]; id: number | null = null; title = ''; description = ''; host = ''; genre = ''; show_art_url = ''; hero_image_url = ''; display_order = 0; schedules: ShowScheduleCreate[] = [ { day_of_week: 1, time: '' }, ]; loading = false; uploading = false; error = ''; success = ''; submitted = false; ngOnChanges(changes: SimpleChanges): void { if (changes['editItem'] && this.editItem) { this.edit(this.editItem); } } edit(show: Show): void { this.id = show.id; this.title = show.title; this.description = show.description; this.host = show.host; this.genre = show.genre; this.show_art_url = show.show_art_url || ''; this.hero_image_url = show.hero_image_url || ''; this.display_order = show.display_order; this.schedules = show.schedules.map((s) => ({ day_of_week: s.day_of_week, time: s.time, })); } formatError(err: any): string { if (err?.error?.detail) return err.error.detail; if (err?.error?.message) return err.error.message; if (err?.message) return err.message; return 'Failed to save. Please try again.'; } reset(): void { this.id = null; this.title = ''; this.description = ''; this.host = ''; this.genre = ''; this.show_art_url = ''; this.hero_image_url = ''; this.display_order = 0; this.schedules = [{ day_of_week: 1, time: '' }]; this.error = ''; this.success = ''; this.submitted = false; } onClose(): void { this.closed.emit(); this.reset(); } /** Add a new empty schedule slot row. */ addScheduleSlot(): void { this.schedules.push({ day_of_week: 1, time: '' }); } /** Remove a schedule slot row by index. */ removeScheduleSlot(index: number): void { this.schedules.splice(index, 1); } /** Handle image file upload and set the URL field. */ async onImageUpload(event: Event, field: string): Promise { const input = event.target as HTMLInputElement; if (!input?.files?.length) return; const file = input.files[0]; if (!file.type.startsWith('image/')) { this.error = 'Only image files are allowed.'; return; } if (file.size > 5 * 1024 * 1024) { this.error = 'File size exceeds 5 MB limit.'; return; } this.uploading = true; this.error = ''; try { const url = await this.uploadService.uploadImage(file); (this as any)[field] = url; this.success = 'Image uploaded successfully.'; } catch (err: any) { this.error = this.formatError(err); } finally { this.uploading = false; } } /** Resolve a relative uploads/… path to an absolute URL for preview. */ getPreviewUrl(url: string): string { if (!url) return ''; if (url.startsWith('http://') || url.startsWith('https://')) return url; return `${getAppConfig().apiBaseUrl}/${url}`; } async onSubmit(): Promise { this.submitted = true; // Validate required fields if (!this.title || !this.host || !this.genre) { this.error = 'Please fill in all required fields.'; return; } // Validate schedules const validSchedules = this.schedules.filter( (s) => s.day_of_week && s.time, ); if (validSchedules.length === 0) { this.error = 'Add at least one schedule slot with a day and time.'; return; } this.loading = true; this.error = ''; const payload = { title: this.title, description: this.description, host: this.host, genre: this.genre, show_art_url: this.show_art_url || null, hero_image_url: this.hero_image_url || null, display_order: this.display_order, schedules: validSchedules, }; try { if (this.id) { const updatePayload: ShowUpdatePayload = { ...payload }; await firstValueFrom(this.showService.updateShow(this.id, updatePayload)); } else { await firstValueFrom(this.showService.createShow(payload as ShowCreatePayload)); } this.saved.emit(); this.reset(); } catch (err: any) { this.error = this.formatError(err); } finally { this.loading = false; } } }