From 98ebe4796d02abb7fababd96a90d0bd7ba0f5ea3 Mon Sep 17 00:00:00 2001 From: DJP Date: Tue, 12 May 2026 20:35:34 -0400 Subject: [PATCH] Review page: add upload zone when latest revision has no asset Surface the AssetUploadZone directly on the deliverable review page when the latest revision is asset-less, plus a "Replace asset" toggle when one is already uploaded. CLIENT_VIEWER stays read-only. Closes the gap where producers were stuck on "No asset uploaded yet for V0.2" with no way to upload from this page (they had to go back to the deliverable detail panel). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../[deliverableId]/review/page.tsx | 62 +++++++++++++++---- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/review/page.tsx b/src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/review/page.tsx index 40e6c36..ece9fb6 100644 --- a/src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/review/page.tsx +++ b/src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/review/page.tsx @@ -1,13 +1,16 @@ "use client"; -import { use } from "react"; +import { use, useState } from "react"; import Link from "next/link"; -import { ArrowLeft, AlertCircle } from "lucide-react"; +import { ArrowLeft } from "lucide-react"; +import { useQueryClient } from "@tanstack/react-query"; import { useDeliverable } from "@/hooks/use-deliverables"; import { useRevisions } from "@/hooks/use-revisions"; +import { useCurrentUser } from "@/hooks/use-current-user"; import { formatRevisionLabel } from "@/lib/format-revision-label"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; +import { AssetUploadZone } from "@/components/review/asset-upload-zone"; // Phase 5 transitional page. The HP-style annotation surface is being // rebuilt against the single-asset revision model. For now, this page @@ -46,6 +49,17 @@ export default function DeliverableReviewPage({ params, searchParams }: PageProp const revisions = (revData as RevisionLike[] | undefined) ?? []; const latest = revisions[0]; + const { data: me } = useCurrentUser(); + const canEdit = !!me && (me as any).role !== "CLIENT_VIEWER"; + + const queryClient = useQueryClient(); + const [showReplace, setShowReplace] = useState(false); + const refreshRevisions = () => { + queryClient.invalidateQueries({ queryKey: ["revisions", stageId] }); + queryClient.invalidateQueries({ queryKey: ["deliverable", projectId, deliverableId] }); + setShowReplace(false); + }; + return (
{!latest ? ( -
- -
- No revision yet on this stage. Open the deliverable page and use - the in-row review panel to create one. -
+
+ No revision yet on this stage. Open the deliverable page and use + the in-row review panel to create one.
) : (
{formatRevisionLabel(latest)} {latest.status} + {canEdit && latest.asset && !showReplace && ( + + )}
- {latest.asset ? ( + {latest.asset && !showReplace ? (
{latest.asset.kind === "image" ? ( /* eslint-disable-next-line @next/next/no-img-element */ @@ -101,6 +122,23 @@ export default function DeliverableReviewPage({ params, searchParams }: PageProp /> )}
+ ) : canEdit ? ( +
+ + {showReplace && ( + + )} +
) : (
No asset uploaded yet for {formatRevisionLabel(latest)}. @@ -117,9 +155,9 @@ export default function DeliverableReviewPage({ params, searchParams }: PageProp

Full annotation + comparison surface is being rebuilt against the - new single-asset revision model. For now, all review actions - (Approve, Request changes, Send to client, New revision) live in - the in-row panel on the deliverable detail page. + new single-asset revision model. For now, the in-row panel on the + deliverable detail page is where Approve / Request changes / Send + to client / New revision live.

)}