mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
Render finalising status on the bulk-upload page and auto-advance to complete
The async finaliser (ADR-0005) introduced the `finalising` status, but the
server page's STATUS_CONFIG had no entry for it, so it fell through to the
`ready_for_processing` fallback ("Awaiting column mapping") and never mounted the
live poller — the page looked stuck even though the Lambda had inserted the
properties and written `complete`.
- Add the `finalising` card ("Uploading to ARA") to STATUS_CONFIG.
- Render OnboardingProgress during `finalising` so it polls live.
- Refresh the server page once when the poll first sees a terminal status
(guarded by a new `serverStatus` prop to avoid a loop; uses react-query v4
onSuccess, no useEffect) so it advances to the "Processing complete" card.
- Add a `finalising` → "Uploading to ARA" badge on the uploads list.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fc2664aeef
commit
5fe86f01d8
4 changed files with 37 additions and 3 deletions
|
|
@ -28,7 +28,7 @@ import {
|
|||
RoofTypeValues,
|
||||
} from "@/app/db/schema/landlord_overrides";
|
||||
import { CLASSIFIER_FIELDS } from "@/lib/bulkUpload/columnFields";
|
||||
import { statusLabel } from "@/lib/bulkUpload/types";
|
||||
import { statusLabel, isTerminalStatus } from "@/lib/bulkUpload/types";
|
||||
|
||||
// Valid enum options per classifier category, for the editable dropdowns (#299).
|
||||
const CATEGORY_VALUES: Record<string, readonly string[]> = {
|
||||
|
|
@ -50,6 +50,10 @@ interface Props {
|
|||
portfolioSlug: string;
|
||||
portfolioId: string;
|
||||
uploadId: string;
|
||||
// The status at the last server render. Used to refresh the server page exactly
|
||||
// once when polling first observes a terminal status (async finalise, ADR-0005),
|
||||
// so the page advances from "Uploading to ARA" to the "Processing complete" card.
|
||||
serverStatus: string;
|
||||
isDomnaUser: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -60,10 +64,22 @@ export default function OnboardingProgress({
|
|||
portfolioSlug,
|
||||
portfolioId,
|
||||
uploadId,
|
||||
serverStatus,
|
||||
isDomnaUser,
|
||||
}: Props) {
|
||||
const router = useRouter();
|
||||
const progress = useBulkUploadProgress(portfolioId, uploadId);
|
||||
const progress = useBulkUploadProgress(portfolioId, uploadId, {
|
||||
// When the async finaliser finishes, the poll flips the status to a terminal
|
||||
// value while the server page is still on `finalising`. Refresh once so the
|
||||
// server re-renders the "Processing complete" / "failed" card. Guarding on the
|
||||
// non-terminal serverStatus prevents a refresh loop: after the refresh the
|
||||
// prop is terminal, so this no-ops.
|
||||
onSuccess: (data) => {
|
||||
if (!isTerminalStatus(serverStatus) && isTerminalStatus(data.upload.status)) {
|
||||
router.refresh();
|
||||
}
|
||||
},
|
||||
});
|
||||
const combine = useRequestCombine(portfolioId, uploadId);
|
||||
const finalize = useFinalize(portfolioId, uploadId);
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,14 @@ const STATUS_CONFIG = {
|
|||
body: "Matches ready, writing into your portfolio.",
|
||||
cta: false,
|
||||
},
|
||||
finalising: {
|
||||
icon: ArrowPathIcon,
|
||||
iconBg: "bg-blue-50",
|
||||
iconColor: "text-blue-500",
|
||||
title: "Uploading to ARA",
|
||||
body: "Creating your properties from the matched addresses. This can take a little while for large files.",
|
||||
cta: false,
|
||||
},
|
||||
complete: {
|
||||
icon: CheckCircleIcon,
|
||||
iconBg: "bg-green-50",
|
||||
|
|
@ -167,6 +175,7 @@ export default async function BulkUploadDetailPage(props: {
|
|||
{(statusKey === "processing" ||
|
||||
statusKey === "combining" ||
|
||||
statusKey === "awaiting_review" ||
|
||||
statusKey === "finalising" ||
|
||||
statusKey === "complete" ||
|
||||
statusKey === "failed") &&
|
||||
upload.taskId && (
|
||||
|
|
@ -174,6 +183,7 @@ export default async function BulkUploadDetailPage(props: {
|
|||
portfolioSlug={slug}
|
||||
portfolioId={upload.portfolioId}
|
||||
uploadId={uploadId}
|
||||
serverStatus={upload.status}
|
||||
isDomnaUser={isDomnaUser}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
const STATUS_LABELS: Record<string, { label: string; classes: string }> = {
|
||||
ready_for_processing: { label: "Ready", classes: "bg-amber-100 text-amber-700" },
|
||||
processing: { label: "Processing", classes: "bg-blue-100 text-blue-700" },
|
||||
finalising: { label: "Uploading to ARA", classes: "bg-blue-100 text-blue-700" },
|
||||
complete: { label: "Complete", classes: "bg-green-100 text-green-700" },
|
||||
failed: { label: "Failed", classes: "bg-red-100 text-red-700" },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -197,7 +197,11 @@ export function useStartAddressMatching(portfolioId: string, uploadId: string) {
|
|||
});
|
||||
}
|
||||
|
||||
export function useBulkUploadProgress(portfolioId: string, uploadId: string) {
|
||||
export function useBulkUploadProgress(
|
||||
portfolioId: string,
|
||||
uploadId: string,
|
||||
options?: { onSuccess?: (data: ProgressView) => void },
|
||||
) {
|
||||
return useQuery<ProgressView, Error>({
|
||||
queryKey: bulkUploadKeys.progress(uploadId),
|
||||
queryFn: async () => {
|
||||
|
|
@ -211,6 +215,9 @@ export function useBulkUploadProgress(portfolioId: string, uploadId: string) {
|
|||
const status = data?.upload.status;
|
||||
return status && isTerminalStatus(status) ? false : 3000;
|
||||
},
|
||||
// v4 onSuccess fires after each successful poll; callers use it to react to a
|
||||
// status transition (e.g. refresh the server page once it goes terminal).
|
||||
onSuccess: options?.onSuccess,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue