/** * Work Assignments Component v2 * * Clean implementation with: * - Array guards before all .map() calls * - Loading & empty states * - User-friendly error handling * - Debug logging */ const { useState, useEffect } = React; // Debug logger function debugLog(context, message, data = null) { if (window.WM_DEBUG_FIELDS) { console.log(`[WorkAssignments-v2] [${context}]`, message, data || ''); } } // Work Assignments Component function WorkAssignmentsV2() { const [assignments, setAssignments] = useState([]); const [fieldConfig, setFieldConfig] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showModal, setShowModal] = useState(false); const [selectedAssignment, setSelectedAssignment] = useState(null); const [showBin, setShowBin] = useState(false); const [toast, setToast] = useState(null); const [errorLogId, setErrorLogId] = useState(null); // Load field configuration useEffect(() => { loadFieldConfig(); }, []); // Load assignments useEffect(() => { loadAssignments(); }, [showBin]); const loadFieldConfig = async () => { debugLog('loadFieldConfig', 'Starting...'); try { const response = await fetch(`${IWM_DATA.apiUrl}/work-manager/v1/work-assignments/fields`, { headers: { 'X-WP-Nonce': IWM_DATA.nonce } }); const result = await response.json(); debugLog('loadFieldConfig', 'Response:', result); if (result.success && result.data) { // CRITICAL: Guard to ensure array const fields = Array.isArray(result.data) ? result.data : []; debugLog('loadFieldConfig', 'Fields array length:', fields.length); setFieldConfig(fields); } else { throw new Error(result.message || 'Failed to load field configuration'); } } catch (err) { debugLog('loadFieldConfig', 'ERROR:', err); handleError('Failed to load form configuration', err); } }; const loadAssignments = async () => { debugLog('loadAssignments', 'Starting...', { showBin }); setLoading(true); setError(null); try { const endpoint = showBin ? '/work-assignments/bin' : '/work-assignments'; const response = await fetch(`${IWM_DATA.apiUrl}/work-manager/v1${endpoint}`, { headers: { 'X-WP-Nonce': IWM_DATA.nonce } }); const result = await response.json(); debugLog('loadAssignments', 'Response:', result); if (result.success && result.data) { // CRITICAL: Guard to ensure array const assignmentList = Array.isArray(result.data) ? result.data : []; debugLog('loadAssignments', 'Assignments array length:', assignmentList.length); setAssignments(assignmentList); } else { throw new Error(result.message || 'Failed to load assignments'); } } catch (err) { debugLog('loadAssignments', 'ERROR:', err); handleError('Failed to load assignments', err); } finally { setLoading(false); } }; const handleCreateAssignment = async (formData) => { debugLog('handleCreateAssignment', 'Data:', formData); try { const response = await fetch(`${IWM_DATA.apiUrl}/work-manager/v1/work-assignments`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': IWM_DATA.nonce }, body: JSON.stringify(formData) }); const result = await response.json(); debugLog('handleCreateAssignment', 'Response:', result); if (result.success) { showToast('Assignment created successfully!', 'success'); setShowModal(false); loadAssignments(); } else { throw new Error(result.message || 'Failed to create assignment'); } } catch (err) { debugLog('handleCreateAssignment', 'ERROR:', err); handleError('Failed to create assignment', err); } }; const handleMoveToBin = async (id) => { debugLog('handleMoveToBin', 'ID:', id); try { const response = await fetch(`${IWM_DATA.apiUrl}/work-manager/v1/work-assignments/${id}/bin`, { method: 'POST', headers: { 'X-WP-Nonce': IWM_DATA.nonce } }); const result = await response.json(); if (result.success) { showToast('Assignment moved to bin', 'success'); loadAssignments(); } else { throw new Error(result.message || 'Failed to move to bin'); } } catch (err) { handleError('Failed to move to bin', err); } }; const handleRestore = async (id) => { debugLog('handleRestore', 'ID:', id); try { const response = await fetch(`${IWM_DATA.apiUrl}/work-manager/v1/work-assignments/${id}/restore`, { method: 'POST', headers: { 'X-WP-Nonce': IWM_DATA.nonce } }); const result = await response.json(); if (result.success) { showToast('Assignment restored', 'success'); loadAssignments(); } else { throw new Error(result.message || 'Failed to restore'); } } catch (err) { handleError('Failed to restore assignment', err); } }; const handleDelete = async (id) => { if (!confirm('Permanently delete this assignment? This action cannot be undone.')) { return; } debugLog('handleDelete', 'ID:', id); try { const response = await fetch(`${IWM_DATA.apiUrl}/work-manager/v1/work-assignments/${id}`, { method: 'DELETE', headers: { 'X-WP-Nonce': IWM_DATA.nonce } }); const result = await response.json(); if (result.success) { showToast('Assignment permanently deleted', 'success'); loadAssignments(); } else { throw new Error(result.message || 'Failed to delete'); } } catch (err) { handleError('Failed to delete assignment', err); } }; const handleError = (userMessage, error) => { const logId = 'ERR_' + Date.now(); console.error(`[${logId}]`, userMessage, error); setError({ message: userMessage, details: error.message, logId: logId }); setErrorLogId(logId); }; const showToast = (message, type = 'success') => { setToast({ message, type }); setTimeout(() => setToast(null), 3000); }; // Loading state if (loading && assignments.length === 0) { return (
âŗ

