diff --git a/src/features/event/components/EventFormInline.tsx b/src/features/event/components/EventFormInline.tsx index c471c16..c83bddb 100644 --- a/src/features/event/components/EventFormInline.tsx +++ b/src/features/event/components/EventFormInline.tsx @@ -649,12 +649,6 @@ const EventFormInline: React.FC = ({ onSuccess, onCancel, {activeTab === 'promotion' && ( { setForm((prev) => ({ ...prev, mediaUrl: url })); @@ -693,7 +687,16 @@ const EventFormInline: React.FC = ({ onSuccess, onCancel, /> )} - {activeTab === 'schedule' && } + {activeTab === 'schedule' && ( + + )} diff --git a/src/features/event/components/PromotionTemplatesSection.tsx b/src/features/event/components/PromotionTemplatesSection.tsx index e063253..28e8002 100644 --- a/src/features/event/components/PromotionTemplatesSection.tsx +++ b/src/features/event/components/PromotionTemplatesSection.tsx @@ -1,16 +1,10 @@ -import React from 'react'; +import React, { useMemo, useRef, useState } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; type Props = { - promotionOffsetDays: string; - setPromotionOffsetDays: (v: string) => void; - promotionStartDate: string; - handlePromotionStartChange: (iso: string) => void; - promotionStartRef: React.RefObject; - formatDateForDisplay: (iso: string) => string; - mediaUrl: string; onMediaUrlChange: (url: string) => void; @@ -20,69 +14,151 @@ type Props = { mediaPreview: string | null; }; +type PromotionTemplate = { + id: string; + name: string; + description: string; + previewUrl?: string; +}; + const PromotionTemplatesSection: React.FC = ({ - promotionOffsetDays, - setPromotionOffsetDays, - promotionStartDate, - handlePromotionStartChange, - promotionStartRef, - formatDateForDisplay, mediaUrl, onMediaUrlChange, fileInputRef, onFileSelected, mediaPreview, }) => { - return ( -
-
-
Promotion Templates
-
- Letzter Post vor Event - setPromotionOffsetDays(e.target.value)} - /> - Tage + const templates: PromotionTemplate[] = useMemo( + () => [ + { + id: 'tpl_event_minimal', + name: 'Event Minimal', + description: 'Cleanes Event-Template mit Fokus auf Titel + Datum.', + previewUrl: '/images/templates/event_minimal_preview.png', + }, + { + id: 'tpl_event_bold', + name: 'Event Bold', + description: 'Große Typo, geeignet für starke Keyvisuals.', + previewUrl: '/images/templates/event_bold_preview.png', + }, + { + id: 'tpl_lineup_grid', + name: 'Lineup Grid', + description: 'Lineup als Grid-Layout, gut für mehrere Artists.', + previewUrl: '/images/templates/lineup_grid_preview.png', + }, + { + id: 'tpl_artist_focus', + name: 'Artist Focus', + description: 'Portrait/Keyvisual im Fokus, Name + Slot unten.', + previewUrl: '/images/templates/artist_focus_preview.png', + }, + { + id: 'tpl_artist_story', + name: 'Artist Story', + description: 'Story-Format mit kurzem Teaser + Social Handles.', + previewUrl: '/images/templates/artist_story_preview.png', + }, + ], + [] + ); + + const [eventPromoTemplateId, setEventPromoTemplateId] = useState(templates[0]?.id ?? ''); + const [eventLineupTemplateId, setEventLineupTemplateId] = useState(templates[2]?.id ?? ''); + const [artistPromoTemplateId, setArtistPromoTemplateId] = useState(templates[3]?.id ?? ''); + + const importZipInputRef = useRef(null); + const [importingTemplate, setImportingTemplate] = useState(false); + + const selectedEventPromo = templates.find((t) => t.id === eventPromoTemplateId) || null; + const selectedEventLineup = templates.find((t) => t.id === eventLineupTemplateId) || null; + const selectedArtistPromo = templates.find((t) => t.id === artistPromoTemplateId) || null; + + const handleImportZip = async (file: File) => { + setImportingTemplate(true); + try { + // Placeholder: Upload to backend endpoint will be added later + await new Promise((r) => setTimeout(r, 400)); + } finally { + setImportingTemplate(false); + } + }; + + const TemplateSelect = ({ + label, + value, + onValueChange, + selected, + }: { + label: string; + value: string; + onValueChange: (next: string) => void; + selected: PromotionTemplate | null; + }) => { + return ( +
+ +
+ + +
+
Vorschau
+
+
+ {selected?.previewUrl ? ( + + ) : ( +
+ )} +
+
+
{selected?.name || 'Kein Template gewählt'}
+
{selected?.description || ''}
+
+
+
+ ); + }; + + return ( +
+
Promotion Templates
Sektion 1 – Event-Promotion
-
-
- {formatDateForDisplay(promotionStartDate) || 'Noch kein Datum gewählt'} - -
- handlePromotionStartChange(e.target.value)} - required - /> -
+ +
@@ -117,6 +193,52 @@ const PromotionTemplatesSection: React.FC = ({
+
+
Sektion 2 – Event Lineup
+ +
+ +
+
Sektion 3 – Artist Promo
+ +
+ +
+
Templates verwalten
+
+ { + const file = e.target.files?.[0]; + if (!file) return; + await handleImportZip(file); + e.target.value = ''; + }} + /> + +
+
+
Dieser Bereich wird später konkretisiert (Templates, Textbausteine, Scheduling etc.).
diff --git a/src/features/event/components/ScheduleSection.tsx b/src/features/event/components/ScheduleSection.tsx index ada330a..38166c5 100644 --- a/src/features/event/components/ScheduleSection.tsx +++ b/src/features/event/components/ScheduleSection.tsx @@ -1,10 +1,79 @@ import React from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; -const ScheduleSection: React.FC = () => { +type Props = { + promotionOffsetDays: string; + setPromotionOffsetDays: (v: string) => void; + promotionStartDate: string; + handlePromotionStartChange: (iso: string) => void; + promotionStartRef: React.RefObject; + formatDateForDisplay: (iso: string) => string; +}; + +const ScheduleSection: React.FC = ({ + promotionOffsetDays, + setPromotionOffsetDays, + promotionStartDate, + handlePromotionStartChange, + promotionStartRef, + formatDateForDisplay, +}) => { return (
-
Zeitplan
-
Hier kommt später alles rund um Scheduling / Zeitplan rein.
+
+
Zeitplan
+
+ Letzter Post vor Event + setPromotionOffsetDays(e.target.value)} + /> + Tage +
+
+ +
+
Promotion Start (Event Promo)
+
+
+ {formatDateForDisplay(promotionStartDate) || 'Noch kein Datum gewählt'} + +
+ handlePromotionStartChange(e.target.value)} + required + /> +
+ +
+ Dieses Datum ist der Start für die Event-Promo (Slot 1). Die restlichen Promo-Slots werden daraus abgeleitet. +
+
); }; diff --git a/src/index.css b/src/index.css index 99d67ba..3c81491 100644 --- a/src/index.css +++ b/src/index.css @@ -31,8 +31,8 @@ --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; + --border: 214 22% 86%; + --input: 214 22% 86%; --ring: 200 100% 45%; --radius: 0.5rem; @@ -43,7 +43,7 @@ --sidebar-primary-foreground: 210 40% 98%; --sidebar-accent: 210 40% 96.1%; --sidebar-accent-foreground: 220 40% 15%; - --sidebar-border: 214.3 31.8% 91.4%; + --sidebar-border: 214 22% 86%; --sidebar-ring: 200 100% 45%; } @@ -72,8 +72,8 @@ --destructive: 0 84.2% 62%; --destructive-foreground: 210 40% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; + --border: 215 20% 28%; + --input: 215 20% 28%; --ring: 200 100% 45%; --sidebar-background: 220 40% 17%; @@ -82,7 +82,7 @@ --sidebar-primary-foreground: 210 40% 98%; --sidebar-accent: 217.2 32.6% 17.5%; --sidebar-accent-foreground: 210 40% 98%; - --sidebar-border: 217.2 32.6% 17.5%; + --sidebar-border: 215 20% 28%; --sidebar-ring: 200 100% 45%; } }