feat: implement auto-filling of license and registration document slots from personal vault and add UI labels for vault-sourced files

This commit is contained in:
estifanos
2026-08-31 08:18:27 +00:00
parent f1d6640629
commit 317a1532b1
13 changed files with 288 additions and 51 deletions

View File

@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import {
Alert,
Button,
Checkbox,
Divider,
@@ -12,6 +13,7 @@ import {
Text,
TextInput,
} from '@mantine/core';
import { IconInfoCircle } from '@tabler/icons-react';
import { useTranslation } from 'react-i18next';
import { BilingualInput, ModalFooter } from '@ema-platform/ui';
import {
@@ -225,6 +227,20 @@ export function DocumentRequirementEditorDrawer({
title={<Text fw={700}>{isNew ? t('certReq.doc.add', 'Add document requirement') : t('certReq.doc.edit', 'Edit document requirement')}</Text>}
>
<Stack gap="md">
{personal && (
<Alert
variant="light"
color="blue"
icon={<IconInfoCircle size={16} />}
title={t('certReq.doc.keyMatchTitle', 'Keys are what link this to a licence')}
>
{t(
'certReq.doc.keyMatchBody',
'An applicant who holds this document has it copied onto any licence application asking for a requirement with the same key. A key that matches nothing is still collected — it simply never fills a form by itself.',
)}
</Alert>
)}
<TextInput
label={t('certReq.doc.key', 'Key')}
placeholder="bank_letter"
@@ -232,7 +248,16 @@ export function DocumentRequirementEditorDrawer({
value={draft.key}
error={keyError}
disabled={!isNew}
description={isNew ? t('certReq.doc.keyHelp', 'Stable slug identifying this document slot') : t('certReq.doc.keyLocked', 'Key cannot change once created')}
description={
!isNew
? t('certReq.doc.keyLocked', 'Key cannot change once created')
: personal
? t(
'certReq.doc.keyHelpPersonal',
'Use the same key as the licence document this should answer — an application is filled from this document only when the two keys match exactly.',
)
: t('certReq.doc.keyHelp', 'Stable slug identifying this document slot')
}
// The value is read out of the event first: a functional updater
// runs after React has released the event, so `currentTarget` is
// null by the time it would be read inside one.

View File

@@ -174,6 +174,7 @@ export function DocumentsTab({
{attachments.map((attachment) => {
const file = attachment.files?.[0];
const fromVault = Boolean(attachment.copiedFromAttachmentId);
const flagged = attachment.documentKey in flags;
const verdict = verdictFor(attachment.documentKey);
const pendingReject = attachment.documentKey in rejecting;
@@ -199,6 +200,14 @@ export function DocumentsTab({
requirementByKey.get(attachment.documentKey)?.name,
) || attachment.documentKey}
</Text>
{/* Filed from the applicant's own document vault rather
than uploaded against this application — worth knowing
when the same file turns up on several files. */}
{fromVault && (
<Badge size="xs" variant="light" color="blue">
{t("review.documents.fromVault", "From My Documents")}
</Badge>
)}
{verdict && (
<Tooltip
label={