164 lines
6.7 KiB
TypeScript
164 lines
6.7 KiB
TypeScript
'use client';
|
||
|
||
import { useEffect } from 'react';
|
||
import Link from 'next/link';
|
||
import { useRouter } from 'next/navigation';
|
||
import { User, Package, MapPin, Phone, Mail, LogOut, Heart } from 'lucide-react';
|
||
import Button from '@/components/ui/Button';
|
||
import { useAuth } from '@/presentation/hooks/useAuth';
|
||
import { formatPrice, formatDate, getFormatLocale } from '@/lib/utils';
|
||
import { useTranslation } from '@/hooks/useTranslation';
|
||
import { localizeProduct } from '@/lib/product-i18n';
|
||
|
||
export default function AccountPage() {
|
||
const router = useRouter();
|
||
const { user, isAuthenticated, orders, logout } = useAuth();
|
||
const { t, locale } = useTranslation();
|
||
|
||
useEffect(() => {
|
||
if (!isAuthenticated) {
|
||
router.push('/login');
|
||
}
|
||
}, [isAuthenticated, router]);
|
||
|
||
if (!isAuthenticated || !user) {
|
||
return null;
|
||
}
|
||
|
||
const handleLogout = () => {
|
||
logout();
|
||
router.push('/');
|
||
};
|
||
|
||
const fmt = (n: number) => formatPrice(n, getFormatLocale(locale));
|
||
|
||
return (
|
||
<div className="bg-gray-50">
|
||
<div className="border-b border-gray-100 bg-white">
|
||
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
|
||
<h1 className="section-heading">{t('account.title')}</h1>
|
||
<p className="text-gray-500">{t('account.welcome', { name: user.name })}</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="mx-auto max-w-7xl px-4 py-10 sm:px-6 lg:px-8">
|
||
<div className="grid gap-8 lg:grid-cols-3">
|
||
<div className="space-y-6">
|
||
<div className="card-premium p-6">
|
||
<div className="mb-4 flex items-center gap-3">
|
||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-brand-100">
|
||
<User className="h-6 w-6 text-brand-700" />
|
||
</div>
|
||
<div>
|
||
<h2 className="font-display text-lg font-semibold">{user.name}</h2>
|
||
<p className="text-sm text-gray-500">
|
||
{t('account.memberSince', { date: formatDate(user.createdAt) })}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-3 border-t border-gray-100 pt-4">
|
||
<div className="flex items-center gap-2 text-sm text-gray-600">
|
||
<Mail className="h-4 w-4 text-brand-600" />
|
||
{user.email}
|
||
</div>
|
||
<div className="flex items-center gap-2 text-sm text-gray-600">
|
||
<Phone className="h-4 w-4 text-brand-600" />
|
||
{user.phone}
|
||
</div>
|
||
<div className="flex items-start gap-2 text-sm text-gray-600">
|
||
<MapPin className="mt-0.5 h-4 w-4 shrink-0 text-brand-600" />
|
||
{user.address.street}, {user.address.city}, {user.address.state}{' '}
|
||
{user.address.zip}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="card-premium p-4">
|
||
<Link
|
||
href="/wishlist"
|
||
className="flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-brand-50 hover:text-brand-700"
|
||
>
|
||
<Heart className="h-4 w-4" />
|
||
{t('account.myWishlist')}
|
||
</Link>
|
||
<button
|
||
onClick={handleLogout}
|
||
className="flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium text-red-500 transition-colors hover:bg-red-50"
|
||
>
|
||
<LogOut className="h-4 w-4" />
|
||
{t('account.signOut')}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="lg:col-span-2" id="orders">
|
||
<div className="card-premium p-6">
|
||
<div className="mb-6 flex items-center gap-2">
|
||
<Package className="h-5 w-5 text-brand-700" />
|
||
<h2 className="font-display text-lg font-semibold">
|
||
{t('account.orderHistory')}
|
||
</h2>
|
||
</div>
|
||
|
||
{orders.length === 0 ? (
|
||
<div className="py-12 text-center">
|
||
<p className="mb-2 text-gray-500">{t('account.noOrders')}</p>
|
||
<Link href="/shop">
|
||
<Button variant="primary">{t('account.startShopping')}</Button>
|
||
</Link>
|
||
</div>
|
||
) : (
|
||
<div className="space-y-4">
|
||
{orders.map((order) => (
|
||
<div
|
||
key={order.id}
|
||
className="rounded-xl border border-gray-100 p-4 transition-colors hover:border-brand-200"
|
||
>
|
||
<div className="mb-3 flex items-center justify-between">
|
||
<div>
|
||
<p className="text-sm font-semibold text-brand-900">{order.id}</p>
|
||
<p className="text-xs text-gray-400">
|
||
{formatDate(order.createdAt)}
|
||
</p>
|
||
</div>
|
||
<div className="text-end">
|
||
<p className="font-bold text-brand-800">{fmt(order.total)}</p>
|
||
<span className="inline-block rounded-full bg-brand-50 px-2 py-0.5 text-xs font-medium capitalize text-brand-700">
|
||
{t(`orderStatus.${order.status}`)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div className="space-y-1">
|
||
{order.items.map((item) => {
|
||
const localized = localizeProduct(item.product, t);
|
||
return (
|
||
<div
|
||
key={item.id}
|
||
className="flex justify-between text-sm text-gray-600"
|
||
>
|
||
<Link
|
||
href={`/product/${item.product.slug}`}
|
||
className="hover:text-brand-700"
|
||
>
|
||
{localized.name} × {item.quantity}
|
||
<span className="ms-2 text-xs text-gold-600">
|
||
({item.customizationLabel})
|
||
</span>
|
||
</Link>
|
||
<span>{fmt(item.product.price * item.quantity)}</span>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
} |