This commit is contained in:
2025-07-10 14:44:06 +03:00
parent 7b71da3825
commit 8e82736f09
7 changed files with 208 additions and 43 deletions

View File

@@ -0,0 +1,5 @@
import SkeletonCartPage from '~/medusa/modules/skeletons/templates/skeleton-cart-page';
export default function Loading() {
return <SkeletonCartPage />;
}

View File

@@ -0,0 +1,21 @@
import { Metadata } from 'next';
import InteractiveLink from '~/medusa/modules/common/components/interactive-link';
export const metadata: Metadata = {
title: '404',
description: 'Something went wrong',
};
export default function NotFound() {
return (
<div className="flex min-h-[calc(100vh-64px)] flex-col items-center justify-center">
<h1 className="text-2xl-semi text-ui-fg-base">Page not found</h1>
<p className="text-small-regular text-ui-fg-base">
The cart you tried to access does not exist. Clear your cookies and try
again.
</p>
<InteractiveLink href="/">Go to frontpage</InteractiveLink>
</div>
);
}

View File

@@ -0,0 +1,23 @@
import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { retrieveCart } from '~/medusa/lib/data/cart';
import { retrieveCustomer } from '~/medusa/lib/data/customer';
import CartTemplate from '~/medusa/modules/cart/templates';
export const metadata: Metadata = {
title: 'Cart',
description: 'View your cart',
};
export default async function Cart() {
const cart = await retrieveCart().catch((error) => {
console.error(error);
return notFound();
});
const customer = await retrieveCustomer();
return <CartTemplate cart={cart} customer={customer} />;
}

View File

