import { useMemo, useState } from 'react'; import { ActionIcon, Alert, Badge, Button, Card, Group, Modal, Stack, Text, Title } from '@mantine/core'; import { IconAlertCircle, IconEdit, IconPlus, IconTrash } from '@tabler/icons-react'; import { useTranslation } from 'react-i18next'; import { EmptyState, ErrorState, ModalFooter, PageLoader } from '@ema-platform/ui'; import { extractErrorMessage, useCreateDocumentRequirementMutation, useDeleteDocumentRequirementMutation, useGetDocumentRequirementsQuery, useGetFormSchemaPaletteQuery, useLocalized, useUpdateDocumentRequirementMutation, type ApplicationKind, type DocumentRequirement, type LicenseType, } from '@ema-platform/api'; import { collectConditionTargets } from '../config/schema-paths'; import { useRequirementActions } from '../hooks/useRequirementActions'; import { describeCondition } from './ConditionBuilder'; import { DocumentRequirementEditorDrawer } from './DocumentRequirementEditorDrawer'; const KINDS: ApplicationKind[] = ['NEW', 'RENEWAL']; const MODE_COLOR: Record = { ALWAYS: 'blue', CONDITIONAL: 'violet', OPTIONAL: 'gray', }; /** * Document upload requirements for one licence type, grouped by application * kind (a new-application slot and its renewal counterpart are different * rows even when they share a key). Every edit is a real CRUD call the * moment the admin confirms it — there is no separate "save all" step here, * unlike the form schema tab's whole-document replace. */ export function DocumentRequirementsTab({ licenseType }: { licenseType: LicenseType }) { const { t } = useTranslation(); const localized = useLocalized(); const run = useRequirementActions(); const { data, isLoading, isError, error, refetch } = useGetDocumentRequirementsQuery(); const { data: palette } = useGetFormSchemaPaletteQuery(); const [createRequirement, { isLoading: creating }] = useCreateDocumentRequirementMutation(); const [updateRequirement, { isLoading: updating }] = useUpdateDocumentRequirementMutation(); const [deleteRequirement] = useDeleteDocumentRequirementMutation(); const [editorState, setEditorState] = useState<{ kind: ApplicationKind; requirement: DocumentRequirement | null } | null>(null); const [deleteTarget, setDeleteTarget] = useState(null); const requirements = useMemo( () => (data?.items ?? []).filter((r) => r.licenseTypeId === licenseType.id), [data, licenseType.id], ); const conditionTargets = collectConditionTargets(licenseType.formSchema.sections); async function handleSave(draft: Omit) { const ok = await run( () => editorState?.requirement ? updateRequirement({ id: editorState.requirement.id, ...draft }).unwrap() : createRequirement({ ...draft, licenseTypeId: licenseType.id }).unwrap(), editorState?.requirement ? t('certReq.doc.updated', 'Document requirement updated') : t('certReq.doc.created', 'Document requirement added'), ); if (ok) setEditorState(null); } async function confirmDelete() { if (!deleteTarget) return; const ok = await run( () => deleteRequirement(deleteTarget.id).unwrap(), t('certReq.doc.deleted', 'Document requirement removed'), ); if (ok) setDeleteTarget(null); } if (isLoading) return ; if (isError) { return ( refetch()} icon={IconAlertCircle} /> ); } return ( {t( 'certReq.doc.subtitle', 'What an applicant must upload for this licence type, split by new application and renewal.', )} {KINDS.map((kind) => { const rows = requirements .filter((r) => r.applicationKind === kind) .sort((a, b) => a.sortOrder - b.sortOrder); return ( {kind === 'NEW' ? t('certReq.doc.kindNew', 'New application') : t('certReq.doc.kindRenewal', 'Renewal')} {rows.length === 0 ? ( {t('certReq.doc.emptyKind', 'No document requirements for this application kind yet.')} ) : ( {rows.map((req) => (
{localized(req.name) || req.key} {req.mode} {req.allowMultiple && {t('certReq.doc.multiple', 'multiple')}} key: {req.key} · {req.maxSizeMb}MB · {req.allowedMimeTypes.join(', ')} {req.mode === 'CONDITIONAL' && req.conditionExpression && ( {t('certReq.doc.when', 'when')} {describeCondition(req.conditionExpression, t)} )}
setEditorState({ kind, requirement: req })}> setDeleteTarget(req)}>
))}
)}
); })} {requirements.length === 0 && ( )} setEditorState(null)} requirement={editorState?.requirement ?? null} defaultApplicationKind={editorState?.kind ?? 'NEW'} onSave={handleSave} palette={palette} conditionTargets={conditionTargets} saving={creating || updating} /> setDeleteTarget(null)} title={t('certReq.doc.delete', 'Delete document requirement')} size="sm"> {t('certReq.doc.deleteWarning', 'Applicants already relying on this slot will no longer see it. This cannot be undone.')} {t('certReq.doc.deleteConfirm', 'Remove "{{name}}"?', { name: deleteTarget ? localized(deleteTarget.name) || deleteTarget.key : '', })}
); }