mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 13:02:50 +00:00
fixes
This commit is contained in:
@@ -57,12 +57,14 @@ import {
|
||||
useResumeApplicationMutation,
|
||||
useScheduleInspectionMutation,
|
||||
type RemarkTargetType,
|
||||
type StaffEvidenceRequirement,
|
||||
} from "@ema-platform/api";
|
||||
import {
|
||||
AdvancedTable,
|
||||
AmharicDatePicker,
|
||||
ErrorState,
|
||||
ModalFooter,
|
||||
PdfPreviewModal,
|
||||
useServerTable,
|
||||
} from "@ema-platform/ui";
|
||||
import { useDateDisplayer } from "@ema-platform/shared";
|
||||
@@ -178,6 +180,19 @@ export function LicenseReviewPage() {
|
||||
),
|
||||
[requirements],
|
||||
);
|
||||
// What each role is *required* to produce (CV, work agreement, ERB
|
||||
// certificate). Without this the tab can only list what was uploaded, so a
|
||||
// missing CV looks identical to a role that never needed one.
|
||||
const evidenceByRole = useMemo(
|
||||
() =>
|
||||
new Map(
|
||||
requirements?.staffRoleRequirements.map((r) => [
|
||||
r.roleKey,
|
||||
r.requiredEvidence ?? [],
|
||||
]) ?? [],
|
||||
),
|
||||
[requirements],
|
||||
);
|
||||
|
||||
const [completeReview] = useCompleteReviewMutation();
|
||||
const [requestAdjustment] = useRequestAdjustmentMutation();
|
||||
@@ -971,6 +986,7 @@ export function LicenseReviewPage() {
|
||||
renderEvidence: (member) => (
|
||||
<StaffEvidenceCell
|
||||
staffId={member.id}
|
||||
required={evidenceByRole.get(member.roleKey) ?? []}
|
||||
fallback={member.documents}
|
||||
/>
|
||||
),
|
||||
@@ -1275,36 +1291,96 @@ export function LicenseReviewPage() {
|
||||
*/
|
||||
function StaffEvidenceCell({
|
||||
staffId,
|
||||
required,
|
||||
fallback,
|
||||
}: {
|
||||
staffId: string;
|
||||
/** What this person's role must produce, from the licence-type config. */
|
||||
required: StaffEvidenceRequirement[];
|
||||
fallback?: { id: string; documentKey: string; files: { url?: string }[] }[];
|
||||
}) {
|
||||
const localized = useLocalized();
|
||||
const { t } = useTranslation();
|
||||
const [preview, setPreview] = useState<{ url: string; title: string } | null>(
|
||||
null,
|
||||
);
|
||||
const { data: attachments } = useGetAttachmentsQuery({
|
||||
ownerType: "APPLICATION_STAFF",
|
||||
ownerId: staffId,
|
||||
});
|
||||
const docs = attachments?.length ? attachments : (fallback ?? []);
|
||||
const uploadedBy = new Map(docs.map((d) => [d.documentKey, d]));
|
||||
|
||||
// Drive the list off the requirements, not off what happens to have been
|
||||
// uploaded: a mandatory CV that is absent has to be visible as absent, which
|
||||
// is the whole point of the officer looking at this column. Anything
|
||||
// uploaded outside the list still gets shown rather than silently dropped.
|
||||
const extras = docs.filter(
|
||||
(d) => !required.some((r) => r.docKey === d.documentKey),
|
||||
);
|
||||
|
||||
if (!required.length && !extras.length) {
|
||||
return (
|
||||
<Text size="xs" c="dimmed">
|
||||
—
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
const badge = (
|
||||
key: string,
|
||||
label: string,
|
||||
url: string | undefined,
|
||||
mandatory: boolean,
|
||||
) => {
|
||||
const missing = !url;
|
||||
return (
|
||||
<Tooltip
|
||||
key={key}
|
||||
label={
|
||||
missing
|
||||
? mandatory
|
||||
? t("review.evidenceMissingRequired", "Required — not uploaded")
|
||||
: t("review.evidenceMissing", "Not uploaded")
|
||||
: t("licensing.documents.view", "View")
|
||||
}
|
||||
withArrow
|
||||
>
|
||||
<Badge
|
||||
size="xs"
|
||||
variant={missing ? "outline" : "light"}
|
||||
color={missing ? (mandatory ? "red" : "gray") : "teal"}
|
||||
style={missing ? undefined : { cursor: "pointer" }}
|
||||
onClick={
|
||||
missing ? undefined : () => setPreview({ url: url, title: label })
|
||||
}
|
||||
>
|
||||
{label}
|
||||
{missing && mandatory ? " *" : ""}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Group gap={4}>
|
||||
{docs.map((doc) => {
|
||||
const url = doc.files?.[0]?.url;
|
||||
return (
|
||||
<Badge
|
||||
key={doc.id}
|
||||
size="xs"
|
||||
variant="light"
|
||||
component={url ? "a" : undefined}
|
||||
href={url}
|
||||
target={url ? "_blank" : undefined}
|
||||
rel={url ? "noreferrer" : undefined}
|
||||
style={url ? { cursor: "pointer" } : undefined}
|
||||
>
|
||||
{doc.documentKey}
|
||||
</Badge>
|
||||
);
|
||||
})}
|
||||
{required.map((item) =>
|
||||
badge(
|
||||
item.docKey,
|
||||
localized(item.label) || item.docKey,
|
||||
uploadedBy.get(item.docKey)?.files?.[0]?.url,
|
||||
item.mandatory,
|
||||
),
|
||||
)}
|
||||
{extras.map((doc) =>
|
||||
badge(doc.id, doc.documentKey, doc.files?.[0]?.url, false),
|
||||
)}
|
||||
<PdfPreviewModal
|
||||
opened={Boolean(preview)}
|
||||
onClose={() => setPreview(null)}
|
||||
url={preview?.url ?? ""}
|
||||
title={preview?.title}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user