From 87b269e57cebc0c96a31b42ed60146e28eab0ea9 Mon Sep 17 00:00:00 2001 From: Andymick Date: Sat, 18 Jul 2026 19:19:38 +0100 Subject: [PATCH] Capture canvas preview when admin sends design to customer Add 'Send to Customer for Review' button to canvas that captures the current canvas state as preview images before sending, so customers see the modified design instead of the original submission. Co-Authored-By: Claude Haiku 4.5 --- src/app/admin/designs/[id]/edit/page.tsx | 16 ++--- .../designs/[id]/send-to-customer/route.ts | 12 +++- src/components/DesignCanvas.tsx | 64 ++++++++++++++++++- 3 files changed, 76 insertions(+), 16 deletions(-) diff --git a/src/app/admin/designs/[id]/edit/page.tsx b/src/app/admin/designs/[id]/edit/page.tsx index cac3ddb..02b1b87 100644 --- a/src/app/admin/designs/[id]/edit/page.tsx +++ b/src/app/admin/designs/[id]/edit/page.tsx @@ -95,18 +95,9 @@ export default function EditDesignPage({ params }: { params: { id: string } }) { ← Back to design approval -
-
-

{design.product.name}

-

Editing design for {design.customerName || design.customerEmail}

-
- +
+

{design.product.name}

+

Editing design for {design.customerName || design.customerEmail}

{/* Full Design Canvas */} @@ -116,6 +107,7 @@ export default function EditDesignPage({ params }: { params: { id: string } }) { initialDesignJson={design.designJson} initialColor={design.color} initialSize={design.size} + designId={params.id} />
diff --git a/src/app/api/designs/[id]/send-to-customer/route.ts b/src/app/api/designs/[id]/send-to-customer/route.ts index 396d2a7..2df0d71 100644 --- a/src/app/api/designs/[id]/send-to-customer/route.ts +++ b/src/app/api/designs/[id]/send-to-customer/route.ts @@ -7,6 +7,8 @@ export async function POST( { params }: { params: { id: string } } ) { try { + const { designPreviewUrl, designPreviewUrlBack, placementPreviewUrl, placementPreviewUrlBack } = await req.json().catch(() => ({})); + const design = await prisma.designApproval.findUnique({ where: { id: params.id }, include: { product: true }, @@ -16,10 +18,16 @@ export async function POST( return NextResponse.json({ error: 'Design not found' }, { status: 404 }); } - // Update status to IN_REVIEW + // Update status to IN_REVIEW and save new preview if provided + const updateData: any = { status: 'IN_REVIEW' }; + if (designPreviewUrl) updateData.designPreviewUrl = designPreviewUrl; + if (designPreviewUrlBack) updateData.designPreviewUrlBack = designPreviewUrlBack; + if (placementPreviewUrl) updateData.placementPreviewUrl = placementPreviewUrl; + if (placementPreviewUrlBack) updateData.placementPreviewUrlBack = placementPreviewUrlBack; + const updated = await prisma.designApproval.update({ where: { id: params.id }, - data: { status: 'IN_REVIEW' }, + data: updateData, include: { product: true, messages: { orderBy: { createdAt: 'asc' } } }, }); diff --git a/src/components/DesignCanvas.tsx b/src/components/DesignCanvas.tsx index 5e67076..7be19f7 100644 --- a/src/components/DesignCanvas.tsx +++ b/src/components/DesignCanvas.tsx @@ -493,12 +493,14 @@ export default function DesignCanvas({ initialDesignJson, initialColor, initialSize, + designId, }: { product: ProductDTO; isAdminEditing?: boolean; initialDesignJson?: string; initialColor?: string; initialSize?: string | null; + designId?: string; }) { const [color, setColor] = useState(initialColor ?? product.colors[0]); const [size, setSize] = useState(initialSize ?? (product.sizes[0] ?? null)); @@ -1037,6 +1039,52 @@ export default function DesignCanvas({ }); }; + const handleSendToCustomerForReview = async (id: string) => { + if (!stageBackRef.current || !stageFrontRef.current) return; + + setSubmittingForApproval(true); + try { + // Capture front preview + const frontStage = stageFrontRef.current; + const frontGuides = frontStage.find('.printGuide'); + frontGuides.forEach((g) => g.hide()); + const frontDataUrl = frontStage.toDataURL({ pixelRatio: 2 }); + frontGuides.forEach((g) => g.show()); + + // Capture back preview if exists + let backDataUrl: string | null = null; + if (hasBack && stageBackRef.current) { + const backStage = stageBackRef.current; + const backGuides = backStage.find('.printGuide'); + backGuides.forEach((g) => g.hide()); + backDataUrl = backStage.toDataURL({ pixelRatio: 2 }); + backGuides.forEach((g) => g.show()); + } + + // Send to API with the new preview images + const res = await fetch(`/api/designs/${id}/send-to-customer`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + designPreviewUrl: frontDataUrl, + designPreviewUrlBack: backDataUrl, + }), + }); + + if (res.ok) { + setSubmissionMessage('✓ Design sent to customer for review!'); + setTimeout(() => setSubmissionMessage(null), 5000); + } else { + setSubmissionMessage('✗ Failed to send design'); + } + } catch (err) { + console.error('Error sending design:', err); + setSubmissionMessage('✗ Error sending design'); + } finally { + setSubmittingForApproval(false); + } + }; + function renderPhoto(v: 'front' | 'back') { const colorPhoto = v === 'front' ? product.colorPhotos[color] : product.colorPhotosBack[color]; if (colorPhoto) { @@ -1564,12 +1612,24 @@ export default function DesignCanvas({ > Add to bag — 0 || elementsBack.length > 0) ? effectivePriceForPersonalised : product.effectivePrice) * quantity} /> + ) : isAdminEditing ? ( + ) : (