'use client'; import { useCallback, useEffect, useState } from 'react'; import { useRouter } from '@/core/lib/navigation'; import type { CustomerProfile, Order } from 'brainerce'; import { getClient } from '@/core/lib/brainerce'; import { useAuth } from '@/core/providers/store-provider'; import { LoadingSpinner } from '@/ui/shared/loading-spinner'; import { ProfileSection } from '@/components/account/profile-section'; import { AddressBook } from '@/components/account/address-book'; import { OrderHistory } from '@/components/account/order-history'; import { LoyaltyPanel } from '@/components/account/loyalty-panel'; import { SavedPaymentMethods } from '@/components/account/saved-payment-methods'; import { useTranslations } from '@/core/lib/translations'; /** Orders fetched per page. The API caps `limit` at 100. */ const ORDERS_PAGE_SIZE = 20; export default function AccountPage() { const router = useRouter(); const { isLoggedIn, authLoading, logout } = useAuth(); const t = useTranslations('account'); const tc = useTranslations('common'); const [profile, setProfile] = useState(null); const [orders, setOrders] = useState([]); // Order history is paginated: `getMyOrders` answers the standard // { data, meta: { page, limit, total, totalPages } } envelope. Without the // page cursor below, order 21 is unreachable forever. const [ordersPage, setOrdersPage] = useState(1); const [ordersTotalPages, setOrdersTotalPages] = useState(1); const [ordersLoadingMore, setOrdersLoadingMore] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); /** Fetch one page of orders. `append` distinguishes load-more from a reload. */ const loadOrders = useCallback(async (page: number, append: boolean) => { if (append) setOrdersLoadingMore(true); try { const result = await getClient().getMyOrders({ page, limit: ORDERS_PAGE_SIZE }); setOrders((prev) => (append ? [...prev, ...result.data] : result.data)); setOrdersPage(result.meta.page); setOrdersTotalPages(result.meta.totalPages); } catch { // A failed page must not blank the pages already on screen. } finally { if (append) setOrdersLoadingMore(false); } }, []); useEffect(() => { if (authLoading) return; if (!isLoggedIn) { router.push('/login'); return; } async function loadAccountData() { try { const client = getClient(); const [profileResult, ordersResult] = await Promise.allSettled([ client.getMyProfile(), client.getMyOrders({ page: 1, limit: ORDERS_PAGE_SIZE }), ]); if (profileResult.status === 'fulfilled') { setProfile(profileResult.value); } else { setError('Failed to load profile.'); } if (ordersResult.status === 'fulfilled') { setOrders(ordersResult.value.data); setOrdersPage(ordersResult.value.meta.page); setOrdersTotalPages(ordersResult.value.meta.totalPages); } } catch (err) { const message = err instanceof Error ? err.message : 'Failed to load account data.'; setError(message); } finally { setLoading(false); } } loadAccountData(); }, [isLoggedIn, authLoading, router]); if (authLoading || !isLoggedIn) { return null; } if (loading) { return (
); } if (error && !profile) { return (

{tc('error')}

{error}

); } return (

{t('myAccount')}

{/* Profile Section */} {profile && ( )} {/* Address Book */} {profile && ( setProfile((p) => (p ? { ...p, addresses: updated } : p))} className="mb-8" /> )} {/* Saved cards. Renders nothing at all until the customer has vaulted one by checking out with saveCard, which is the normal case, so it is mounted unconditionally and hides itself. Read-only on purpose: the only storefront call that CHARGES a vaulted card is subscribeToMembership, so there is no "pay with this card" action to offer here. */} {/* Rewards. Renders nothing at all when the store has no loyalty programme, which is the normal case until the merchant turns one on — so it is mounted unconditionally and hides itself. */} {/* Order History */}

{t('orderHistory')}

loadOrders(ordersPage + 1, true)} />
); }