Loading Work Assignments...

); } return (
{/* Header */}

{showBin ? 'đŸ—‘ī¸ Bin' : '📋 Work Assignments'}

{!showBin && ( )}
{/* Error Display */} {error && (

âš ī¸ {error.message}

{error.details}

Error Log ID: {error.logId} (Check browser console for details)

)} {/* Empty state */} {!loading && assignments.length === 0 && (
{showBin ? 'đŸ—‘ī¸' : '📋'}

{showBin ? 'Bin is empty' : 'No assignments yet'}

{showBin ? 'Deleted assignments will appear here for 30 days before auto-deletion.' : 'Get started by creating your first assignment.'}

{!showBin && ( )}
)} {/* Assignments List */} {Array.isArray(assignments) && assignments.length > 0 && (
{assignments.map(assignment => ( ))}
)} {/* Create/Edit Modal */} {showModal && ( setShowModal(false)} onSubmit={handleCreateAssignment} /> )} {/* Toast Notification */} {toast && ( setToast(null)} /> )}
); } // Assignment Card Component function AssignmentCard({ assignment, isInBin, onMoveToBin, onRestore, onDelete }) { return (

{assignment.title}

{assignment.category}
{assignment.assignee_name && (

Assignee: {assignment.assignee_name}

)} {assignment.description && (

{assignment.description}

)}

Created: {new Date(assignment.created_at).toLocaleDateString()}

{!isInBin ? ( <> ) : ( <> )}
); } // Assignment Modal Component function AssignmentModal({ fieldConfig, assignment, onClose, onSubmit }) { const [formData, setFormData] = useState(assignment || {}); const [errors, setErrors] = useState({}); // CRITICAL: Guard before mapping const fields = Array.isArray(fieldConfig) ? fieldConfig : []; debugLog('AssignmentModal', 'Field config:', { isArray: Array.isArray(fieldConfig), count: fields.length }); const handleSubmit = (e) => { e.preventDefault(); // Validate required fields const newErrors = {}; fields.forEach(field => { if (field.required && field.enabled && !formData[field.key]) { newErrors[field.key] = `${field.label} is required`; } }); if (Object.keys(newErrors).length > 0) { setErrors(newErrors); return; } onSubmit(formData); }; const handleChange = (key, value) => { setFormData({ ...formData, [key]: value }); if (errors[key]) { setErrors({ ...errors, [key]: null }); } }; return (
e.stopPropagation()}>

{assignment ? 'Edit Assignment' : 'New Assignment'}

{fields.length === 0 ? (

âš ī¸ No form fields configured. Please configure fields in the Dev Tools panel.

) : ( fields.map(field => ( field.enabled && (
{field.type === 'textarea' ? (