fix: Guard EventEditorPage state initialization with loadedEventIdRef and key prop to prevent values resetting to defaults on edit

This commit is contained in:
Richard
2026-07-31 12:20:37 +02:00
parent c295471827
commit 1a880d3452
3 changed files with 65 additions and 55 deletions
+1 -1
View File
@@ -11,7 +11,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<title>Schichtplaner — Veranstaltungsschichtpläne</title> <title>Schichtplaner — Veranstaltungsschichtpläne</title>
<script type="module" crossorigin src="/assets/index-udMfIy9d.js"></script> <script type="module" crossorigin src="/assets/index-Mp9FLWF3.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B6pC8vGA.css"> <link rel="stylesheet" crossorigin href="/assets/index-B6pC8vGA.css">
</head> </head>
<body class="bg-paper text-main font-sans antialiased min-h-screen"> <body class="bg-paper text-main font-sans antialiased min-h-screen">
+1
View File
@@ -490,6 +490,7 @@ export default function App() {
/> />
) : activeTab === 'event-editor' ? ( ) : activeTab === 'event-editor' ? (
<EventEditorPage <EventEditorPage
key={eventToEdit?.id || 'new-event'}
eventToEdit={eventToEdit} eventToEdit={eventToEdit}
skills={skills} skills={skills}
user={user} user={user}
+63 -54
View File
@@ -1,10 +1,12 @@
/* Hallmark · pre-emit critique: P5 H5 E5 S5 R5 V5 */ /* Hallmark · pre-emit critique: P5 H5 E5 S5 R5 V5 */
/* Hallmark · component: EventEditorPage · genre: editorial · theme: Atelier (Dark & Light) · studied-DNA: https://inihaus.de */ /* Hallmark · component: EventEditorPage · genre: editorial · theme: Atelier (Dark & Light) · studied-DNA: https://inihaus.de */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { ArrowLeft, Calendar, Layers, Plus, Trash2, Edit3, CheckCircle2, AlertCircle, Users, UserCheck } from 'lucide-react'; import { ArrowLeft, Calendar, Layers, Plus, Trash2, Edit3, CheckCircle2, AlertCircle, Users, UserCheck } from 'lucide-react';
import { apiFetch } from '../api/client'; import { apiFetch } from '../api/client';
export default function EventEditorPage({ eventToEdit, skills, user, onBack, onSubmit, onDeleteEvent }) { export default function EventEditorPage({ eventToEdit, skills, user, onBack, onSubmit, onDeleteEvent }) {
const loadedEventIdRef = useRef(undefined);
const [eventId, setEventId] = useState(eventToEdit?.id || null); const [eventId, setEventId] = useState(eventToEdit?.id || null);
const [title, setTitle] = useState(eventToEdit?.title || ''); const [title, setTitle] = useState(eventToEdit?.title || '');
const [description, setDescription] = useState(eventToEdit?.description || ''); const [description, setDescription] = useState(eventToEdit?.description || '');
@@ -26,61 +28,68 @@ export default function EventEditorPage({ eventToEdit, skills, user, onBack, onS
useEffect(() => { useEffect(() => {
fetchUsers(); fetchUsers();
if (eventToEdit) { const currentId = eventToEdit?.id || null;
setEventId(eventToEdit.id);
setTitle(eventToEdit.title || '');
setDescription(eventToEdit.description || '');
setLocation(eventToEdit.location || '');
setStartDate(eventToEdit.start_date || new Date().toISOString().split('T')[0]);
setEndDate(eventToEdit.end_date || new Date().toISOString().split('T')[0]);
setCoManagerIds(
eventToEdit.co_managers ? eventToEdit.co_managers.map(u => (typeof u === 'object' ? u.id : u)) : []
);
if (eventToEdit.task_areas && eventToEdit.task_areas.length > 0) { // Only populate form fields if eventId changed or initial load
const formattedAreas = eventToEdit.task_areas.map(ta => ({ if (loadedEventIdRef.current !== currentId) {
id: ta.id, loadedEventIdRef.current = currentId;
name: ta.name,
description: ta.description || '', if (eventToEdit) {
shifts: (ta.shifts || []).map(s => ({ setEventId(eventToEdit.id);
id: s.id, setTitle(eventToEdit.title || '');
title: s.title, setDescription(eventToEdit.description || '');
date: s.date || eventToEdit.start_date, setLocation(eventToEdit.location || '');
start_time: s.start_time, setStartDate(eventToEdit.start_date || new Date().toISOString().split('T')[0]);
end_time: s.end_time, setEndDate(eventToEdit.end_date || new Date().toISOString().split('T')[0]);
max_participants: s.max_participants || 1, setCoManagerIds(
required_skill_ids: s.required_skills ? s.required_skills.map(sk => (typeof sk === 'object' ? sk.id : sk)) : [] eventToEdit.co_managers ? eventToEdit.co_managers.map(u => (typeof u === 'object' ? u.id : u)) : []
})) );
}));
setTaskAreas(formattedAreas); if (eventToEdit.task_areas && eventToEdit.task_areas.length > 0) {
} else { const formattedAreas = eventToEdit.task_areas.map(ta => ({
setTaskAreas([]); id: ta.id,
} name: ta.name,
} else { description: ta.description || '',
setEventId(null); shifts: (ta.shifts || []).map(s => ({
setTitle(''); id: s.id,
setDescription(''); title: s.title,
setLocation(''); date: s.date || eventToEdit.start_date,
setStartDate(new Date().toISOString().split('T')[0]); start_time: s.start_time,
setEndDate(new Date().toISOString().split('T')[0]); end_time: s.end_time,
setCoManagerIds([]); max_participants: s.max_participants || 1,
setTaskAreas([ required_skill_ids: s.required_skills ? s.required_skills.map(sk => (typeof sk === 'object' ? sk.id : sk)) : []
{ }))
name: 'Tresendienst', }));
description: 'Getränke- und Barverkauf', setTaskAreas(formattedAreas);
shifts: [{ title: 'Schicht 1', start_time: '18:00', end_time: '22:00', max_participants: 2, required_skill_ids: [] }] } else {
}, setTaskAreas([]);
{
name: 'Essen kochen',
description: 'Zubereitung von Speisen',
shifts: [{ title: 'Frühschicht', start_time: '15:00', end_time: '19:00', max_participants: 3, required_skill_ids: [] }]
},
{
name: 'Aufbau & Aufräumen',
description: 'Tische, Stühle & Technik',
shifts: [{ title: 'Aufbau', start_time: '12:00', end_time: '15:00', max_participants: 4, required_skill_ids: [] }]
} }
]); } else {
setEventId(null);
setTitle('');
setDescription('');
setLocation('');
setStartDate(new Date().toISOString().split('T')[0]);
setEndDate(new Date().toISOString().split('T')[0]);
setCoManagerIds([]);
setTaskAreas([
{
name: 'Tresendienst',
description: 'Getränke- und Barverkauf',
shifts: [{ title: 'Schicht 1', start_time: '18:00', end_time: '22:00', max_participants: 2, required_skill_ids: [] }]
},
{
name: 'Essen kochen',
description: 'Zubereitung von Speisen',
shifts: [{ title: 'Frühschicht', start_time: '15:00', end_time: '19:00', max_participants: 3, required_skill_ids: [] }]
},
{
name: 'Aufbau & Aufräumen',
description: 'Tische, Stühle & Technik',
shifts: [{ title: 'Aufbau', start_time: '12:00', end_time: '15:00', max_participants: 4, required_skill_ids: [] }]
}
]);
}
} }
}, [eventToEdit]); }, [eventToEdit]);