Files
emaui/apps/backoffice/src/app/features/license-review/components/AssignDialog.tsx
2026-08-22 07:54:35 +00:00

114 lines
3.4 KiB
TypeScript

import { useEffect, useState } from 'react';
import { Modal, Select, Stack, Text, Textarea } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { useGetAssignableOfficersQuery } from '@ema-platform/api';
import { ModalFooter } from '@ema-platform/ui';
import { Button } from '@mantine/core';
export type AssignKind = 'review' | 'inspection';
interface AssignDialogProps {
opened: boolean;
onClose: () => void;
/** Which stage is being handed out — changes the wording, not the mechanics. */
kind: AssignKind;
/** Reference of the application being dispatched, shown for confirmation. */
applicationNumber?: string;
loading?: boolean;
onConfirm: (officerId: string, remark?: string) => void;
}
/**
* The team leader handing work to an employee.
*
* One dialog for both stages because the decision is identical — pick a person,
* optionally say why — and two near-identical modals would drift apart. The
* `kind` only selects wording.
*
* Confirm stays disabled until someone is picked: an assignment with no
* assignee is the one mistake this dialog exists to prevent.
*/
export function AssignDialog({
opened,
onClose,
kind,
applicationNumber,
loading,
onConfirm,
}: AssignDialogProps) {
const { t } = useTranslation();
const { data: officers = [], isLoading } = useGetAssignableOfficersQuery();
const [officerId, setOfficerId] = useState<string | null>(null);
const [remark, setRemark] = useState('');
// Reopening for a different application must not offer the previous
// dialog's answers as if they had been chosen for this one.
useEffect(() => {
if (opened) {
setOfficerId(null);
setRemark('');
}
}, [opened]);
const title =
kind === 'review'
? t('queue.assignReview', 'Assign document review')
: t('queue.assignInspection', 'Assign inspection');
return (
<Modal opened={opened} onClose={onClose} title={title} size="md">
<Stack gap="md">
{applicationNumber && (
<Text size="sm" c="dimmed">
{applicationNumber}
</Text>
)}
<Select
label={
kind === 'review'
? t('queue.assignReviewTo', 'Employee to review the documents')
: t('queue.assignInspectionTo', 'Employee to conduct the inspection')
}
placeholder={t('queue.selectEmployee', 'Select an employee')}
data={officers.map((o) => ({
value: o.id,
label: o.name ?? o.id,
}))}
value={officerId}
onChange={setOfficerId}
disabled={isLoading}
searchable
withAsterisk
/>
<Textarea
label={t('queue.assignRemark', 'Instructions')}
description={t(
'queue.assignRemarkHint',
'Optional. Sent with the assignment notification.',
)}
value={remark}
onChange={(e) => setRemark(e.currentTarget.value)}
autosize
minRows={2}
/>
<ModalFooter>
<Button variant="default" onClick={onClose} size="sm">
{t('common.cancel', 'Cancel')}
</Button>
<Button
size="sm"
loading={loading}
disabled={!officerId}
onClick={() => officerId && onConfirm(officerId, remark || undefined)}
>
{t('queue.assignConfirm', 'Assign')}
</Button>
</ModalFooter>
</Stack>
</Modal>
);
}