import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import { Button, Field, Icon, Modal, SelectInput } from '../../components/ui' import ActivityFields from '../../components/activity/ActivityFields' import { ASSET_FIELDS, ASSET_INIT_ACTIVITY_ID, createAsset, fetchAssignableEmployees, uploadAssetFile, } from '../../api/assets' import { emptyValues, validateActivity } from '../../api/activityForm' import { useAuth } from '../../store/AuthContext' // Asset Brand and Asset Location come first, as on the Edit form; the rest // keep the builder's order. const FIRST_FIELDS = ['field_59', 'field_61'] const FORM_FIELDS = [ ...FIRST_FIELDS.map((id) => ASSET_FIELDS.find((f) => f.id === id)), ...ASSET_FIELDS.filter((f) => !FIRST_FIELDS.includes(f.id)), ] // Asset Owner sits after Condition, as in the builder. const OWNER_AFTER = FORM_FIELDS.findIndex((f) => f.id === 'field_47') + 1 const FIELDS_BEFORE_OWNER = FORM_FIELDS.slice(0, OWNER_AFTER) const FIELDS_AFTER_OWNER = FORM_FIELDS.slice(OWNER_AFTER) const upload = (fieldId, file) => uploadAssetFile(ASSET_INIT_ACTIVITY_ID, fieldId, file) /** * "Add Asset" dialog. Fields are rendered straight from the Zino builder * definition in api/assets.js, so the form always matches the activity. * "Asset Owner" is a lookup, so it is picked from a list of active employees. */ export default function AddAssetModal({ open, onClose, onCreated }) { const navigate = useNavigate() const { logout } = useAuth() const [values, setValues] = useState(() => emptyValues(ASSET_FIELDS)) const [employees, setEmployees] = useState([]) const [loadingEmployees, setLoadingEmployees] = useState(false) const [ownerId, setOwnerId] = useState('') const [errors, setErrors] = useState({}) const [serverError, setServerError] = useState('') const [submitting, setSubmitting] = useState(false) // A fresh employee list each time the dialog opens. useEffect(() => { if (!open) return undefined let cancelled = false setLoadingEmployees(true) fetchAssignableEmployees() .then((list) => !cancelled && setEmployees(list)) .catch((err) => { if (cancelled) return if (err?.status === 401) { logout() navigate('/login', { replace: true }) return } setServerError(err?.message || 'Could not load the employee list.') }) .finally(() => !cancelled && setLoadingEmployees(false)) return () => { cancelled = true } }, [open, logout, navigate]) function reset() { setValues(emptyValues(ASSET_FIELDS)) setOwnerId('') setErrors({}) setServerError('') setSubmitting(false) } function close() { if (submitting) return reset() onClose?.() } function handleChange(id, value) { setValues((v) => ({ ...v, [id]: value })) if (errors[id]) setErrors((er) => ({ ...er, [id]: undefined })) if (serverError) setServerError('') } async function handleSubmit(e) { e.preventDefault() const nextErrors = validateActivity(ASSET_FIELDS, values) setErrors(nextErrors) if (Object.keys(nextErrors).length) return setSubmitting(true) setServerError('') try { const owner = employees.find((emp) => String(emp.id) === ownerId) ?? null const response = await createAsset(values, owner) onCreated?.(values, response) reset() onClose?.() } catch (err) { if (err?.status === 401) { logout() navigate('/login', { replace: true }) return } setServerError(err?.message || 'Could not create the asset. Please try again.') setSubmitting(false) } } return ( } > {serverError && (
{serverError}
)}
{(id) => ( { setOwnerId(e.target.value) if (serverError) setServerError('') }} options={employees.map((emp) => ({ value: String(emp.id), label: emp.code ? `${emp.name} (${emp.code})` : emp.name, }))} placeholder={loadingEmployees ? 'Loading employees…' : 'Select employee'} disabled={submitting || loadingEmployees} /> )}
) }