diff --git a/src/features/event/components/ArtistsSection.tsx b/src/features/event/components/ArtistsSection.tsx new file mode 100644 index 0000000..15f98ee --- /dev/null +++ b/src/features/event/components/ArtistsSection.tsx @@ -0,0 +1,248 @@ +import React from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; + +type ArtistSection = { + name: string; + email: string; + link: string; + status: 'Wartet auf Künstler' | 'Link generiert' | 'Fertig' | 'Künstler einladen'; +}; + +type Props = { + mode: 'create' | 'edit'; + eventId?: string | number | null; + + artistSections: ArtistSection[]; + setArtistSections: React.Dispatch>; + + sectionDates: string[]; + setSectionDates: React.Dispatch>; + sectionDateRefs: React.MutableRefObject>; + + promotionSlots: any[] | null; + + generatingLinkIndex: number | null; + setGeneratingLinkIndex: React.Dispatch>; + + generateCollabLink: (eventId: string | number, slotIndex: number, artistName: string, artistEmail: string) => Promise; + + setError: React.Dispatch>; + formatDateForDisplay: (iso: string) => string; + + onOpenPreview: (slot: any) => void; +}; + +const ArtistsSection: React.FC = ({ + mode, + eventId, + artistSections, + setArtistSections, + sectionDates, + setSectionDates, + sectionDateRefs, + promotionSlots, + generatingLinkIndex, + setGeneratingLinkIndex, + generateCollabLink, + setError, + formatDateForDisplay, + onOpenPreview, +}) => { + return ( +
+ {artistSections.map((section, idx) => { + const slot = promotionSlots?.find((s) => s.slotIndex === idx + 2); + const hasArtistData = !!slot?.artistDescription || (Array.isArray(slot?.media) && slot.media.length > 0); + const canOpenPreview = mode === 'edit' && !!slot && hasArtistData; + + return ( +
+
+ {`Künstler ${idx + 1}`} + {artistSections[idx].status} +
+ +
+
+
+ {formatDateForDisplay(sectionDates[idx + 1]) || 'Noch kein Datum gewählt'} + +
+ (sectionDateRefs.current[idx + 1] = el)} + id={`section_date_${idx + 2}`} + type="date" + className="hidden" + value={sectionDates[idx + 1]} + onChange={(e) => { + const next = [...sectionDates]; + next[idx + 1] = e.target.value; + setSectionDates(next); + }} + /> +
+ +
+
+ + { + const next = [...artistSections]; + next[idx] = { ...next[idx], name: e.target.value }; + setArtistSections(next); + }} + /> +
+ +
+ + + {section.link && ( +
+ e.target.select()} /> + +
+ )} +
+ +
+ +
+
+
+
+ ); + })} + +
+ + +
+
+ ); +}; + +export default ArtistsSection; diff --git a/src/features/event/components/EventFormInline.tsx b/src/features/event/components/EventFormInline.tsx index 2349ad9..659c5f5 100644 --- a/src/features/event/components/EventFormInline.tsx +++ b/src/features/event/components/EventFormInline.tsx @@ -4,8 +4,13 @@ import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { CalendarDays, Megaphone, Users } from 'lucide-react'; import { createEvent, fetchEventById, updateEvent, getEventInfoFromUrl, generateCollabLink, updateArtistCollabDataByOp } from '@/features/event/api/eventService'; import { useApiFetch } from '@/utils/apiFetch'; +import { useIsMobile } from '@/hooks/use-mobile'; +import EventStammdatenSection from '@/features/event/components/EventStammdatenSection'; +import PromotionTemplatesSection from '@/features/event/components/PromotionTemplatesSection'; +import ArtistsSection from '@/features/event/components/ArtistsSection'; interface EventFormInlineProps { onSuccess: () => void; @@ -55,6 +60,9 @@ const EventFormInline: React.FC = ({ onSuccess, onCancel, const [extractingFromUrl, setExtractingFromUrl] = useState(false); const [promotionOffsetDays, setPromotionOffsetDays] = useState('2'); const [generatingLinkIndex, setGeneratingLinkIndex] = useState(null); + const isMobile = useIsMobile(); + const [activeTab, setActiveTab] = useState<'stammdaten' | 'promotion' | 'artists'>('stammdaten'); + const navExpanded = !isMobile; useEffect(() => { return () => { @@ -469,427 +477,155 @@ const EventFormInline: React.FC = ({ onSuccess, onCancel, return fields.some((key) => (previewDraft as any)[key] !== (previewSlot as any)[key]); })(); + const sidebarWidthClass = navExpanded ? 'w-56' : 'w-14'; + + const NavItemButton = ({ + tab, + label, + Icon, + }: { + tab: 'stammdaten' | 'promotion' | 'artists'; + label: string; + Icon: React.ComponentType<{ className?: string }>; + }) => { + const isActive = activeTab === tab; + return ( + + ); + }; + return (
-
- {/* Event-URL ganz oben */} -
- -
- - {mode === 'create' && ( - + +
+
+
+ {mode === 'edit' ? 'Event bearbeiten' : 'Event erstellen'} +
+
+ {loadingInitial ? 'Lade…' : ''} +
+
+ {error &&
{error}
} +
+ +
+ + +
+ {activeTab === 'stammdaten' && ( + + )} + + {activeTab === 'promotion' && ( + { + setForm((prev) => ({ ...prev, mediaUrl: url })); + setMediaPreview(url || null); + }} + fileInputRef={fileInputRef} + onFileSelected={(file) => { + const url = URL.createObjectURL(file); + setMediaPreview(url); + }} + mediaPreview={mediaPreview} + /> + )} + + {activeTab === 'artists' && ( + { + setPreviewSlot(slot); + setPreviewDraft(slot ? { ...slot } : null); + setIsPreviewEditing(false); + setIsPreviewOpen(true); + }} + /> )}
-
- - -
- -
- -