diff --git a/src/app/account/profile/page.tsx b/src/app/account/profile/page.tsx new file mode 100644 index 0000000..f1ac6cb --- /dev/null +++ b/src/app/account/profile/page.tsx @@ -0,0 +1,165 @@ +'use client' +import { useEffect, useState } from 'react' +import { usePathname, useRouter } from 'next/navigation' +import Link from 'next/link' +import { Customer, useCustomerAuth } from '@/hooks/useCustomerAuth' + +const COMMERCE_URL = process.env.NEXT_PUBLIC_VULA_COMMERCE_URL || 'https://ide.vulai.co.za' +const PROJECT_ID = process.env.NEXT_PUBLIC_VULA_PROJECT_ID || '' + +function AccountShell({ customer, onLogout, children }: { customer: Customer | null; onLogout: () => void; children: React.ReactNode }) { + const pathname = usePathname() + const navLink = (href: string, label: string) => ( + + {label} + + ) + return ( +
+
+
+
+

My Account

+ {customer &&

{customer.first_name} {customer.last_name}

} +
+ +
+ + {children} +
+
+ ) +} + +export default function AccountProfilePage() { + const { customer, isAuthenticated, loading: authLoading, logout, updateProfile } = useCustomerAuth() + const router = useRouter() + const [saving, setSaving] = useState(false) + const [saved, setSaved] = useState(false) + const [error, setError] = useState(null) + const [form, setForm] = useState({ + first_name: '', last_name: '', phone: '', + address_line1: '', address_line2: '', city: '', province: '', postal_code: '', + current_password: '', new_password: '', confirm_password: '', + }) + + useEffect(() => { + if (!authLoading && !isAuthenticated) router.replace('/account/login') + }, [authLoading, isAuthenticated, router]) + + useEffect(() => { + if (!customer) return + setForm(f => ({ + ...f, + first_name: customer.first_name ?? '', + last_name: customer.last_name ?? '', + phone: customer.phone ?? '', + address_line1: customer.address_line1 ?? '', + address_line2: customer.address_line2 ?? '', + city: customer.city ?? '', + province: customer.province ?? '', + postal_code: customer.postal_code ?? '', + })) + }, [customer]) + + const set = (k: string) => (e: React.ChangeEvent) => + setForm(f => ({ ...f, [k]: e.target.value })) + + const handleLogout = () => { logout(); router.push('/') } + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setError(null) + if (form.new_password && form.new_password !== form.confirm_password) { + setError('New passwords do not match'); return + } + setSaving(true) + try { + await updateProfile({ + first_name: form.first_name || undefined, + last_name: form.last_name || undefined, + phone: form.phone || undefined, + address_line1: form.address_line1 || undefined, + address_line2: form.address_line2 || undefined, + city: form.city || undefined, + province: form.province || undefined, + postal_code: form.postal_code || undefined, + ...(form.new_password ? { password: form.current_password, new_password: form.new_password } : {}), + }) + setSaved(true) + setTimeout(() => setSaved(false), 3000) + setForm(f => ({ ...f, current_password: '', new_password: '', confirm_password: '' })) + } catch (e: any) { setError(e.message) } + finally { setSaving(false) } + } + + if (authLoading) return ( +
+
+
+ ) + + const inputCls = "w-full px-3 py-2.5 border border-border rounded-lg bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/30 text-sm" + const labelCls = "block text-sm font-medium mb-1.5 text-foreground" + + return ( + +
+ + {/* Personal details */} +
+

Personal details

+
+
+
+
+
+ + +

Email address cannot be changed

+
+
+
+ + {/* Delivery address */} +
+
+

Delivery address

+

Saved address auto-fills at checkout

+
+
+
+ + +
+
+
+
+
+
+
+ + {/* Change password */} +
+

Change password

+
+
+
+
+
+
+ + {error &&

{error}

} + {saved &&

Profile saved successfully

} + + +
+
+ ) +}