@@ -1,16 +1,15 @@
import Link from 'next/link';
import { ShoppingCart } from 'lucide-react';
import { Trans } from '@kit/ui/trans'; import { Trans } from '@kit/ui/trans';
import { AppLogo } from '~/components/app-logo'; import { AppLogo } from '~/components/app-logo';
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container'; import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
import { Search } from '~/components/ui/search'; import { Search } from '~/components/ui/search';
import { SIDEBAR_WIDTH_PROPERTY } from '@/packages/ui/src/shadcn/constants';
import { Button } from '@kit/ui/button';
import { SIDEBAR_WIDTH_PROPERTY } from '../../../../packages/ui/src/shadcn/constants';
// home imports
import { UserNotifications } from '../_components/user-notifications'; import { UserNotifications } from '../_components/user-notifications';
import { type UserWorkspace } from '../_lib/server/load-user-workspace'; import { type UserWorkspace } from '../_lib/server/load-user-workspace';
import { Button } from '@kit/ui/button';
import { ShoppingCart } from 'lucide-react';
export function HomeMenuNavigation(props: { workspace: UserWorkspace }) { export function HomeMenuNavigation(props: { workspace: UserWorkspace }) {
const { workspace, user, accounts } = props.workspace; const { workspace, user, accounts } = props.workspace;
@@ -30,10 +29,12 @@ export function HomeMenuNavigation(props: { workspace: UserWorkspace }) {
<Button className='relative px-4 py-2 h-10 border-1 mr-0 cursor-pointer' variant='ghost'> <Button className='relative px-4 py-2 h-10 border-1 mr-0 cursor-pointer' variant='ghost'>
<span className='flex items-center text-nowrap'> 231,89</span> <span className='flex items-center text-nowrap'> 231,89</span>
</Button> </Button>
<Button variant="ghost" className='relative px-4 py-2 h-10 border-1 mr-0 cursor-pointer' > <Link href='/home/cart'>
<ShoppingCart className="stroke-[1.5px]" /> <Button variant="ghost" className='relative px-4 py-2 h-10 border-1 mr-0 cursor-pointer' >
<Trans i18nKey="common:shoppingCart" /> (0) <ShoppingCart className="stroke-[1.5px]" />
</Button> <Trans i18nKey="common:shoppingCart" /> (0)
</Button>
</Link>
<UserNotifications userId={user.id} /> <UserNotifications userId={user.id} />
<div> <div>

View File

@@ -68,13 +68,11 @@ async function JoinTeamAccountPage(props: JoinTeamAccountPageProps) {
const invitation = await api.getInvitation(adminClient, token); const invitation = await api.getInvitation(adminClient, token);
// the invitation is not found or expired // the invitation is not found or expired
if (!invitation) {
return ( return (
<AuthLayoutShell Logo={AppLogo}> <AuthLayoutShell Logo={AppLogo}>
<InviteNotFoundOrExpired /> <InviteNotFoundOrExpired />
</AuthLayoutShell> </AuthLayoutShell>
); );
}
// we need to verify the user isn't already in the account // we need to verify the user isn't already in the account
// we do so by checking if the user can read the account // we do so by checking if the user can read the account

View File

@@ -1,46 +1,117 @@
import { Metadata } from 'next'; import { use } from 'react';
import { StoreCartShippingOption } from '@medusajs/types'; import { cookies } from 'next/headers';
import { listCartOptions, retrieveCart } from '~/medusa/lib/data/cart'; import { z } from 'zod';
import { retrieveCustomer } from '~/medusa/lib/data/customer';
import { getBaseURL } from '~/medusa/lib/util/env';
import CartMismatchBanner from '~/medusa/modules/layout/components/cart-mismatch-banner';
import Footer from '~/medusa/modules/layout/templates/footer';
import Nav from '~/medusa/modules/layout/templates/nav';
import FreeShippingPriceNudge from '~/medusa/modules/shipping/components/free-shipping-price-nudge';
export const metadata: Metadata = { import { UserWorkspaceContextProvider } from '@kit/accounts/components';
metadataBase: new URL(getBaseURL()), import { Page, PageMobileNavigation, PageNavigation } from '@kit/ui/page';
}; import { SidebarProvider } from '@kit/ui/shadcn-sidebar';
export default async function PageLayout(props: { children: React.ReactNode }) { import { AppLogo } from '~/components/app-logo';
const customer = await retrieveCustomer(); import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
const cart = await retrieveCart(); import { withI18n } from '~/lib/i18n/with-i18n';
let shippingOptions: StoreCartShippingOption[] = []; import { loadUserWorkspace } from '@/app/home/(user)/_lib/server/load-user-workspace';
import { HomeSidebar } from '@/app/home/(user)/_components/home-sidebar';
import { HomeMenuNavigation } from '@/app/home/(user)/_components/home-menu-navigation';
import { HomeMobileNavigation } from '@/app/home/(user)/_components/home-mobile-navigation';
if (cart) { function UserHomeLayout({ children }: React.PropsWithChildren) {
const { shipping_options } = await listCartOptions(); const state = use(getLayoutState());
shippingOptions = shipping_options; if (state.style === 'sidebar') {
return <SidebarLayout>{children}</SidebarLayout>;
} }
return <HeaderLayout>{children}</HeaderLayout>;
}
export default withI18n(UserHomeLayout);
function SidebarLayout({ children }: React.PropsWithChildren) {
const workspace = use(loadUserWorkspace());
const state = use(getLayoutState());
return (
<UserWorkspaceContextProvider value={workspace}>
<SidebarProvider defaultOpen={state.open}>
<Page style={'sidebar'}>
<PageNavigation>
<HomeSidebar />
</PageNavigation>
<PageMobileNavigation className={'flex items-center justify-between'}>
<MobileNavigation workspace={workspace} />
</PageMobileNavigation>
{children}
</Page>
</SidebarProvider>
</UserWorkspaceContextProvider>
);
}
function HeaderLayout({ children }: React.PropsWithChildren) {
const workspace = use(loadUserWorkspace());
return (
<UserWorkspaceContextProvider value={workspace}>
<Page style={'header'}>
<PageNavigation>
<HomeMenuNavigation workspace={workspace} />
</PageNavigation>
<PageMobileNavigation className={'flex items-center justify-between'}>
<MobileNavigation workspace={workspace} />
</PageMobileNavigation>
<SidebarProvider defaultOpen>
<Page style={'sidebar'}>
<PageNavigation>
<HomeSidebar />
</PageNavigation>
{children}
</Page>
</SidebarProvider>
</Page>
</UserWorkspaceContextProvider>
);
}
function MobileNavigation({
workspace,
}: {
workspace: Awaited<ReturnType<typeof loadUserWorkspace>>;
}) {
return ( return (
<> <>
<Nav /> <AppLogo />
{customer && cart && (
<CartMismatchBanner customer={customer} cart={cart} />
)}
{cart && ( <HomeMobileNavigation workspace={workspace} />
<FreeShippingPriceNudge
variant="popup"
cart={cart}
shippingOptions={shippingOptions}
/>
)}
{props.children}
<Footer />
</> </>
); );
} }
async function getLayoutState() {
const cookieStore = await cookies();
const LayoutStyleSchema = z.enum(['sidebar', 'header', 'custom']);
const layoutStyleCookie = cookieStore.get('layout-style');
const sidebarOpenCookie = cookieStore.get('sidebar:state');
const sidebarOpen = sidebarOpenCookie
? sidebarOpenCookie.value === 'false'
: !personalAccountNavigationConfig.sidebarCollapsed;
const parsedStyle = LayoutStyleSchema.safeParse(layoutStyleCookie?.value);
const style = parsedStyle.success
? parsedStyle.data
: personalAccountNavigationConfig.style;
return {
open: sidebarOpen,
style,
};
}

View File

@@ -0,0 +1,46 @@
import { Metadata } from 'next';
import { StoreCartShippingOption } from '@medusajs/types';
import { listCartOptions, retrieveCart } from '~/medusa/lib/data/cart';
import { retrieveCustomer } from '~/medusa/lib/data/customer';
import { getBaseURL } from '~/medusa/lib/util/env';
import CartMismatchBanner from '~/medusa/modules/layout/components/cart-mismatch-banner';
import Footer from '~/medusa/modules/layout/templates/footer';
import Nav from '~/medusa/modules/layout/templates/nav';
import FreeShippingPriceNudge from '~/medusa/modules/shipping/components/free-shipping-price-nudge';
export const metadata: Metadata = {
metadataBase: new URL(getBaseURL()),
};
export default async function PageLayout(props: { children: React.ReactNode }) {
const customer = await retrieveCustomer();
const cart = await retrieveCart();
let shippingOptions: StoreCartShippingOption[] = [];
if (cart) {
const { shipping_options } = await listCartOptions();
shippingOptions = shipping_options;
}
return (
<>
<Nav />
{customer && cart && (
<CartMismatchBanner customer={customer} cart={cart} />
)}
{cart && (
<FreeShippingPriceNudge
variant="popup"
cart={cart}
shippingOptions={shippingOptions}
/>
)}
{props.children}
<Footer />
</>
);
}