diff --git a/prisma/migrations/20260719085000_add_customer_address_fields/migration.sql b/prisma/migrations/20260719085000_add_customer_address_fields/migration.sql
new file mode 100644
index 0000000..c85cb46
--- /dev/null
+++ b/prisma/migrations/20260719085000_add_customer_address_fields/migration.sql
@@ -0,0 +1,17 @@
+-- AddField phone to Customer
+ALTER TABLE "Customer" ADD COLUMN "phone" TEXT;
+
+-- AddField addressLine1 to Customer
+ALTER TABLE "Customer" ADD COLUMN "addressLine1" TEXT;
+
+-- AddField addressLine2 to Customer
+ALTER TABLE "Customer" ADD COLUMN "addressLine2" TEXT;
+
+-- AddField city to Customer
+ALTER TABLE "Customer" ADD COLUMN "city" TEXT;
+
+-- AddField postalCode to Customer
+ALTER TABLE "Customer" ADD COLUMN "postalCode" TEXT;
+
+-- AddField country to Customer
+ALTER TABLE "Customer" ADD COLUMN "country" TEXT;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 08d6c98..50bf208 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -250,6 +250,12 @@ model Customer {
email String @unique
passwordHash String
name String?
+ phone String? // delivery phone number
+ addressLine1 String? // street address
+ addressLine2 String? // apartment, suite, etc.
+ city String?
+ postalCode String?
+ country String? // ISO 2-letter country code, e.g., "GB"
createdAt DateTime @default(now())
orders Order[]
diff --git a/src/app/account/actions.ts b/src/app/account/actions.ts
index c1786b1..1edc04b 100644
--- a/src/app/account/actions.ts
+++ b/src/app/account/actions.ts
@@ -149,3 +149,30 @@ export async function resetPassword(formData: FormData) {
await createSession(resetToken.customerId);
redirect('/account');
}
+
+export async function updateCustomerAddress(formData: FormData) {
+ const { getCurrentCustomer } = await import('@/lib/auth');
+ const customer = await getCurrentCustomer();
+ if (!customer) redirect('/account/login');
+
+ const phone = String(formData.get('phone') ?? '').trim();
+ const addressLine1 = String(formData.get('addressLine1') ?? '').trim();
+ const addressLine2 = String(formData.get('addressLine2') ?? '').trim();
+ const city = String(formData.get('city') ?? '').trim();
+ const postalCode = String(formData.get('postalCode') ?? '').trim();
+ const country = String(formData.get('country') ?? '').trim();
+
+ await prisma.customer.update({
+ where: { id: customer.id },
+ data: {
+ phone: phone || null,
+ addressLine1: addressLine1 || null,
+ addressLine2: addressLine2 || null,
+ city: city || null,
+ postalCode: postalCode || null,
+ country: country || null,
+ },
+ });
+
+ redirect('/account?addressUpdated=1');
+}
diff --git a/src/app/account/page.tsx b/src/app/account/page.tsx
index 550938b..ed099da 100644
--- a/src/app/account/page.tsx
+++ b/src/app/account/page.tsx
@@ -3,6 +3,8 @@ import { redirect } from 'next/navigation';
import { getCurrentCustomer } from '@/lib/auth';
import { prisma } from '@/lib/prisma';
import { logoutCustomer } from './actions';
+import AddressFormSection from '@/components/AddressFormSection';
+import OlderOrders from '@/components/OlderOrders';
function formatPrice(cents: number) {
return `£${(cents / 100).toFixed(2)}`;
@@ -36,6 +38,13 @@ export default async function AccountPage() {
orderBy: { createdAt: 'desc' },
});
+ // Separate recent (< 7 days) and older (>= 7 days) orders
+ const now = new Date();
+ const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
+
+ const recentOrders = orders.filter(order => new Date(order.createdAt) > sevenDaysAgo);
+ const olderOrders = orders.filter(order => new Date(order.createdAt) <= sevenDaysAgo);
+
return (
@@ -56,6 +65,8 @@ export default async function AccountPage() {
+
+
Design reviews
{designReviews.length === 0 ? (
No designs awaiting review.
@@ -88,41 +99,49 @@ export default async function AccountPage() {
{orders.length === 0 ? (
No orders yet.
) : (
-
- {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)}
-
+ <>
+ {recentOrders.length > 0 && (
+
+ {recentOrders.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)}
+
- {/* Tracking Information */}
- {order.carrier && order.trackingNumber && (
-
-
- Tracking: {order.carrier} · {order.trackingNumber}
-
- {order.estimatedDeliveryDate && (
-
- Est. delivery: {new Date(order.estimatedDeliveryDate).toLocaleDateString(undefined, { dateStyle: 'medium' })}
-
+ {/* Tracking Information */}
+ {order.carrier && order.trackingNumber && (
+
+
+ Tracking: {order.carrier} · {order.trackingNumber}
+
+ {order.estimatedDeliveryDate && (
+
+ Est. delivery: {new Date(order.estimatedDeliveryDate).toLocaleDateString(undefined, { dateStyle: 'medium' })}
+
+ )}
+
)}
- )}
+ ))}
- ))}
-
+ )}
+
+ {olderOrders.length > 0 && (
+
+ )}
+ >
)}
);
diff --git a/src/app/checkout/client.tsx b/src/app/checkout/client.tsx
index 6a8574b..618a26f 100644
--- a/src/app/checkout/client.tsx
+++ b/src/app/checkout/client.tsx
@@ -1,6 +1,6 @@
'use client';
-import { useEffect, useState } from 'react';
+import { useEffect, useState, useCallback } from 'react';
import Link from 'next/link';
import { useCart } from '@/lib/cartStore';
import { loginCustomer } from '@/app/account/actions';
@@ -10,14 +10,15 @@ import ShippingSelector from '@/components/ShippingSelector';
interface CheckoutPageClientProps {
isLoggedIn: boolean;
customer?: any;
+ customerAddress?: any;
}
-export default function CheckoutPageClient({ isLoggedIn, customer }: CheckoutPageClientProps) {
+export default function CheckoutPageClient({ isLoggedIn, customer, customerAddress }: CheckoutPageClientProps) {
const [mounted, setMounted] = useState(false);
- const [mode, setMode] = useState<'logged-in' | 'login' | 'guest' | null>(isLoggedIn ? 'logged-in' : null);
+ const [mode, setMode] = useState<'logged-in' | 'login' | 'guest' | 'edit-address' | null>(isLoggedIn ? 'logged-in' : null);
const [checkingOut, setCheckingOut] = useState(false);
const [checkoutError, setCheckoutError] = useState
(null);
- const [shippingCountry, setShippingCountry] = useState('GB');
+ const [shippingCountry, setShippingCountry] = useState(customerAddress?.country || 'GB');
const [shippingService, setShippingService] = useState(null);
const [shippingCost, setShippingCost] = useState(0);
const [guestData, setGuestData] = useState({
@@ -26,11 +27,27 @@ export default function CheckoutPageClient({ isLoggedIn, customer }: CheckoutPag
phone: '',
address: '',
});
+ const [customerAddressData, setCustomerAddressData] = useState({
+ phone: customerAddress?.phone || '',
+ addressLine1: customerAddress?.addressLine1 || '',
+ addressLine2: customerAddress?.addressLine2 || '',
+ city: customerAddress?.city || '',
+ postalCode: customerAddress?.postalCode || '',
+ country: customerAddress?.country || 'GB',
+ });
const items = useCart((s) => s.items);
+ const removeItem = useCart((s) => s.removeItem);
+ const setQuantity = useCart((s) => s.setQuantity);
const subtotal = useCart((s) => s.total());
const total = subtotal + shippingCost;
+ const handleShippingSelect = useCallback((country: string, service: string, cost: number) => {
+ setShippingCountry(country);
+ setShippingService(service);
+ setShippingCost(cost);
+ }, []);
+
useEffect(() => setMounted(true), []);
if (!mounted) return null;
@@ -100,18 +117,47 @@ export default function CheckoutPageClient({ isLoggedIn, customer }: CheckoutPag
return (
-
Checkout
+
+
Checkout
+
+ ← Continue shopping
+
+
{/* Order summary */}
Order summary
-
+
{items.map((item) => (
-
+
{item.name}
-
Qty: {item.quantity}
+
+
+ {item.quantity}
+
+
+
@@ -144,13 +190,30 @@ export default function CheckoutPageClient({ isLoggedIn, customer }: CheckoutPag
Logged in as {customer?.email}
+
+
Delivery address
+ {customerAddressData.addressLine1 ? (
+
+
{customerAddressData.addressLine1}
+ {customerAddressData.addressLine2 &&
{customerAddressData.addressLine2}
}
+
{customerAddressData.city}, {customerAddressData.postalCode}
+
Phone: {customerAddressData.phone}
+
+ ) : (
+
No address saved. Please add one below.
+ )}
+
+
+
{
- setShippingCountry(country);
- setShippingService(service);
- setShippingCost(cost);
- }}
+ onShippingSelect={handleShippingSelect}
/>
+ ) : mode === 'edit-address' ? (
+
+
+
Edit delivery address
+
+
) : (
- {justAdded &&
Added to your bag.
}
setShowLoginPrompt(false)} />
+ setShowAddToCartModal(false)}
+ productName={product.name}
+ />
);
}