diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 7ee78c5..d10072e 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -75,7 +75,8 @@ "Bash(git commit -m 'Fix: Handle undefined designElements in design spec SVG rendering *)", "Bash(git commit -m 'Fix: Ensure cart is completely cleared after payment success *)", "Bash(git commit -m 'Fix: Update order status to PAID on checkout success page *)", - "Bash(git commit -m 'Improve: Cart clearing on success page with better state management *)" + "Bash(git commit -m 'Improve: Cart clearing on success page with better state management *)", + "Bash(git commit -m 'Feature: Add order tracking and shipping status updates *)" ] } } diff --git a/prisma/migrations/20260718193316_add_order_tracking/migration.sql b/prisma/migrations/20260718193316_add_order_tracking/migration.sql new file mode 100644 index 0000000..165eab2 --- /dev/null +++ b/prisma/migrations/20260718193316_add_order_tracking/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE "Order" ADD COLUMN "carrier" TEXT, +ADD COLUMN "estimatedDeliveryDate" TIMESTAMP(3), +ADD COLUMN "trackingNumber" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 18f48bd..5a6a892 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -218,9 +218,15 @@ model Order { stripeSessionId String? @unique email String? shippingAddress String? // JSON — collected by Stripe Checkout, saved once payment completes - status String @default("PENDING") // PENDING | PAID | FAILED + status String @default("PENDING") // PENDING | PAID | PRINTING | PRINTED | SHIPPED | DELIVERED | FAILED archived Boolean @default(false) total Int // cents + + // Shipping & tracking info + carrier String? // e.g. "Royal Mail", "DPD", "Courier" + trackingNumber String? // tracking number from carrier + estimatedDeliveryDate DateTime? // estimated delivery date + createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/src/app/account/page.tsx b/src/app/account/page.tsx index ad1593c..550938b 100644 --- a/src/app/account/page.tsx +++ b/src/app/account/page.tsx @@ -90,22 +90,37 @@ export default async function AccountPage() { ) : (
{orders.map((order) => ( - -
-

- {new Date(order.createdAt).toLocaleDateString(undefined, { dateStyle: 'medium' })} ·{' '} - {order.items.length} item{order.items.length === 1 ? '' : 's'} -

- - {statusLabel(order.status)} - -
- {formatPrice(order.total)} - +
+ +
+

+ {new Date(order.createdAt).toLocaleDateString(undefined, { dateStyle: 'medium' })} ·{' '} + {order.items.length} item{order.items.length === 1 ? '' : 's'} +

+ + {statusLabel(order.status)} + +
+ {formatPrice(order.total)} + + + {/* Tracking Information */} + {order.carrier && order.trackingNumber && ( +
+

+ Tracking: {order.carrier} · {order.trackingNumber} +

+ {order.estimatedDeliveryDate && ( +

+ Est. delivery: {new Date(order.estimatedDeliveryDate).toLocaleDateString(undefined, { dateStyle: 'medium' })} +

+ )} +
+ )} +
))}
)} diff --git a/src/app/admin/orders/[id]/page.tsx b/src/app/admin/orders/[id]/page.tsx index f37eadc..cb1f20c 100644 --- a/src/app/admin/orders/[id]/page.tsx +++ b/src/app/admin/orders/[id]/page.tsx @@ -1,7 +1,7 @@ import { notFound } from 'next/navigation'; import { prisma } from '@/lib/prisma'; import AdminNav from '@/components/AdminNav'; -import { archiveOrder, unarchiveOrder } from '../actions'; +import { archiveOrder, unarchiveOrder, updateOrderTracking } from '../actions'; function formatPrice(cents: number) { return `£${(cents / 100).toFixed(2)}`; @@ -76,6 +76,72 @@ export default async function OrderDetailPage({ params }: { params: { id: string )} + {/* Tracking Information */} +
+

Shipping & Tracking

+
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+
+
{order.items.map((item) => { let hasFrontDesign = false; diff --git a/src/app/admin/orders/actions.ts b/src/app/admin/orders/actions.ts index 1b93e4e..518640b 100644 --- a/src/app/admin/orders/actions.ts +++ b/src/app/admin/orders/actions.ts @@ -35,3 +35,25 @@ export async function unarchiveOrder(formData: FormData) { await prisma.order.update({ where: { id }, data: { archived: false } }); redirect('/admin/orders?archived=1'); } + +export async function updateOrderTracking(formData: FormData) { + const id = String(formData.get('orderId') ?? ''); + const status = String(formData.get('status') ?? 'PENDING'); + const carrier = formData.get('carrier') ? String(formData.get('carrier')) : null; + const trackingNumber = formData.get('trackingNumber') ? String(formData.get('trackingNumber')) : null; + const estimatedDeliveryDate = formData.get('estimatedDeliveryDate') ? new Date(String(formData.get('estimatedDeliveryDate'))) : null; + + if (!id) return; + + await prisma.order.update({ + where: { id }, + data: { + status, + carrier, + trackingNumber, + estimatedDeliveryDate, + }, + }); + + // TODO: Send email notification to customer when status changes +}