feat(events): duplicate-gallery action (#626)
Daniel asked for a way to re-use a good gallery configuration without re-entering every setting. Two of his three suggested workflows are covered by this PR; the third (per-event-type behaviour defaults) is partially shipped already via event_types.theme_preset + theme_config and is left as a follow-up if the duplicate workflow doesn't cover it. Backend — POST /admin/events/:id/duplicate. Validates a new event_name (required) + event_date (optional) + customer_name/email (optional); copies branding (color_theme, css_template_id, header/hero/divider/anchor), behaviour toggles (allow_downloads, watermark_*, allow_user_uploads, require_password, etc.), photo_cap, welcome_message, default_photo_sort, admin_email, and feedback settings + per-event photo categories. Mints a fresh slug + share_token + random-placeholder password_hash (admin sets the real one via the publish dialog shipped in #627). Recomputes expires_at = new_event_date + (source.expires_at - source.event_date) so the duplicate keeps the same active window; defaults to 30 days if either source field was null. is_draft is always true. Deliberately NOT carried over: photos, hero_photo_id, client_access secrets, og_image_share opt-in, customer_phone, sent_at flags, archive state, customer-account assignments. Frontend — new DuplicateEventDialog (matches the PublishGalleryDialog pattern), wired into the Actions card on EventDetailsPage. Visible in both draft and live mode since admins typically duplicate from a published gallery. On success the page navigates to the new draft so the admin can finish customising + publish. I18n: EN + DE entries for the dialog + button label. Backend logs an event_duplicated activity with the source event id/name so the trail is auditable. Frontend service: eventsService.duplicateEvent(eventId, data).
This commit is contained in:
@@ -1027,6 +1027,22 @@
|
||||
"passwordHelp": "Gib das bei der Erstellung gesetzte Passwort erneut ein (oder wähle ein neues). Die E-Mail enthält genau diesen Text; das Backend hasht es erneut, sodass die Galerie-Anmeldung weiterhin funktioniert.",
|
||||
"errorMinLength": "Das Passwort muss mindestens 6 Zeichen lang sein."
|
||||
},
|
||||
"duplicateEvent": "Galerie duplizieren",
|
||||
"duplicateDialog": {
|
||||
"title": "Galerie duplizieren",
|
||||
"description": "Erstellt eine neue Entwurfsgalerie, die Branding, Verhalten, Feedback und Kategorien aus \"{{sourceEventName}}\" übernimmt. Fotos, Passwort und Share-Tokens werden NICHT übernommen.",
|
||||
"eventNameLabel": "Neuer Veranstaltungsname *",
|
||||
"eventNamePlaceholder": "z. B. Hochzeit Müller 2026",
|
||||
"eventDateLabel": "Veranstaltungsdatum",
|
||||
"eventDateHelp": "Leer lassen, um eine zufällige Endung in der Galerie-URL zu verwenden. Das Ablaufdatum wird aus diesem Datum zuzüglich des Ablauffensters der Quellgalerie berechnet.",
|
||||
"customerNameLabel": "Kundenname",
|
||||
"customerNamePlaceholder": "Optional – kann später ausgefüllt werden",
|
||||
"customerEmailLabel": "Kunden-E-Mail",
|
||||
"customerEmailPlaceholder": "Optional",
|
||||
"confirm": "Duplikat erstellen",
|
||||
"errorNameRequired": "Veranstaltungsname ist erforderlich.",
|
||||
"successToast": "Galerie dupliziert."
|
||||
},
|
||||
"draftBanner": "Diese Galerie befindet sich im Entwurfsmodus. Laden Sie Ihre Fotos hoch und veröffentlichen Sie, wenn Sie bereit sind.",
|
||||
"subtitle": "Verwalten Sie Ihre Fotogalerien und Veranstaltungen",
|
||||
"failedToLoadEvents": "Veranstaltungen konnten nicht geladen werden",
|
||||
|
||||
@@ -574,6 +574,22 @@
|
||||
"passwordHelp": "Re-type the password set at creation (or pick a new one). The email includes this exact text; the backend re-hashes it so the gallery login still works.",
|
||||
"errorMinLength": "Password must be at least 6 characters long."
|
||||
},
|
||||
"duplicateEvent": "Duplicate gallery",
|
||||
"duplicateDialog": {
|
||||
"title": "Duplicate gallery",
|
||||
"description": "Creates a new draft gallery that inherits the branding, behaviour, feedback, and category configuration from \"{{sourceEventName}}\". Photos, password, and share tokens are NOT carried over.",
|
||||
"eventNameLabel": "New event name *",
|
||||
"eventNamePlaceholder": "e.g. Müller Wedding 2026",
|
||||
"eventDateLabel": "Event date",
|
||||
"eventDateHelp": "Leave blank to use a random suffix in the gallery URL. Expiration is recomputed from this date plus the source gallery's expiration window.",
|
||||
"customerNameLabel": "Customer name",
|
||||
"customerNamePlaceholder": "Optional — fill in later if unknown",
|
||||
"customerEmailLabel": "Customer email",
|
||||
"customerEmailPlaceholder": "Optional",
|
||||
"confirm": "Create duplicate",
|
||||
"errorNameRequired": "Event name is required.",
|
||||
"successToast": "Gallery duplicated."
|
||||
},
|
||||
"draftBanner": "This gallery is in draft mode. Upload your photos, then publish when ready.",
|
||||
"subtitle": "Manage your photo galleries and events",
|
||||
"failedToLoadEvents": "Failed to load events",
|
||||
|
||||
Reference in New Issue
Block a user