import React, { useState, useEffect, useRef } from 'react'; import { Area } from '../../entities/Area'; import { useToast } from '../Shared/ToastContext'; import { useTranslation } from 'react-i18next'; import DiscardChangesDialog from '../Shared/DiscardChangesDialog'; import { TrashIcon } from '@heroicons/react/24/outline'; interface AreaModalProps { isOpen: boolean; onClose: () => void; onSave: (areaData: Partial) => Promise; onDelete?: (areaUid: string) => Promise; area?: Area | null; } const AreaModal: React.FC = ({ isOpen, onClose, area, onSave, onDelete, }) => { const { t } = useTranslation(); const [formData, setFormData] = useState({ id: area?.id || 0, uid: area?.uid || '', name: area?.name || '', description: area?.description || '', }); const [error, setError] = useState(null); const modalRef = useRef(null); const nameInputRef = useRef(null); const [isSubmitting, setIsSubmitting] = useState(false); const [isClosing, setIsClosing] = useState(false); const [showDiscardDialog, setShowDiscardDialog] = useState(false); const { showSuccessToast, showErrorToast } = useToast(); useEffect(() => { if (isOpen) { setFormData({ id: area?.id || 0, uid: area?.uid || '', name: area?.name || '', description: area?.description || '', }); setError(null); // Auto-focus on the name input when modal opens setTimeout(() => { nameInputRef.current?.focus(); }, 100); } }, [isOpen, area]); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( modalRef.current && !modalRef.current.contains(event.target as Node) ) { handleClose(); } }; if (isOpen) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isOpen]); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { // Don't show discard dialog if already showing if (showDiscardDialog) { // Let the dialog handle its own Escape return; } event.preventDefault(); event.stopPropagation(); // Check for unsaved changes using ref to get current value if (hasUnsavedChangesRef.current()) { setShowDiscardDialog(true); } else { handleClose(); } } }; if (isOpen) { document.addEventListener('keydown', handleKeyDown); } return () => { document.removeEventListener('keydown', handleKeyDown); }; }, [isOpen, showDiscardDialog]); const handleChange = ( e: React.ChangeEvent ) => { const { name, value } = e.target; setFormData((prev) => ({ ...prev, [name]: value, })); }; const handleSubmit = async (e?: React.FormEvent) => { if (e) { e.preventDefault(); } if (!formData.name.trim()) { setError(t('errors.areaNameRequired')); return; } setIsSubmitting(true); setError(null); try { await onSave(formData); showSuccessToast( formData.uid ? t('success.areaUpdated') : t('success.areaCreated') ); handleClose(); } catch (err) { setError((err as Error).message); showErrorToast(t('errors.failedToSaveArea')); } finally { setIsSubmitting(false); } }; // Check if there are unsaved changes const hasUnsavedChanges = () => { if (!area) { // New area - check if any field has been filled return ( formData.name.trim() !== '' || formData.description?.trim() !== '' ); } // Existing area - compare with original return ( formData.name !== area.name || formData.description !== area.description ); }; // Use ref to store hasUnsavedChanges so it's always current in the event handler const hasUnsavedChangesRef = useRef(hasUnsavedChanges); useEffect(() => { hasUnsavedChangesRef.current = hasUnsavedChanges; }); const handleClose = () => { setIsClosing(true); setTimeout(() => { onClose(); setIsClosing(false); setShowDiscardDialog(false); }, 300); }; const handleDiscardChanges = () => { setShowDiscardDialog(false); handleClose(); }; const handleCancelDiscard = () => { setShowDiscardDialog(false); }; const handleDeleteArea = async () => { if (formData.uid && onDelete) { try { await onDelete(formData.uid); showSuccessToast( t('success.areaDeleted', 'Area deleted successfully!') ); handleClose(); } catch (err) { setError((err as Error).message); showErrorToast( t('errors.failedToDeleteArea', 'Failed to delete area.') ); } } }; if (!isOpen) return null; return ( <>
{/* Main Form Section */}
{/* Area Title Section - Always Visible */}
{/* Description Section - Always Visible */}