+
+ {done ? (
+
+ ) : waiting ? (
+
+ ) : (
+
+ {number}
+
+ )}
+
+
+
+
+
+ {step.title}
+
+ {current && (
+ }
+ >
+ {waiting
+ ? t("review.steps.waitingOnApplicant", "Applicant")
+ : t("review.steps.yourMove", "You")}
+
+ )}
+
+
+ {/* Done steps carry their date only — the instruction has served its
+ purpose and repeating it buries the step that is actually live. */}
+ {done ? (
+ completedLabel && (
+
+ {completedLabel}
+
+ )
+ ) : (
+
+ {step.detail}
+
+ )}
+
+ {step.progress && (
+
+ {step.progress}
+
+ )}
+
+ {step.blockedBy && (
+
+
+
+
+ {step.blockedBy}
+
+
+
+ )}
+
+
+ );
+}
diff --git a/apps/backoffice/src/app/features/license-review/pages/LicenseReviewPage/index.tsx b/apps/backoffice/src/app/features/license-review/pages/LicenseReviewPage/index.tsx
index f253d8e16..ae2b786bf 100644
--- a/apps/backoffice/src/app/features/license-review/pages/LicenseReviewPage/index.tsx
+++ b/apps/backoffice/src/app/features/license-review/pages/LicenseReviewPage/index.tsx
@@ -100,14 +100,17 @@ import { DocumentsTab } from "../../components/DocumentsTab";
import { FormDetailsTab } from "../../components/FormDetailsTab";
import { ApplicantCard } from "../../components/ApplicantCard";
import { ExamStatePanel } from "../../components/ExamStatePanel";
+import { NextStepsPanel } from "../../components/NextStepsPanel";
import { useGetLocationsQuery } from "../../../location/api/location-api";
import { computeSla } from "../../sla";
import { reviewStaffColumns } from "./columns";
import {
evaluateEligibility,
+ isSeafarerCertificate,
presentationFor,
requiresPassedInspection,
} from "../../config/license-types";
+import { buildReviewSteps } from "../../steps";
import {
resolveActions,
type ActionId,
@@ -248,10 +251,8 @@ export function LicenseReviewPage() {
const [resumeApplication] = useResumeApplicationMutation();
const [escalateApplication] = useEscalateApplicationMutation();
const [assignApplication] = useAssignApplicationMutation();
- const [assignReviewer, { isLoading: assigningReviewer }] =
- useAssignReviewerMutation();
- const [assignInspector, { isLoading: assigningInspector }] =
- useAssignInspectorMutation();
+ const [assignReviewer] = useAssignReviewerMutation();
+ const [assignInspector] = useAssignInspectorMutation();
const [reportReview] = useReportReviewMutation();
const [reportInspection] = useReportInspectionMutation();
@@ -651,21 +652,33 @@ export function LicenseReviewPage() {
const status = app.status;
const staffPaged = staffTable.paginate(data.staff);
const presentation = presentationFor(app.licenseType?.key);
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- i18next's overloaded TFunction type doesn't structurally match a plain callback signature
const sla = computeSla(
app,
undefined,
i18n.language,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- i18next's overloaded TFunction type doesn't structurally match a plain callback signature
(key, options) => t(key, options as any) as string,
);
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- i18next's overloaded TFunction type doesn't structurally match a plain callback signature
const eligibility = evaluateEligibility(
app,
app.licenseType,
i18n.language,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- i18next's overloaded TFunction type doesn't structurally match a plain callback signature
(key, options) => t(key, options as any) as string,
);
+ // What is still outstanding, in order, with the current step's blocker
+ // spelled out. Derived from the same status and resolved actions the
+ // Decision Bar runs on, so the panel never offers a step the server refuses.
+ const stepPlan = buildReviewSteps({
+ detail: data,
+ actions,
+ documentProgress,
+ skipsAssignment: isSeafarerCertificate(app.licenseType),
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- i18next's overloaded TFunction type doesn't structurally match a plain callback signature
+ t: (key, options) => t(key, options as any) as string,
+ });
+
const rawThreshold = app.licenseType?.capitalThreshold;
const threshold =
rawThreshold === null || rawThreshold === undefined
@@ -1126,11 +1139,27 @@ export function LicenseReviewPage() {