feat(slideshow): admin ui for live slideshow

- per-event Live Slideshow card on the event detail page: generate/copy/
  regenerate/disable the share link + live style (transition, timing, color
  filter, watermark).
- shared SlideshowStyleFields, reused by the per-event card and the per-event-
  type preset section in the Edit Event Type modal.
- global watermark default card on the Event Types page (Settings -> slideshow).
- WatermarkSourcePicker: visible logo tiles with previews (light logo / dark-mode
  logo / favicon / event logo) instead of a blind dropdown.
- watermark mode tri-state (inherit/on/off) + white-vs-original style.
- supporting service methods + Event/EventType types.
This commit is contained in:
Luca
2026-06-19 22:11:12 +02:00
parent fd02254f78
commit 385b05adcf
10 changed files with 769 additions and 3 deletions
@@ -4,6 +4,7 @@
*/
import { api } from '../config/api';
import type { SlideshowStyle } from './slideshow.service';
export interface EventType {
id: number;
@@ -12,6 +13,9 @@ export interface EventType {
emoji: string;
theme_preset: string;
theme_config: string | null;
// Live Slideshow preset new events of this type inherit (migration 138).
// Stored as a JSON string by the API; null = no preset.
slideshow_preset: string | null;
display_order: number;
is_system: boolean;
is_active: boolean;
@@ -25,6 +29,7 @@ export interface CreateEventTypeData {
emoji?: string;
theme_preset?: string;
theme_config?: Record<string, unknown>;
slideshow_preset?: SlideshowStyle | null;
display_order?: number;
}
@@ -34,6 +39,7 @@ export interface UpdateEventTypeData {
emoji?: string;
theme_preset?: string;
theme_config?: Record<string, unknown>;
slideshow_preset?: SlideshowStyle | null;
display_order?: number;
is_active?: boolean;
}
+30
View File
@@ -138,6 +138,36 @@ export const eventsService = {
await api.delete(`/admin/events/${id}`);
},
// Live Slideshow ("Diashow") — mint/rotate the share token (admin)
async generateSlideshowLink(id: number): Promise<{ show_share_token: string; slideshow_url: string }> {
const response = await api.post(`/admin/events/${id}/slideshow/generate`);
return response.data;
},
// Disable the slideshow link (null the token) (admin)
async disableSlideshowLink(id: number): Promise<void> {
await api.post(`/admin/events/${id}/slideshow/disable`);
},
// Update live slideshow settings (display time / transition / style) (admin)
async updateSlideshowSettings(
id: number,
settings: {
show_interval_ms?: number;
show_transition?: string;
show_transition_ms?: number;
show_watermark?: boolean | null;
show_watermark_source?: string;
show_watermark_position?: string;
show_watermark_opacity?: number;
show_watermark_style?: string;
show_colorfilter?: string;
}
): Promise<Record<string, unknown>> {
const response = await api.patch(`/admin/events/${id}/slideshow`, settings);
return response.data;
},
// Force archive event (admin)
async archiveEvent(id: number): Promise<void> {
await api.post(`/admin/events/${id}/archive`);
+6 -1
View File
@@ -189,11 +189,16 @@ export const settingsService = {
},
// Get settings by type
async getSettingsByType(type: 'branding' | 'theme' | 'general'): Promise<Record<string, any>> {
async getSettingsByType(type: 'branding' | 'theme' | 'general' | 'slideshow'): Promise<Record<string, any>> {
const response = await api.get<Record<string, any>>(`/admin/settings/${type}`);
return response.data;
},
// Update global Live Slideshow defaults (watermark)
async updateSlideshowDefaults(settings: Record<string, unknown>): Promise<void> {
await api.put('/admin/settings/slideshow', settings);
},
// Update branding settings
async updateBranding(settings: BrandingSettings): Promise<void> {
await api.put('/admin/settings/branding', settings);