mobile desigh test4
#deploy
This commit is contained in:
parent
543f6de552
commit
6a0381ff99
@ -1,10 +1,12 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Card, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
import { type Event } from '@/features/event/api/eventService';
|
||||
import { type DashboardModule, type DashboardModuleContext } from '@/components/dashboard/dashboardModuleTypes';
|
||||
import { eventDashboardModule } from '@/features/event/dashboardModule';
|
||||
import { quickPostDashboardModule } from '@/features/photopost/dashboardModule';
|
||||
|
||||
const DASHBOARD_ACTIVE_MODULE_KEY = 'kgb.dashboard.activeModuleId';
|
||||
|
||||
interface DashboardCardProps {
|
||||
title: string;
|
||||
description: string;
|
||||
@ -49,7 +51,25 @@ interface DashboardProps {
|
||||
}
|
||||
|
||||
const Dashboard: React.FC<DashboardProps> = ({ onActionClick, hasActiveEvent, currentEvent }) => {
|
||||
const [activeModuleId, setActiveModuleId] = useState<string | null>(null);
|
||||
const [activeModuleId, setActiveModuleId] = useState<string | null>(() => {
|
||||
try {
|
||||
return sessionStorage.getItem(DASHBOARD_ACTIVE_MODULE_KEY);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (activeModuleId) {
|
||||
sessionStorage.setItem(DASHBOARD_ACTIVE_MODULE_KEY, activeModuleId);
|
||||
} else {
|
||||
sessionStorage.removeItem(DASHBOARD_ACTIVE_MODULE_KEY);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, [activeModuleId]);
|
||||
|
||||
const ctx: DashboardModuleContext = {
|
||||
currentEvent,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
@ -36,6 +36,7 @@ const EventFormInline: React.FC<EventFormInlineProps> = ({ onSuccess, onCancel,
|
||||
const [form, setForm] = useState(initialForm);
|
||||
const [mediaPreview, setMediaPreview] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const fileInputRef = React.useRef<HTMLInputElement>(null);
|
||||
const [promotionStartDate, setPromotionStartDate] = useState<string>('');
|
||||
const [sectionDates, setSectionDates] = useState<string[]>(() => Array(INITIAL_ARTISTS + 1).fill(''));
|
||||
@ -65,6 +66,34 @@ const EventFormInline: React.FC<EventFormInlineProps> = ({ onSuccess, onCancel,
|
||||
const [activeTab, setActiveTab] = useState<'stammdaten' | 'promotion' | 'artists' | 'schedule'>('stammdaten');
|
||||
const navExpanded = !isMobile;
|
||||
|
||||
const initialSnapshotRef = useRef<string | null>(null);
|
||||
const [isDirty, setIsDirty] = useState(false);
|
||||
|
||||
const currentSnapshot = useMemo(() => {
|
||||
return JSON.stringify({
|
||||
form,
|
||||
promotionStartDate,
|
||||
promotionOffsetDays,
|
||||
sectionDates,
|
||||
artistSections,
|
||||
});
|
||||
}, [artistSections, form, promotionOffsetDays, promotionStartDate, sectionDates]);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialSnapshotRef.current === null) return;
|
||||
setIsDirty(initialSnapshotRef.current !== currentSnapshot);
|
||||
}, [currentSnapshot]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDirty) return;
|
||||
const handler = (e: BeforeUnloadEvent) => {
|
||||
e.preventDefault();
|
||||
e.returnValue = '';
|
||||
};
|
||||
window.addEventListener('beforeunload', handler);
|
||||
return () => window.removeEventListener('beforeunload', handler);
|
||||
}, [isDirty]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setForm(initialForm);
|
||||
@ -72,6 +101,22 @@ const EventFormInline: React.FC<EventFormInlineProps> = ({ onSuccess, onCancel,
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// establish baseline snapshot for create mode immediately
|
||||
if (mode !== 'create') return;
|
||||
if (initialSnapshotRef.current !== null) return;
|
||||
initialSnapshotRef.current = currentSnapshot;
|
||||
}, [currentSnapshot, mode]);
|
||||
|
||||
useEffect(() => {
|
||||
// establish baseline snapshot for edit mode after initial load finished
|
||||
if (mode !== 'edit') return;
|
||||
if (loadingInitial) return;
|
||||
if (initialSnapshotRef.current !== null) return;
|
||||
initialSnapshotRef.current = currentSnapshot;
|
||||
setIsDirty(false);
|
||||
}, [currentSnapshot, loadingInitial, mode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (mode !== 'edit' || !eventId) return;
|
||||
|
||||
@ -118,10 +163,13 @@ const EventFormInline: React.FC<EventFormInlineProps> = ({ onSuccess, onCancel,
|
||||
dates[0] = section1?.promoDate || start;
|
||||
|
||||
artistSlots.forEach((slot, idx) => {
|
||||
if (idx >= artists.length) return;
|
||||
artists[idx] = {
|
||||
name: slot.artistName || '',
|
||||
email: (slot as any).artistEmail || '',
|
||||
link: (slot as any).artistLink || '',
|
||||
status: (slot as any).artistStatus || 'Wartet auf Künstler',
|
||||
};
|
||||
dates[idx + 1] = slot.promoDate || '';
|
||||
artists[idx].name = slot.artistName || '';
|
||||
artists[idx].email = slot.artistInstagram || '';
|
||||
|
||||
const collabUrl = (slot as any).collabUrl || (slot as any).collab_url;
|
||||
const collabToken = (slot as any).collabToken || (slot as any).collab_token;
|
||||
@ -130,44 +178,19 @@ const EventFormInline: React.FC<EventFormInlineProps> = ({ onSuccess, onCancel,
|
||||
} else if (collabToken) {
|
||||
artists[idx].link = `/collab/${collabToken}`;
|
||||
}
|
||||
|
||||
const rawStatus = (slot as any).collab_status || (slot as any).collabStatus;
|
||||
const status = typeof rawStatus === 'string' ? rawStatus.toUpperCase() : '';
|
||||
|
||||
// Fallbacks für alte Daten, falls collab_status noch nicht gesetzt ist
|
||||
const hasArtistData = !!(slot as any).artist_description || !!(slot as any).artistDescription;
|
||||
const hasLink = !!collabUrl || !!collabToken;
|
||||
|
||||
if (status === 'APPROVED') {
|
||||
artists[idx].status = 'Fertig';
|
||||
} else if (status === 'UPDATED') {
|
||||
artists[idx].status = 'Fertig';
|
||||
} else if (status === 'SUBMITTED') {
|
||||
artists[idx].status = 'Fertig';
|
||||
} else if (status === 'INVITED') {
|
||||
artists[idx].status = 'Wartet auf Künstler';
|
||||
} else if (status === 'PENDING') {
|
||||
artists[idx].status = 'Künstler einladen';
|
||||
} else if (hasArtistData) {
|
||||
// Legacy: keine Status-Info, aber Beschreibung vorhanden
|
||||
artists[idx].status = 'Fertig';
|
||||
} else if (hasLink) {
|
||||
artists[idx].status = 'Link generiert';
|
||||
} else {
|
||||
artists[idx].status = 'Wartet auf Künstler';
|
||||
}
|
||||
});
|
||||
|
||||
setSectionDates(dates);
|
||||
setArtistSections(artists);
|
||||
} catch (e) {
|
||||
if (!cancelled) {
|
||||
|
||||
// reset baseline so it can be re-established after all state updates
|
||||
initialSnapshotRef.current = null;
|
||||
setIsDirty(false);
|
||||
} catch (err) {
|
||||
console.error('Error loading event details', err);
|
||||
setError('Event konnte nicht geladen werden');
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setLoadingInitial(false);
|
||||
}
|
||||
if (!cancelled) setLoadingInitial(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -323,6 +346,7 @@ const EventFormInline: React.FC<EventFormInlineProps> = ({ onSuccess, onCancel,
|
||||
}
|
||||
|
||||
setError(null);
|
||||
setSaving(true);
|
||||
|
||||
const payload = {
|
||||
title: form.name,
|
||||
@ -356,10 +380,16 @@ const EventFormInline: React.FC<EventFormInlineProps> = ({ onSuccess, onCancel,
|
||||
} else {
|
||||
await createEvent(payload);
|
||||
}
|
||||
onSuccess?.();
|
||||
onSuccess();
|
||||
|
||||
// mark clean after successful save
|
||||
initialSnapshotRef.current = currentSnapshot;
|
||||
setIsDirty(false);
|
||||
} catch (err) {
|
||||
console.error('Error while saving event', err);
|
||||
setError('Event konnte nicht gespeichert werden');
|
||||
console.error('Error saving event:', err);
|
||||
setError('Speichern fehlgeschlagen');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -7,6 +7,8 @@ import { ArrowLeft, LayoutGrid } from 'lucide-react';
|
||||
import { useHeader } from '@/contexts/HeaderContext';
|
||||
import EventFormInline from './EventFormInline';
|
||||
|
||||
const EVENT_WIZARD_STATE_KEY = 'kgb.eventWizard.state';
|
||||
|
||||
interface EventWizardProps {
|
||||
onComplete: () => void;
|
||||
onBackToOverview: () => void;
|
||||
@ -21,6 +23,31 @@ const EventWizard: React.FC<EventWizardProps> = ({ onComplete, onBackToOverview,
|
||||
const [activeEventId, setActiveEventId] = useState<string | number | null>(null);
|
||||
const { setHeader, resetHeader } = useHeader();
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(EVENT_WIZARD_STATE_KEY);
|
||||
if (!raw) return;
|
||||
const parsed = JSON.parse(raw) as { showForm?: boolean; activeEventId?: string | number | null };
|
||||
if (typeof parsed.showForm === 'boolean') setShowForm(parsed.showForm);
|
||||
if (parsed.activeEventId !== undefined) setActiveEventId(parsed.activeEventId);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
// only on mount
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
sessionStorage.setItem(
|
||||
EVENT_WIZARD_STATE_KEY,
|
||||
JSON.stringify({ showForm, activeEventId: activeEventId ?? null })
|
||||
);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, [activeEventId, showForm]);
|
||||
|
||||
const activeEvent = useMemo(() => {
|
||||
if (!activeEventId) return null;
|
||||
return events.find((e) => String(e.id) === String(activeEventId)) || null;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user