fix duplicate updates
This commit is contained in:
parent
2712522bf6
commit
ee49347ff0
1 changed files with 565 additions and 449 deletions
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useCallback, useMemo, memo } from "react";
|
||||
import { Get } from "../../pocketbase/Get";
|
||||
import { Authentication } from "../../pocketbase/Authentication";
|
||||
import { Update } from "../../pocketbase/Update";
|
||||
|
@ -39,257 +39,51 @@ interface EventEditorProps {
|
|||
onEventSaved?: () => void;
|
||||
}
|
||||
|
||||
export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
||||
// State for form data and UI
|
||||
const [event, setEvent] = useState<Event | null>(null);
|
||||
const [previewUrl, setPreviewUrl] = useState("");
|
||||
const [previewFilename, setPreviewFilename] = useState("");
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
const [selectedFiles, setSelectedFiles] = useState<Map<string, File>>(new Map());
|
||||
const [filesToDelete, setFilesToDelete] = useState<Set<string>>(new Set());
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
// Memoize the FilePreview component
|
||||
const MemoizedFilePreview = memo(FilePreview);
|
||||
|
||||
// Initialize services
|
||||
const get = Get.getInstance();
|
||||
const auth = Authentication.getInstance();
|
||||
const update = Update.getInstance();
|
||||
const fileManager = FileManager.getInstance();
|
||||
const sendLog = SendLog.getInstance();
|
||||
|
||||
// Method to initialize form with event data
|
||||
const initializeEventData = async (eventId: string) => {
|
||||
try {
|
||||
const eventData = await get.getOne<Event>("events", eventId);
|
||||
setEvent(eventData);
|
||||
setSelectedFiles(new Map());
|
||||
setFilesToDelete(new Set());
|
||||
setShowPreview(false);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch event data:", error);
|
||||
alert("Failed to load event data. Please try again.");
|
||||
}
|
||||
};
|
||||
|
||||
// Expose initializeEventData to window
|
||||
useEffect(() => {
|
||||
(window as any).openEditModal = async (event?: Event) => {
|
||||
const modal = document.getElementById("editEventModal") as HTMLDialogElement;
|
||||
if (!modal) return;
|
||||
|
||||
if (event?.id) {
|
||||
await initializeEventData(event.id);
|
||||
} else {
|
||||
setEvent(null);
|
||||
setSelectedFiles(new Map());
|
||||
setFilesToDelete(new Set());
|
||||
setShowPreview(false);
|
||||
}
|
||||
|
||||
modal.showModal();
|
||||
};
|
||||
|
||||
return () => {
|
||||
delete (window as any).openEditModal;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Handle file selection
|
||||
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.files) {
|
||||
const newFiles = Array.from(e.target.files);
|
||||
const updatedFiles = new Map(selectedFiles);
|
||||
|
||||
newFiles.forEach(file => {
|
||||
updatedFiles.set(file.name, file);
|
||||
});
|
||||
|
||||
setSelectedFiles(updatedFiles);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle file deletion
|
||||
const handleFileDelete = (filename: string) => {
|
||||
if (confirm("Are you sure you want to remove this file?")) {
|
||||
const updatedFilesToDelete = new Set(filesToDelete);
|
||||
updatedFilesToDelete.add(filename);
|
||||
setFilesToDelete(updatedFilesToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle file deletion undo
|
||||
const handleUndoFileDelete = (filename: string) => {
|
||||
const updatedFilesToDelete = new Set(filesToDelete);
|
||||
updatedFilesToDelete.delete(filename);
|
||||
setFilesToDelete(updatedFilesToDelete);
|
||||
};
|
||||
|
||||
// Handle file preview
|
||||
const handlePreviewFile = (url: string, filename: string) => {
|
||||
setPreviewUrl(url);
|
||||
setPreviewFilename(filename);
|
||||
setShowPreview(true);
|
||||
};
|
||||
|
||||
// Handle form submission
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const form = e.target as HTMLFormElement;
|
||||
const formData = new FormData(form);
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
window.showLoading?.();
|
||||
const eventData = {
|
||||
event_name: formData.get("editEventName"),
|
||||
event_code: formData.get("editEventCode"),
|
||||
event_description: formData.get("editEventDescription"),
|
||||
location: formData.get("editEventLocation"),
|
||||
points_to_reward: Number(formData.get("editEventPoints")),
|
||||
start_date: new Date(formData.get("editEventStartDate") as string).toISOString(),
|
||||
end_date: new Date(formData.get("editEventEndDate") as string).toISOString(),
|
||||
published: formData.get("editEventPublished") === "on",
|
||||
has_food: formData.get("editEventHasFood") === "on",
|
||||
};
|
||||
|
||||
const pb = auth.getPocketBase();
|
||||
|
||||
if (event?.id) {
|
||||
// Update existing event
|
||||
const currentEvent = await pb.collection("events").getOne(event.id);
|
||||
const currentFiles = currentEvent.files || [];
|
||||
|
||||
// Filter out files marked for deletion
|
||||
const remainingFiles = currentFiles.filter(
|
||||
(filename: string) => !filesToDelete.has(filename)
|
||||
);
|
||||
|
||||
// Create FormData for update
|
||||
const updateFormData = new FormData();
|
||||
|
||||
// Add event data
|
||||
Object.entries(eventData).forEach(([key, value]) => {
|
||||
updateFormData.append(key, String(value));
|
||||
});
|
||||
|
||||
// Add remaining and new files
|
||||
const filePromises = remainingFiles.map(async (filename: string) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
fileManager.getFileUrl("events", event.id, filename)
|
||||
);
|
||||
const blob = await response.blob();
|
||||
return new File([blob], filename, { type: blob.type });
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch file ${filename}:`, error);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const existingFiles = (await Promise.all(filePromises)).filter(
|
||||
(file): file is File => file !== null
|
||||
);
|
||||
|
||||
[...existingFiles, ...Array.from(selectedFiles.values())].forEach(
|
||||
(file: File) => {
|
||||
updateFormData.append("files", file);
|
||||
}
|
||||
);
|
||||
|
||||
await pb.collection("events").update(event.id, updateFormData);
|
||||
await sendLog.send(
|
||||
"update",
|
||||
"event",
|
||||
`Updated event: ${eventData.event_name}`
|
||||
);
|
||||
|
||||
// Log file deletions
|
||||
for (const filename of filesToDelete) {
|
||||
await sendLog.send(
|
||||
"delete",
|
||||
"event_file",
|
||||
`Deleted file ${filename} from event ${eventData.event_name}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Create new event
|
||||
const createFormData = new FormData();
|
||||
|
||||
// Add event data
|
||||
Object.entries(eventData).forEach(([key, value]) => {
|
||||
createFormData.append(key, String(value));
|
||||
});
|
||||
|
||||
// Initialize empty attendees array
|
||||
createFormData.append("attendees", JSON.stringify([]));
|
||||
|
||||
// Add new files
|
||||
Array.from(selectedFiles.values()).forEach((file: File) => {
|
||||
createFormData.append("files", file);
|
||||
});
|
||||
|
||||
await pb.collection("events").create(createFormData);
|
||||
await sendLog.send(
|
||||
"create",
|
||||
"event",
|
||||
`Created event: ${eventData.event_name}`
|
||||
);
|
||||
}
|
||||
|
||||
// Close modal and reset state
|
||||
const modal = document.getElementById("editEventModal") as HTMLDialogElement;
|
||||
if (modal) modal.close();
|
||||
|
||||
setEvent(null);
|
||||
setSelectedFiles(new Map());
|
||||
setFilesToDelete(new Set());
|
||||
setShowPreview(false);
|
||||
form.reset();
|
||||
|
||||
// Notify parent component
|
||||
onEventSaved?.();
|
||||
|
||||
} catch (error) {
|
||||
console.error("Failed to save event:", error);
|
||||
alert("Failed to save event. Please try again.");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
window.hideLoading?.();
|
||||
}
|
||||
};
|
||||
// Define EventForm props interface
|
||||
interface EventFormProps {
|
||||
event: Event | null;
|
||||
setEvent: React.Dispatch<React.SetStateAction<Event | null>>;
|
||||
selectedFiles: Map<string, File>;
|
||||
setSelectedFiles: React.Dispatch<React.SetStateAction<Map<string, File>>>;
|
||||
filesToDelete: Set<string>;
|
||||
setFilesToDelete: React.Dispatch<React.SetStateAction<Set<string>>>;
|
||||
handlePreviewFile: (url: string, filename: string) => void;
|
||||
isSubmitting: boolean;
|
||||
fileManager: FileManager;
|
||||
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
|
||||
}
|
||||
|
||||
// Create a memoized form component
|
||||
const EventForm = memo(({
|
||||
event,
|
||||
setEvent,
|
||||
selectedFiles,
|
||||
setSelectedFiles,
|
||||
filesToDelete,
|
||||
setFilesToDelete,
|
||||
handlePreviewFile,
|
||||
isSubmitting,
|
||||
fileManager,
|
||||
onSubmit
|
||||
}: EventFormProps): React.ReactElement => {
|
||||
return (
|
||||
<dialog id="editEventModal" className="modal">
|
||||
{showPreview ? (
|
||||
<div className="modal-box max-w-4xl">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
className="btn btn-ghost btn-sm"
|
||||
onClick={() => setShowPreview(false)}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<h3 className="font-bold text-lg truncate">
|
||||
{previewFilename}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<FilePreview
|
||||
url={previewUrl}
|
||||
filename={previewFilename}
|
||||
isModal={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="modal-box max-w-2xl">
|
||||
{/* Main Edit Form Section */}
|
||||
<div id="editFormSection">
|
||||
<h3 className="font-bold text-lg mb-4" id="editModalTitle">
|
||||
{event?.id ? 'Edit Event' : 'Add New Event'}
|
||||
</h3>
|
||||
<form id="editEventForm" onSubmit={handleSubmit} className="space-y-4">
|
||||
<form
|
||||
id="editEventForm"
|
||||
className="space-y-4"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
if (!isSubmitting) {
|
||||
onSubmit(e);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<input type="hidden" id="editEventId" name="editEventId" value={event?.id || ''} />
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Event Name */}
|
||||
|
@ -413,7 +207,16 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
</label>
|
||||
<input
|
||||
type="file"
|
||||
onChange={handleFileSelect}
|
||||
name="editEventFiles"
|
||||
onChange={(e) => {
|
||||
if (e.target.files) {
|
||||
const newFiles = new Map(selectedFiles);
|
||||
Array.from(e.target.files).forEach(file => {
|
||||
newFiles.set(file.name, file);
|
||||
});
|
||||
setSelectedFiles(newFiles);
|
||||
}
|
||||
}}
|
||||
className="file-input file-input-bordered"
|
||||
multiple
|
||||
/>
|
||||
|
@ -471,7 +274,11 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
<button
|
||||
type="button"
|
||||
className="btn btn-ghost btn-xs"
|
||||
onClick={() => handleUndoFileDelete(filename)}
|
||||
onClick={() => {
|
||||
const newFilesToDelete = new Set(filesToDelete);
|
||||
newFilesToDelete.delete(filename);
|
||||
setFilesToDelete(newFilesToDelete);
|
||||
}}
|
||||
>
|
||||
Undo
|
||||
</button>
|
||||
|
@ -479,7 +286,11 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
<button
|
||||
type="button"
|
||||
className="btn btn-ghost btn-xs text-error"
|
||||
onClick={() => handleFileDelete(filename)}
|
||||
onClick={() => {
|
||||
const newFilesToDelete = new Set(filesToDelete);
|
||||
newFilesToDelete.add(filename);
|
||||
setFilesToDelete(newFilesToDelete);
|
||||
}}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule="evenodd" />
|
||||
|
@ -533,14 +344,12 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="modal-action">
|
||||
{/* Action Buttons */}
|
||||
<div className="modal-action mt-6">
|
||||
<button
|
||||
type="submit"
|
||||
className={`btn btn-primary ${isSubmitting ? 'loading' : ''}`}
|
||||
form="editEventForm"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? 'Saving...' : 'Save Changes'}
|
||||
|
@ -552,15 +361,322 @@ export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
|||
const modal = document.getElementById("editEventModal") as HTMLDialogElement;
|
||||
if (modal) modal.close();
|
||||
}}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default function EventEditor({ onEventSaved }: EventEditorProps) {
|
||||
// State for form data and UI
|
||||
const [event, setEvent] = useState<Event | null>(null);
|
||||
const [previewUrl, setPreviewUrl] = useState("");
|
||||
const [previewFilename, setPreviewFilename] = useState("");
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
const [selectedFiles, setSelectedFiles] = useState<Map<string, File>>(new Map());
|
||||
const [filesToDelete, setFilesToDelete] = useState<Set<string>>(new Set());
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Memoize service instances
|
||||
const services = useMemo(() => ({
|
||||
get: Get.getInstance(),
|
||||
auth: Authentication.getInstance(),
|
||||
update: Update.getInstance(),
|
||||
fileManager: FileManager.getInstance(),
|
||||
sendLog: SendLog.getInstance()
|
||||
}), []);
|
||||
|
||||
// Memoize handlers
|
||||
const handleFileSelect = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.files) {
|
||||
setSelectedFiles(prev => {
|
||||
const newFiles = new Map(prev);
|
||||
Array.from(e.target.files!).forEach(file => {
|
||||
newFiles.set(file.name, file);
|
||||
});
|
||||
return newFiles;
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleFileDelete = useCallback((filename: string) => {
|
||||
if (confirm("Are you sure you want to remove this file?")) {
|
||||
setFilesToDelete(prev => new Set([...prev, filename]));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleUndoFileDelete = useCallback((filename: string) => {
|
||||
setFilesToDelete(prev => {
|
||||
const newSet = new Set(prev);
|
||||
newSet.delete(filename);
|
||||
return newSet;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handlePreviewFile = useCallback((url: string, filename: string) => {
|
||||
setPreviewUrl(url);
|
||||
setPreviewFilename(filename);
|
||||
setShowPreview(true);
|
||||
}, []);
|
||||
|
||||
// Optimize form submission
|
||||
const handleSubmit = useCallback(async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (isSubmitting) return; // Early return if already submitting
|
||||
|
||||
const form = e.target as HTMLFormElement;
|
||||
const formData = new FormData(form);
|
||||
const submitButton = form.querySelector('button[type="submit"]') as HTMLButtonElement;
|
||||
const cancelButton = form.querySelector('button[type="button"]') as HTMLButtonElement;
|
||||
|
||||
setIsSubmitting(true);
|
||||
if (submitButton) submitButton.disabled = true;
|
||||
if (cancelButton) cancelButton.disabled = true;
|
||||
|
||||
try {
|
||||
window.showLoading?.();
|
||||
const eventData = {
|
||||
event_name: formData.get("editEventName"),
|
||||
event_code: formData.get("editEventCode"),
|
||||
event_description: formData.get("editEventDescription"),
|
||||
location: formData.get("editEventLocation"),
|
||||
points_to_reward: Number(formData.get("editEventPoints")),
|
||||
start_date: new Date(formData.get("editEventStartDate") as string).toISOString(),
|
||||
end_date: new Date(formData.get("editEventEndDate") as string).toISOString(),
|
||||
published: formData.get("editEventPublished") === "on",
|
||||
has_food: formData.get("editEventHasFood") === "on",
|
||||
};
|
||||
|
||||
const pb = services.auth.getPocketBase();
|
||||
|
||||
if (event?.id) {
|
||||
// Optimize update by fetching files in parallel
|
||||
const currentEvent = await pb.collection("events").getOne(event.id);
|
||||
const currentFiles = currentEvent.files || [];
|
||||
const remainingFiles = currentFiles.filter(
|
||||
(filename: string) => !filesToDelete.has(filename)
|
||||
);
|
||||
|
||||
const updateFormData = new FormData();
|
||||
Object.entries(eventData).forEach(([key, value]) => {
|
||||
updateFormData.append(key, String(value));
|
||||
});
|
||||
|
||||
// Fetch all remaining files in parallel
|
||||
const filePromises = remainingFiles.map(async (filename: string) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
services.fileManager.getFileUrl("events", event.id, filename)
|
||||
);
|
||||
const blob = await response.blob();
|
||||
return new File([blob], filename, { type: blob.type });
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch file ${filename}:`, error);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const existingFiles = (await Promise.all(filePromises)).filter(
|
||||
(file): file is File => file !== null
|
||||
);
|
||||
|
||||
[...existingFiles, ...Array.from(selectedFiles.values())].forEach(
|
||||
(file: File) => {
|
||||
updateFormData.append("files", file);
|
||||
}
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
pb.collection("events").update(event.id, updateFormData),
|
||||
services.sendLog.send(
|
||||
"update",
|
||||
"event",
|
||||
`Updated event: ${eventData.event_name}`
|
||||
),
|
||||
...Array.from(filesToDelete).map(filename =>
|
||||
services.sendLog.send(
|
||||
"delete",
|
||||
"event_file",
|
||||
`Deleted file ${filename} from event ${eventData.event_name}`
|
||||
)
|
||||
)
|
||||
]);
|
||||
} else {
|
||||
const createFormData = new FormData();
|
||||
Object.entries(eventData).forEach(([key, value]) => {
|
||||
createFormData.append(key, String(value));
|
||||
});
|
||||
createFormData.append("attendees", JSON.stringify([]));
|
||||
Array.from(selectedFiles.values()).forEach((file: File) => {
|
||||
createFormData.append("files", file);
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
pb.collection("events").create(createFormData),
|
||||
services.sendLog.send(
|
||||
"create",
|
||||
"event",
|
||||
`Created event: ${eventData.event_name}`
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
// Show success state briefly
|
||||
if (submitButton) {
|
||||
submitButton.classList.remove("btn-disabled");
|
||||
submitButton.classList.add("btn-success");
|
||||
submitButton.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
`;
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
// Reset form and close modal
|
||||
setEvent(null);
|
||||
setSelectedFiles(new Map());
|
||||
setFilesToDelete(new Set());
|
||||
setShowPreview(false);
|
||||
form.reset();
|
||||
|
||||
// Call onEventSaved before closing modal
|
||||
onEventSaved?.();
|
||||
|
||||
// Close modal last
|
||||
const modal = document.getElementById("editEventModal") as HTMLDialogElement;
|
||||
if (modal) {
|
||||
modal.addEventListener('close', () => {
|
||||
// Do nothing on close event
|
||||
}, { once: true });
|
||||
modal.close();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to save event:", error);
|
||||
if (submitButton) {
|
||||
submitButton.classList.remove("btn-disabled");
|
||||
submitButton.classList.add("btn-error");
|
||||
submitButton.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
`;
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
}
|
||||
alert("Failed to save event. Please try again.");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
if (submitButton) {
|
||||
submitButton.disabled = false;
|
||||
submitButton.classList.remove("btn-disabled", "btn-success", "btn-error");
|
||||
submitButton.innerHTML = 'Save Changes';
|
||||
}
|
||||
if (cancelButton) cancelButton.disabled = false;
|
||||
window.hideLoading?.();
|
||||
}
|
||||
}, [event, selectedFiles, filesToDelete, services, onEventSaved, isSubmitting]);
|
||||
|
||||
// Method to initialize form with event data
|
||||
const initializeEventData = async (eventId: string) => {
|
||||
try {
|
||||
const eventData = await services.get.getOne<Event>("events", eventId);
|
||||
setEvent(eventData);
|
||||
setSelectedFiles(new Map());
|
||||
setFilesToDelete(new Set());
|
||||
setShowPreview(false);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch event data:", error);
|
||||
alert("Failed to load event data. Please try again.");
|
||||
}
|
||||
};
|
||||
|
||||
// Expose initializeEventData to window
|
||||
useEffect(() => {
|
||||
(window as any).openEditModal = async (event?: Event) => {
|
||||
const modal = document.getElementById("editEventModal") as HTMLDialogElement;
|
||||
if (!modal) return;
|
||||
|
||||
if (event?.id) {
|
||||
await initializeEventData(event.id);
|
||||
} else {
|
||||
setEvent(null);
|
||||
setSelectedFiles(new Map());
|
||||
setFilesToDelete(new Set());
|
||||
setShowPreview(false);
|
||||
}
|
||||
|
||||
modal.showModal();
|
||||
};
|
||||
|
||||
return () => {
|
||||
delete (window as any).openEditModal;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<dialog id="editEventModal" className="modal" onClose={(e) => {
|
||||
// Prevent any default close behavior
|
||||
if (isSubmitting) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}}>
|
||||
{showPreview ? (
|
||||
<div className="modal-box max-w-4xl">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
className="btn btn-ghost btn-sm"
|
||||
onClick={() => setShowPreview(false)}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<h3 className="font-bold text-lg truncate">
|
||||
{previewFilename}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<MemoizedFilePreview
|
||||
url={previewUrl}
|
||||
filename={previewFilename}
|
||||
isModal={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="modal-box max-w-2xl">
|
||||
<EventForm
|
||||
event={event}
|
||||
setEvent={setEvent}
|
||||
selectedFiles={selectedFiles}
|
||||
setSelectedFiles={setSelectedFiles}
|
||||
filesToDelete={filesToDelete}
|
||||
setFilesToDelete={setFilesToDelete}
|
||||
handlePreviewFile={handlePreviewFile}
|
||||
isSubmitting={isSubmitting}
|
||||
fileManager={services.fileManager}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<form method="dialog" className="modal-backdrop">
|
||||
<button>close</button>
|
||||
</form>
|
||||
<div className="modal-backdrop" onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (!isSubmitting) {
|
||||
const modal = document.getElementById("editEventModal") as HTMLDialogElement;
|
||||
if (modal) {
|
||||
modal.addEventListener('close', () => {
|
||||
// Do nothing on close event
|
||||
}, { once: true });
|
||||
modal.close();
|
||||
}
|
||||
}
|
||||
}}>
|
||||
<button onClick={(e) => e.preventDefault()}>close</button>
|
||||
</div>
|
||||
</dialog>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue