Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 3 additions & 13 deletions src/app/[country]/[locale]/(checkout)/checkout/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useTranslations } from "next-intl";
import { Suspense, use, useCallback, useEffect, useRef, useState } from "react";
import { AddressSection } from "@/components/checkout/AddressSection";
import { CheckoutPageSkeleton } from "@/components/checkout/CheckoutPageSkeleton";
import { CouponCode } from "@/components/checkout/CouponCode";
import { DeliveryMethodSection } from "@/components/checkout/DeliveryMethodSection";
import {
Expand Down Expand Up @@ -543,19 +544,8 @@ function CheckoutPageContent({ params }: CheckoutPageProps) {
// PaymentSection handles setProcessing(false) on error internally
};

// Loading state
if (loading || authLoading) {
return (
<div className="animate-pulse space-y-6">
<div className="h-8 bg-gray-200 rounded w-1/3" />
<div className="h-4 bg-gray-200 rounded w-1/4" />
<div className="space-y-4 mt-8">
<div className="h-12 bg-gray-200 rounded" />
<div className="h-12 bg-gray-200 rounded" />
<div className="h-12 bg-gray-200 rounded" />
</div>
</div>
);
return <CheckoutPageSkeleton />;
}

// Error state (no cart loaded)
Expand Down Expand Up @@ -613,7 +603,7 @@ function CheckoutPageContent({ params }: CheckoutPageProps) {
countries={countries}
savedAddresses={savedAddresses}
isAuthenticated={isAuthenticated}
signInUrl={`${basePath}/account?redirect=${encodeURIComponent(pathname)}`}
signInUrl={`${basePath}/account/login?redirect=${encodeURIComponent(pathname)}`}
fetchStates={fetchStates}
onEmailBlur={handleEmailBlur}
onAutoSave={handleAutoSave}
Expand Down
10 changes: 2 additions & 8 deletions src/app/[country]/[locale]/(checkout)/order-placed/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { usePathname } from "next/navigation";
import { useTranslations } from "next-intl";
import { use, useEffect, useRef, useState } from "react";
import { AddressBlock } from "@/components/order/AddressBlock";
import { OrderPlacedSkeleton } from "@/components/order/OrderPlacedSkeleton";
import { OrderTotals } from "@/components/order/OrderTotals";
import { PaymentInfo } from "@/components/order/PaymentInfo";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -90,14 +91,7 @@ export default function OrderPlacedPage({ params }: OrderPlacedPageProps) {
}, [cartId]);

if (loading) {
return (
<div className="animate-pulse space-y-6 py-12">
<div className="h-12 w-12 bg-gray-200 rounded-lg mx-auto" />
<div className="h-8 bg-gray-200 rounded w-1/2 mx-auto" />
<div className="h-4 bg-gray-200 rounded w-1/3 mx-auto" />
<div className="h-64 bg-gray-200 rounded mt-8" />
</div>
);
return <OrderPlacedSkeleton />;
}

if (error || !order) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AddressesSkeleton } from "@/components/account/AddressesSkeleton";

