- {orders.map((order) => {
- const hasPersonalised = order.items.some((item) => {
+ {orders.map((order: any) => {
+ const hasPersonalised = order.items.some((item: any) => {
try {
const design = JSON.parse(item.designJson) as { front: unknown[]; back: unknown[] };
return (design.front?.length ?? 0) > 0 || (design.back?.length ?? 0) > 0;
diff --git a/src/app/admin/products/[id]/edit/page.tsx b/src/app/admin/products/[id]/edit/page.tsx
index da17f6b..7157fae 100644
--- a/src/app/admin/products/[id]/edit/page.tsx
+++ b/src/app/admin/products/[id]/edit/page.tsx
@@ -9,25 +9,22 @@ import SizePicker from '@/components/SizePicker';
import PhotoPrintAreaField, { type Box } from '@/components/PhotoPrintAreaField';
import CategoryPicker from '@/components/CategoryPicker';
-export default async function EditProductPage({ params }: { params: { id: string } }) {
+export default async function EditProductPage({ params }: { params: Promise<{ id: string }> }) {
+ const { id } = await params;
const activePromotions = await fetchActivePromotions();
- const row = await prisma.product.findUnique({ where: { id: params.id }, include: { collections: true } });
+ const row = await prisma.product.findUnique({ where: { id }, include: { collections: true } });
if (!row) notFound();
const product = toProductDTO(row, activePromotions);
const categories = await prisma.category.findMany({
where: { parentId: null },
orderBy: { name: 'asc' },
- select: {
- id: true,
- name: true,
- slug: true,
+ include: {
children: {
- select: { id: true, name: true, slug: true },
orderBy: { name: 'asc' },
},
},
- });
+ }) as any;
const collections = await prisma.collection.findMany({ orderBy: { name: 'asc' } });
const occasions = collections.filter((c) => c.group === 'OCCASION');
const recipients = collections.filter((c) => c.group === 'RECIPIENT');
@@ -49,6 +46,10 @@ export default async function EditProductPage({ params }: { params: { id: string
hPct: printAreaBack.hPct * 100,
} : undefined;
+ // Parse color photos for preview if no base image
+ const colorPhotos = row.colorPhotos ? JSON.parse(row.colorPhotos) : undefined;
+ const colorPhotosBack = row.colorPhotosBack ? JSON.parse(row.colorPhotosBack) : undefined;
+
return (
@@ -279,12 +280,12 @@ export default async function EditProductPage({ params }: { params: { id: string
diff --git a/src/app/admin/products/actions.ts b/src/app/admin/products/actions.ts
index c2651da..58715cc 100644
--- a/src/app/admin/products/actions.ts
+++ b/src/app/admin/products/actions.ts
@@ -276,10 +276,10 @@ export async function updateProduct(formData: FormData) {
referenceWidthCm: number | null;
referenceHeightCm: number | null;
weightGrams: number | null;
- imageUrl?: string;
- imageUrlBack?: string;
+ imageUrl?: string | null;
+ imageUrlBack?: string | null;
printArea?: string;
- printAreaBack?: string;
+ printAreaBack?: string | null;
} = {
name,
category,
diff --git a/src/app/admin/products/new/page.tsx b/src/app/admin/products/new/page.tsx
index 9cfcf94..59e8893 100644
--- a/src/app/admin/products/new/page.tsx
+++ b/src/app/admin/products/new/page.tsx
@@ -10,16 +10,12 @@ export default async function NewProductPage() {
const categories = await prisma.category.findMany({
where: { parentId: null },
orderBy: { name: 'asc' },
- select: {
- id: true,
- name: true,
- slug: true,
+ include: {
children: {
- select: { id: true, name: true, slug: true },
orderBy: { name: 'asc' },
},
},
- });
+ }) as any;
const collections = await prisma.collection.findMany({ orderBy: { name: 'asc' } });
const occasions = collections.filter((c) => c.group === 'OCCASION');
const recipients = collections.filter((c) => c.group === 'RECIPIENT');
diff --git a/src/app/admin/products/page.tsx b/src/app/admin/products/page.tsx
index a92958e..13c0569 100644
--- a/src/app/admin/products/page.tsx
+++ b/src/app/admin/products/page.tsx
@@ -9,13 +9,14 @@ import { deleteProduct } from './actions';
export default async function AdminProductsPage({
searchParams,
}: {
- searchParams: { error?: string };
+ searchParams: Promise<{ error?: string }>;
}) {
+ const { error } = await searchParams;
const activePromotions = await fetchActivePromotions();
- let products = [];
+ let products: any[] = [];
try {
const rows = await prisma.product.findMany();
- products = rows.map((p) => toProductDTO(p, activePromotions)).sort((a, b) =>
+ products = rows.map((p: any) => toProductDTO(p, activePromotions)).sort((a: any, b: any) =>
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
);
} catch (err) {
@@ -35,7 +36,7 @@ export default async function AdminProductsPage({
- {searchParams.error === 'in_use' && (
+ {error === 'in_use' && (
Can't delete that product — it has existing orders or design proofs tied to it.
diff --git a/src/app/api/checkout/route.ts b/src/app/api/checkout/route.ts
index d71b776..12f60f4 100644
--- a/src/app/api/checkout/route.ts
+++ b/src/app/api/checkout/route.ts
@@ -130,7 +130,7 @@ export async function POST(req: Request) {
// Calculate shipping cost (skip for collection orders)
let shippingCost = 0;
- let selectedShippingService = shippingService;
+ let selectedShippingService: string | undefined | null = shippingService;
if (isCollection) {
console.log('Collection order - no shipping cost');
@@ -193,7 +193,7 @@ export async function POST(req: Request) {
email: guest?.email ?? customer?.email,
guestName: guest?.name,
guestPhone: guest?.phone,
- isCollectionPickup,
+ isCollectionPickup: isCollection,
items: {
create: pricedItems.map((i) => ({
productId: i.productId,
diff --git a/src/app/api/designs/[id]/approve/route.ts b/src/app/api/designs/[id]/approve/route.ts
index 8f5d993..1e46dda 100644
--- a/src/app/api/designs/[id]/approve/route.ts
+++ b/src/app/api/designs/[id]/approve/route.ts
@@ -5,14 +5,15 @@ import { toProductDTO } from '@/lib/product';
export async function POST(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
const body = await req.json();
const { expectedDeliveryDate } = body;
const design = await prisma.designApproval.findUnique({
- where: { id: params.id },
+ where: { id },
include: { product: true },
});
@@ -21,7 +22,7 @@ export async function POST(
}
const updated = await prisma.designApproval.update({
- where: { id: params.id },
+ where: { id },
data: {
status: 'APPROVED',
expectedDeliveryDate: expectedDeliveryDate ? new Date(expectedDeliveryDate) : null,
diff --git a/src/app/api/designs/[id]/messages/route.ts b/src/app/api/designs/[id]/messages/route.ts
index 9334425..e9533be 100644
--- a/src/app/api/designs/[id]/messages/route.ts
+++ b/src/app/api/designs/[id]/messages/route.ts
@@ -4,11 +4,12 @@ import type { DesignApprovalMessage } from '@prisma/client';
export async function GET(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
const messages = await prisma.designApprovalMessage.findMany({
- where: { designId: params.id },
+ where: { designId: id },
orderBy: { createdAt: 'asc' },
});
@@ -24,9 +25,10 @@ export async function GET(
export async function POST(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
const { sender, body, role, message, messageType } = await req.json();
// Support both old format (sender/body) and new format (role/message/messageType)
@@ -43,7 +45,7 @@ export async function POST(
const created = await prisma.designApprovalMessage.create({
data: {
- designId: params.id,
+ designId: id,
sender: finalSender,
body: finalBody,
messageType: finalMessageType,
diff --git a/src/app/api/designs/[id]/modify/route.ts b/src/app/api/designs/[id]/modify/route.ts
index 8cca751..846c181 100644
--- a/src/app/api/designs/[id]/modify/route.ts
+++ b/src/app/api/designs/[id]/modify/route.ts
@@ -3,9 +3,10 @@ import { prisma } from '@/lib/prisma';
export async function POST(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
const {
designJson,
designPreviewUrl,
@@ -17,7 +18,7 @@ export async function POST(
} = await req.json();
const design = await prisma.designApproval.findUnique({
- where: { id: params.id },
+ where: { id: id },
});
if (!design) {
@@ -25,7 +26,7 @@ export async function POST(
}
const updated = await prisma.designApproval.update({
- where: { id: params.id },
+ where: { id: id },
data: {
...(designJson && { designJson }),
...(designPreviewUrl && { designPreviewUrl }),
diff --git a/src/app/api/designs/[id]/route.ts b/src/app/api/designs/[id]/route.ts
index 77ac59c..7ebcfda 100644
--- a/src/app/api/designs/[id]/route.ts
+++ b/src/app/api/designs/[id]/route.ts
@@ -5,11 +5,12 @@ import { toProductDTO } from '@/lib/product';
export async function GET(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
const design = await prisma.designApproval.findUnique({
- where: { id: params.id },
+ where: { id },
include: { product: true, messages: { orderBy: { createdAt: 'asc' } } },
});
@@ -32,9 +33,10 @@ export async function GET(
export async function PATCH(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
const customer = await getCurrentCustomer();
if (!customer) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
@@ -43,7 +45,7 @@ export async function PATCH(
const { status } = await req.json();
const updated = await prisma.designApproval.update({
- where: { id: params.id },
+ where: { id },
data: { status },
include: { product: true, messages: { orderBy: { createdAt: 'asc' } } },
});
@@ -63,17 +65,18 @@ export async function PATCH(
export async function DELETE(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
// Delete associated messages first
await prisma.designApprovalMessage.deleteMany({
- where: { designId: params.id },
+ where: { designId: id },
});
// Delete the design approval
await prisma.designApproval.delete({
- where: { id: params.id },
+ where: { id: id },
});
return NextResponse.json({ success: true });
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 2df0d71..362a9fe 100644
--- a/src/app/api/designs/[id]/send-to-customer/route.ts
+++ b/src/app/api/designs/[id]/send-to-customer/route.ts
@@ -4,13 +4,14 @@ import { sendDesignReadyForReview } from '@/lib/mail';
export async function POST(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
const { designPreviewUrl, designPreviewUrlBack, placementPreviewUrl, placementPreviewUrlBack } = await req.json().catch(() => ({}));
const design = await prisma.designApproval.findUnique({
- where: { id: params.id },
+ where: { id: id },
include: { product: true },
});
@@ -26,7 +27,7 @@ export async function POST(
if (placementPreviewUrlBack) updateData.placementPreviewUrlBack = placementPreviewUrlBack;
const updated = await prisma.designApproval.update({
- where: { id: params.id },
+ where: { id: id },
data: updateData,
include: { product: true, messages: { orderBy: { createdAt: 'asc' } } },
});
@@ -36,7 +37,7 @@ export async function POST(
customerName: design.customerName || 'Customer',
customerEmail: design.customerEmail,
productName: design.product.name,
- reviewLink: `${process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'}/designs/${params.id}/review`,
+ reviewLink: `${process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'}/designs/${id}/review`,
});
return NextResponse.json(updated);
diff --git a/src/app/api/orders/[id]/design-spec/route.ts b/src/app/api/orders/[id]/design-spec/route.ts
index b7eaf80..f8dfde3 100644
--- a/src/app/api/orders/[id]/design-spec/route.ts
+++ b/src/app/api/orders/[id]/design-spec/route.ts
@@ -1,9 +1,9 @@
import { NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
-export async function GET(req: Request, { params }: { params: { id: string } }) {
+export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
try {
- const { id } = params;
+ const { id } = await params;
const order = await prisma.order.findUnique({
where: { id },
include: { items: true },
@@ -292,7 +292,7 @@ export async function GET(req: Request, { params }: { params: { id: string } })
${item.designImageDimensions ? (() => {
try {
const imageDims = JSON.parse(item.designImageDimensions);
- return imageDims && imageDims.length > 0 ? `
Image Dimensions:
${imageDims.map((img, idx) => `- Image ${idx + 1}: ${img.widthCm} cm × ${img.heightCm} cm
`).join('')}
` : '';
+ return imageDims && imageDims.length > 0 ? `
Image Dimensions:
${imageDims.map((img: any, idx: any) => `- Image ${idx + 1}: ${img.widthCm} cm × ${img.heightCm} cm
`).join('')}
` : '';
} catch (e) {
return '';
}
diff --git a/src/app/api/orders/[id]/sync-tracking/route.ts b/src/app/api/orders/[id]/sync-tracking/route.ts
index 26579aa..6de2952 100644
--- a/src/app/api/orders/[id]/sync-tracking/route.ts
+++ b/src/app/api/orders/[id]/sync-tracking/route.ts
@@ -4,11 +4,12 @@ import { fetchRoyalMailTracking, mapTrackingStatusToOrderStatus } from '@/lib/ro
export async function POST(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
const order = await prisma.order.findUnique({
- where: { id: params.id },
+ where: { id: id },
});
if (!order) {
@@ -31,13 +32,13 @@ export async function POST(
// Update order with new status
const updated = await prisma.order.update({
- where: { id: params.id },
+ where: { id: id },
data: {
status: newStatus,
},
});
- console.log(`✓ Order ${params.id} tracking synced: ${trackingData.status} → ${newStatus}`);
+ console.log(`✓ Order ${id} tracking synced: ${trackingData.status} → ${newStatus}`);
return NextResponse.json({
success: true,
diff --git a/src/app/api/proofs/[id]/approve/route.ts b/src/app/api/proofs/[id]/approve/route.ts
index ed4b31e..be5344c 100644
--- a/src/app/api/proofs/[id]/approve/route.ts
+++ b/src/app/api/proofs/[id]/approve/route.ts
@@ -1,14 +1,15 @@
import { NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
-export async function POST(_req: Request, { params }: { params: { id: string } }) {
- const proof = await prisma.designProof.findUnique({ where: { id: params.id } });
+export async function POST(_req: Request, { params }: { params: Promise<{ id: string }> }) {
+ const { id } = await params;
+ const proof = await prisma.designProof.findUnique({ where: { id } });
if (!proof) {
return NextResponse.json({ error: 'Not found' }, { status: 404 });
}
await prisma.designProof.update({
- where: { id: params.id },
+ where: { id },
data: { status: 'APPROVED' },
});
diff --git a/src/app/api/proofs/[id]/messages/route.ts b/src/app/api/proofs/[id]/messages/route.ts
index 81b1d2d..30295dc 100644
--- a/src/app/api/proofs/[id]/messages/route.ts
+++ b/src/app/api/proofs/[id]/messages/route.ts
@@ -2,13 +2,14 @@ import { NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import type { MessageDTO } from '@/lib/types';
-export async function GET(_req: Request, { params }: { params: { id: string } }) {
+export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
+ const { id } = await params;
const rows = await prisma.message.findMany({
- where: { proofId: params.id },
+ where: { proofId: id },
orderBy: { createdAt: 'asc' },
});
- const messages: MessageDTO[] = rows.map((m) => ({
+ const messages: MessageDTO[] = rows.map((m: any) => ({
id: m.id,
proofId: m.proofId,
sender: m.sender as MessageDTO['sender'],
@@ -19,7 +20,8 @@ export async function GET(_req: Request, { params }: { params: { id: string } })
return NextResponse.json(messages);
}
-export async function POST(req: Request, { params }: { params: { id: string } }) {
+export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
+ const { id } = await params;
const { sender, body } = await req.json();
if (sender !== 'ADMIN' && sender !== 'CUSTOMER') {
@@ -30,13 +32,13 @@ export async function POST(req: Request, { params }: { params: { id: string } })
return NextResponse.json({ error: 'Message body is required' }, { status: 400 });
}
- const proof = await prisma.designProof.findUnique({ where: { id: params.id } });
+ const proof = await prisma.designProof.findUnique({ where: { id } });
if (!proof) {
return NextResponse.json({ error: 'Not found' }, { status: 404 });
}
const created = await prisma.message.create({
- data: { proofId: params.id, sender, body: trimmed },
+ data: { proofId: id, sender, body: trimmed },
});
const message: MessageDTO = {
diff --git a/src/app/api/reviews/[id]/route.ts b/src/app/api/reviews/[id]/route.ts
index 2898112..18ca4bd 100644
--- a/src/app/api/reviews/[id]/route.ts
+++ b/src/app/api/reviews/[id]/route.ts
@@ -3,11 +3,12 @@ import { prisma } from '@/lib/prisma';
export async function DELETE(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
const review = await prisma.review.delete({
- where: { id: params.id },
+ where: { id: id },
});
return NextResponse.json(review);
@@ -22,14 +23,15 @@ export async function DELETE(
export async function PATCH(
req: Request,
- { params }: { params: { id: string } }
+ { params }: { params: Promise<{ id: string }> }
) {
try {
+ const { id } = await params;
const body = await req.json();
const { approved } = body;
const review = await prisma.review.update({
- where: { id: params.id },
+ where: { id: id },
data: { approved },
});
diff --git a/src/app/contact/actions.ts b/src/app/contact/actions.ts
index 27906af..297976e 100644
--- a/src/app/contact/actions.ts
+++ b/src/app/contact/actions.ts
@@ -15,7 +15,7 @@ export async function sendContactMessage(formData: FormData) {
redirect('/contact?error=missing');
}
- const ip = getClientIp(headers());
+ const ip = getClientIp(await headers());
const turnstileToken = String(formData.get('cf-turnstile-response') ?? '');
const { success } = await verifyTurnstileToken(turnstileToken, ip);
if (!success) {
diff --git a/src/app/custom-request/actions.ts b/src/app/custom-request/actions.ts
index c8c7b17..05705a6 100644
--- a/src/app/custom-request/actions.ts
+++ b/src/app/custom-request/actions.ts
@@ -20,7 +20,7 @@ export async function createCustomRequest(formData: FormData) {
throw new Error('Missing required fields: product, name, email, and a photo are all required.');
}
- const ip = getClientIp(headers());
+ const ip = getClientIp(await headers());
const turnstileToken = String(formData.get('cf-turnstile-response') ?? '');
const { success } = await verifyTurnstileToken(turnstileToken, ip);
if (!success) {
diff --git a/src/app/designs/[id]/review/page.tsx b/src/app/designs/[id]/review/page.tsx
index 5ce6314..fd1a048 100644
--- a/src/app/designs/[id]/review/page.tsx
+++ b/src/app/designs/[id]/review/page.tsx
@@ -19,7 +19,9 @@ interface DesignApproval {
};
designJson: string;
designPreviewUrl: string;
+ designPreviewUrlBack: string | null;
placementPreviewUrl: string | null;
+ placementPreviewUrlBack: string | null;
color: string;
size: string | null;
status: string;
@@ -67,7 +69,7 @@ export default function DesignReviewPage({ params }: { params: { id: string } })
size: design.size,
quantity: 1,
unitPrice: design.product.personalisedPrice ?? design.product.effectivePrice,
- designJson: design.designJson,
+ designJson: JSON.parse(design.designJson),
designPreviewUrl: design.designPreviewUrl,
designPreviewUrlBack: design.designPreviewUrlBack,
placementPreviewUrl: design.placementPreviewUrl,
diff --git a/src/app/gallery/page.tsx b/src/app/gallery/page.tsx
index d8dec00..87f3755 100644
--- a/src/app/gallery/page.tsx
+++ b/src/app/gallery/page.tsx
@@ -2,10 +2,11 @@ import Link from 'next/link';
import { prisma } from '@/lib/prisma';
import Reveal from '@/components/Reveal';
-export default async function GalleryPage({ searchParams }: { searchParams: { category?: string } }) {
+export default async function GalleryPage({ searchParams }: { searchParams: Promise<{ category?: string }> }) {
+ const { category } = await searchParams;
const categories = await prisma.galleryCategory.findMany({ orderBy: { name: 'asc' } });
- const activeSlug = searchParams.category ?? null;
+ const activeSlug = category ?? null;
const activeCategory = activeSlug ? categories.find((c) => c.slug === activeSlug) ?? null : null;
const photos = await prisma.galleryPhoto.findMany({
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 9edce90..334a64f 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -7,6 +7,7 @@ import Footer from '@/components/Footer';
import PromotionBanner from '@/components/PromotionBanner';
import MaintenancePage from '@/components/MaintenancePage';
import LogoutOnUnload from '@/components/LogoutOnUnload';
+import ThemeInitializer from '@/components/ThemeInitializer';
import { CurrencyProvider } from '@/lib/CurrencyContext';
import { getSiteSettings } from '@/lib/settings';
import { isAdminAuthenticated } from '@/lib/adminAuth';
@@ -31,7 +32,7 @@ export const metadata: Metadata = {
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const { maintenanceMode, maintenanceMessage } = await getSiteSettings();
- const pathname = headers().get('x-pathname') ?? '';
+ const pathname = (await headers()).get('x-pathname') ?? '';
// Admins bypass maintenance (they can keep working); /admin/* is always
// reachable so the owner can log in and toggle it back off.
// Also allow /checkout/* pages so customers can complete purchases and see results.
@@ -50,21 +51,9 @@ export default async function RootLayout({ children }: { children: React.ReactNo
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@600&family=Oswald:wght@500&family=Bebas+Neue&family=Pacifico&family=Permanent+Marker&family=Caveat:wght@600&display=swap"
rel="stylesheet"
/>
- {/* Runs before paint so there's no flash of the wrong theme on load. */}
-
+
{showMaintenance ? (
diff --git a/src/app/made-to-order/[slug]/page.tsx b/src/app/made-to-order/[slug]/page.tsx
index fb11288..5924310 100644
--- a/src/app/made-to-order/[slug]/page.tsx
+++ b/src/app/made-to-order/[slug]/page.tsx
@@ -1,22 +1,22 @@
import { notFound } from 'next/navigation';
-import dynamic from 'next/dynamic';
import { prisma } from '@/lib/prisma';
import { toProductDTO } from '@/lib/product';
import { fetchActivePromotions } from '@/lib/promotions';
import ProductCard from '@/components/ProductCard';
-
-const DesignCanvas = dynamic(() => import('@/components/DesignCanvas'), { ssr: false });
+import DesignCanvasWrapper from '@/components/DesignCanvasWrapper';
export default async function ProductDetailPage({
params,
searchParams,
}: {
- params: { slug: string };
- searchParams: { customRequestId?: string };
+ params: Promise<{ slug: string }>;
+ searchParams: Promise<{ customRequestId?: string }>;
}) {
+ const { slug } = await params;
+ const { customRequestId } = await searchParams;
const activePromotions = await fetchActivePromotions();
- const row = await prisma.product.findUnique({ where: { slug: params.slug } });
+ const row = await prisma.product.findUnique({ where: { slug } });
if (!row || !row.showOnPersonalised) notFound();
const product = toProductDTO(row, activePromotions, 'personalised');
@@ -29,9 +29,9 @@ export default async function ProductDetailPage({
// Fetch custom photo from request if customRequestId is provided
let customPhoto: string | undefined;
- if (searchParams.customRequestId) {
+ if (customRequestId) {
const customRequest = await prisma.customRequest.findUnique({
- where: { id: searchParams.customRequestId },
+ where: { id: customRequestId },
});
if (customRequest) {
customPhoto = customRequest.imageDataUrl;
@@ -40,7 +40,7 @@ export default async function ProductDetailPage({
return (
-
+
{related.length > 0 && (
diff --git a/src/app/made-to-order/page.tsx b/src/app/made-to-order/page.tsx
index 0712158..0537e17 100644
--- a/src/app/made-to-order/page.tsx
+++ b/src/app/made-to-order/page.tsx
@@ -9,8 +9,9 @@ type SearchParams = {
const PRODUCTS_PER_PAGE = 20;
-export default async function ProductsPage({ searchParams }: { searchParams: SearchParams }) {
- const currentPage = Math.max(1, Number(searchParams.page) || 1);
+export default async function ProductsPage({ searchParams }: { searchParams: Promise }) {
+ const { page } = await searchParams;
+ const currentPage = Math.max(1, Number(page) || 1);
const activePromotions = await fetchActivePromotions();
const [rows, totalCount] = await Promise.all([
diff --git a/src/components/AdminDesignEditor.tsx b/src/components/AdminDesignEditor.tsx
index 8f8401b..25140a4 100644
--- a/src/components/AdminDesignEditor.tsx
+++ b/src/components/AdminDesignEditor.tsx
@@ -2,7 +2,7 @@
import { useRef, useState, useEffect } from 'react';
import { Stage, Layer, Text, Image as KonvaImage, Transformer } from 'react-konva';
-import type Konva from 'konva';
+import Konva from 'konva';
import type { DesignElement } from '@/lib/types';
interface AdminDesignEditorProps {
diff --git a/src/components/ApproveSection.tsx b/src/components/ApproveSection.tsx
index ec400a0..d7f199d 100644
--- a/src/components/ApproveSection.tsx
+++ b/src/components/ApproveSection.tsx
@@ -26,11 +26,13 @@ export default function ApproveSection({ proof }: { proof: ProofDTO }) {
size: null,
quantity: proof.quantity,
unitPrice: proof.unitPrice,
- designJson: { front: [], back: [] },
+ designJson: { front: [], back: [] } as any,
designPreviewUrl: proof.imageDataUrl,
designPreviewUrlBack: null,
placementPreviewUrl: proof.imageDataUrl,
placementPreviewUrlBack: null,
+ designWidthCm: null,
+ designHeightCm: null,
});
setStatus('APPROVED');
router.push('/cart');
diff --git a/src/components/DesignCanvas.tsx b/src/components/DesignCanvas.tsx
index 0c10706..9ed3e01 100644
--- a/src/components/DesignCanvas.tsx
+++ b/src/components/DesignCanvas.tsx
@@ -325,12 +325,13 @@ function PrintSurface({
/>
{elements.map((el) =>
el.type === 'text' ? (
- el.curvedPath ? (
+ (el as any).curvedPath ? (
// Curved text - TextPath with dragging via offset
(() => {
+ const elAny = el as any;
const offsetX = el.x - 280;
const offsetY = el.y - 280;
- const pathData = generateCurvePath(el.curvedPath.type, el.curvedPath.radius, el.curvedPath.reverse, offsetX, offsetY);
+ const pathData = generateCurvePath(elAny.curvedPath.type, elAny.curvedPath.radius, elAny.curvedPath.reverse, offsetX, offsetY);
console.log(`🔄 Rendering curved text ${el.text}: position (${el.x}, ${el.y}), offset (${offsetX}, ${offsetY})`);
return (
@@ -1493,20 +1494,24 @@ export default function DesignCanvas({
- {text.curvedPath && (
+ {(text as any).curvedPath && (
+ {(() => {
+ const textAny = text as any;
+ return (
+ <>
+ >
+ );
+ })()}
)}
diff --git a/src/components/DesignCanvasErrorBoundary.tsx b/src/components/DesignCanvasErrorBoundary.tsx
new file mode 100644
index 0000000..0c40b78
--- /dev/null
+++ b/src/components/DesignCanvasErrorBoundary.tsx
@@ -0,0 +1,39 @@
+'use client';
+
+import React, { ReactNode } from 'react';
+
+interface Props {
+ children: ReactNode;
+}
+
+interface State {
+ hasError: boolean;
+}
+
+class DesignCanvasErrorBoundary extends React.Component