export default function AddressesLoading() {
return <AddressesSkeleton />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { CreditCardsSkeleton } from "@/components/account/CreditCardsSkeleton";

export default function CreditCardsLoading() {
return <CreditCardsSkeleton />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default function ForgotPasswordPage() {

<CardFooter className="justify-center">
<Link
href={`${basePath}/account`}
href={`${basePath}/account/login`}
className="text-sm text-primary hover:text-primary/70 font-medium"
>
{t("backToSignIn")}
Expand Down Expand Up @@ -145,7 +145,7 @@ export default function ForgotPasswordPage() {

<CardFooter className="justify-center">
<Link
href={`${basePath}/account`}
href={`${basePath}/account/login`}
className="text-sm text-primary hover:text-primary/70 font-medium"
>
{t("backToSignIn")}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { GiftCardsSkeleton } from "@/components/account/GiftCardsSkeleton";

export default function GiftCardsLoading(): React.JSX.Element {
return <GiftCardsSkeleton />;
}
95 changes: 54 additions & 41 deletions src/app/[country]/[locale]/(storefront)/account/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { useEffect } from "react";
import { AccountDashboardSkeleton } from "@/components/account/AccountDashboardSkeleton";
import { AddressesSkeleton } from "@/components/account/AddressesSkeleton";
import { AuthFallbackSkeleton } from "@/components/account/AuthFallbackSkeleton";
import { ContentSkeleton } from "@/components/account/ContentSkeleton";
import { CreditCardsSkeleton } from "@/components/account/CreditCardsSkeleton";
import { ForgotPasswordFormSkeleton } from "@/components/account/ForgotPasswordFormSkeleton";
import { GiftCardsSkeleton } from "@/components/account/GiftCardsSkeleton";
import { LoginFormSkeleton } from "@/components/account/LoginFormSkeleton";
import { OrdersListSkeleton } from "@/components/account/OrdersListSkeleton";
import { ProfileSkeleton } from "@/components/account/ProfileSkeleton";
import { RegisterFormSkeleton } from "@/components/account/RegisterFormSkeleton";
import { ResetPasswordFormSkeleton } from "@/components/account/ResetPasswordFormSkeleton";
import { SidebarUserInfoSkeleton } from "@/components/account/SidebarUserInfoSkeleton";
import { Button } from "@/components/ui/button";
import { useAuth } from "@/contexts/AuthContext";
import { extractBasePath } from "@/lib/utils/path";
Expand All @@ -37,17 +50,6 @@ function getNavItems(t: ReturnType<typeof useTranslations<"account">>): {
];
}

function ContentSkeleton() {
return (
<div className="animate-pulse space-y-4">
<div className="h-8 bg-gray-200 rounded w-1/3" />
<div className="h-4 bg-gray-200 rounded w-2/3" />
<div className="h-32 bg-gray-200 rounded" />
<div className="h-32 bg-gray-200 rounded" />
</div>
);
}

interface AccountShellProps {
children: React.ReactNode;
basePath: string;
Expand Down Expand Up @@ -80,10 +82,7 @@ function AccountShell({
{/* User Info */}
<div className="p-4 border-b border-gray-200">
{isLoading ? (
<div className="animate-pulse space-y-2">
<div className="h-4 bg-gray-200 rounded w-24" />
<div className="h-3 bg-gray-200 rounded w-32" />
</div>
<SidebarUserInfoSkeleton />
) : (
<>
<p className="font-medium text-gray-900">
Expand Down Expand Up @@ -155,49 +154,63 @@ export default function AccountLayout({

// Pages that don't require authentication
const authPagePaths = new Set([
`${basePath}/account/login`,
`${basePath}/account/register`,
`${basePath}/account/forgot-password`,
`${basePath}/account/reset-password`,
]);
const isAuthPage = authPagePaths.has(pathname);
const isMainAccountPage = pathname === `${basePath}/account`;

// Redirect to login if not authenticated and trying to access protected sub-pages
// Redirect to login if not authenticated and trying to access protected pages.
// Preserve the originally requested path via `?redirect=` so LoginPage can
// send the user back after they sign in.
useEffect(() => {
if (!loading && !isAuthenticated && !isAuthPage && !isMainAccountPage) {
router.replace(`${basePath}/account`);
if (!loading && !isAuthenticated && !isAuthPage) {
const redirect = encodeURIComponent(pathname);
router.replace(`${basePath}/account/login?redirect=${redirect}`);
}
}, [
loading,
isAuthenticated,
isAuthPage,
isMainAccountPage,
basePath,
router,
]);
}, [loading, isAuthenticated, isAuthPage, basePath, pathname, router]);

// Show loading or redirect-in-progress skeleton
if (loading || (!isAuthenticated && !isAuthPage && !isMainAccountPage)) {
if (isAuthPage || isMainAccountPage) {
return (
<div className="max-w-md mx-auto px-4 sm:px-6 lg:px-8 py-16">
<div className="animate-pulse space-y-4">
<div className="h-8 bg-gray-200 rounded w-1/2 mx-auto" />
<div className="h-4 bg-gray-200 rounded w-3/4 mx-auto" />
<div className="h-48 bg-gray-200 rounded" />
</div>
</div>
);
if (loading || (!isAuthenticated && !isAuthPage)) {
if (pathname === `${basePath}/account/login`) {
return <LoginFormSkeleton />;
}
if (pathname === `${basePath}/account/register`) {
return <RegisterFormSkeleton />;
}
if (pathname === `${basePath}/account/forgot-password`) {
return <ForgotPasswordFormSkeleton />;
}
if (pathname === `${basePath}/account/reset-password`) {
return <ResetPasswordFormSkeleton />;
}
if (isAuthPage) {
// generic fallback for any future auth page
return <AuthFallbackSkeleton />;
}
const isDashboardPage = pathname === `${basePath}/account`;
const isProfilePage = pathname === `${basePath}/account/profile`;
const isOrdersPage = pathname === `${basePath}/account/orders`;
const isGiftCardsPage = pathname === `${basePath}/account/gift-cards`;
const isCreditCardsPage = pathname === `${basePath}/account/credit-cards`;
const isAddressesPage = pathname === `${basePath}/account/addresses`;
let content: React.ReactNode = <ContentSkeleton />;
if (isDashboardPage) content = <AccountDashboardSkeleton />;
else if (isProfilePage) content = <ProfileSkeleton />;
else if (isOrdersPage) content = <OrdersListSkeleton />;
else if (isGiftCardsPage) content = <GiftCardsSkeleton />;
else if (isCreditCardsPage) content = <CreditCardsSkeleton />;
else if (isAddressesPage) content = <AddressesSkeleton />;
return (
<AccountShell basePath={basePath} pathname={pathname} isLoading={true}>
<ContentSkeleton />
{content}
</AccountShell>
);
}

// Don't show nav for login/register pages
if (isAuthPage || !isAuthenticated) {
// Don't show nav for login/register/forgot/reset pages
if (isAuthPage) {
return <>{children}</>;
}

Expand Down
Loading
Loading