Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 615dde52e6 | |||
| 7f2c6f2374 | |||
| 6368a5b5ff | |||
| 8e82736f09 | |||
| 7b71da3825 | |||
| ad213dd4f8 | |||
| 0a0b1f0dee | |||
| bbcf0b6d83 |
34
app/home/(user)/(dashboard)/booking/page.tsx
Normal file
34
app/home/(user)/(dashboard)/booking/page.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { PageBody } from '@kit/ui/page';
|
||||||
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
|
||||||
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
|
||||||
|
import { HomeLayoutPageHeader } from '../../_components/home-page-header';
|
||||||
|
import OrderCards from '../../_components/order-cards';
|
||||||
|
|
||||||
|
export const generateMetadata = async () => {
|
||||||
|
const i18n = await createI18nServerInstance();
|
||||||
|
const title = i18n.t('booking:title');
|
||||||
|
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function BookingPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<HomeLayoutPageHeader
|
||||||
|
title={<Trans i18nKey={'booking:title'} />}
|
||||||
|
description={<Trans i18nKey={'booking:description'} />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PageBody>
|
||||||
|
<OrderCards />
|
||||||
|
</PageBody>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withI18n(BookingPage);
|
||||||
5
app/home/(user)/(dashboard)/cart/loading.tsx
Normal file
5
app/home/(user)/(dashboard)/cart/loading.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import SkeletonCartPage from '~/medusa/modules/skeletons/templates/skeleton-cart-page';
|
||||||
|
|
||||||
|
export default function Loading() {
|
||||||
|
return <SkeletonCartPage />;
|
||||||
|
}
|
||||||
21
app/home/(user)/(dashboard)/cart/not-found.tsx
Normal file
21
app/home/(user)/(dashboard)/cart/not-found.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
59
app/home/(user)/(dashboard)/cart/page.tsx
Normal file
59
app/home/(user)/(dashboard)/cart/page.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { PageBody, PageHeader } from '@/packages/ui/src/makerkit/page';
|
||||||
|
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 cart2 = await retrieveCart().catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
return notFound();
|
||||||
|
});
|
||||||
|
|
||||||
|
const customer = await retrieveCustomer();
|
||||||
|
|
||||||
|
const cart: NonNullable<typeof cart2> = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
quantity: 1,
|
||||||
|
cart: cart2!,
|
||||||
|
item_total: 100,
|
||||||
|
item_subtotal: 100,
|
||||||
|
item_tax_total: 100,
|
||||||
|
original_total: 100,
|
||||||
|
original_subtotal: 100,
|
||||||
|
original_tax_total: 100,
|
||||||
|
total: 100,
|
||||||
|
subtotal: 100,
|
||||||
|
tax_total: 100,
|
||||||
|
title: 'Test',
|
||||||
|
requires_shipping: true,
|
||||||
|
discount_total: 0,
|
||||||
|
discount_tax_total: 0,
|
||||||
|
metadata: {},
|
||||||
|
created_at: new Date(),
|
||||||
|
is_discountable: true,
|
||||||
|
is_tax_inclusive: true,
|
||||||
|
unit_price: 100,
|
||||||
|
cart_id: '1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageBody>
|
||||||
|
<PageHeader title={`Ostukorv`} description={`Vali kalendrist sobiv kuupäev ja broneeri endale vastuvõtuaeg.`} />
|
||||||
|
|
||||||
|
<CartTemplate cart={cart} customer={customer} />
|
||||||
|
</PageBody>
|
||||||
|
);
|
||||||
|
}
|
||||||
42
app/home/(user)/(dashboard)/order-analysis-package/page.tsx
Normal file
42
app/home/(user)/(dashboard)/order-analysis-package/page.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { Scale } from 'lucide-react';
|
||||||
|
|
||||||
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
import { Button } from '@kit/ui/button';
|
||||||
|
import SelectAnalysisPackages from '@/components/select-analysis-packages';
|
||||||
|
import { PageBody } from '@kit/ui/page';
|
||||||
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
|
||||||
|
import ComparePackagesModal from '../../_components/compare-packages-modal';
|
||||||
|
|
||||||
|
export const generateMetadata = async () => {
|
||||||
|
const i18n = await createI18nServerInstance();
|
||||||
|
const title = i18n.t('order-analysis-package:title');
|
||||||
|
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
async function OrderAnalysisPackagePage() {
|
||||||
|
return (
|
||||||
|
<PageBody>
|
||||||
|
<div className="space-y-3 text-center">
|
||||||
|
<h3>
|
||||||
|
<Trans i18nKey={'marketing:selectPackage'} />
|
||||||
|
</h3>
|
||||||
|
<ComparePackagesModal
|
||||||
|
triggerElement={
|
||||||
|
<Button variant="secondary" className="gap-2">
|
||||||
|
<Trans i18nKey={'marketing:comparePackages'} />
|
||||||
|
<Scale className="size-4 stroke-[1.5px]" />
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<SelectAnalysisPackages />
|
||||||
|
</PageBody>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withI18n(OrderAnalysisPackagePage);
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
import { use } from 'react';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
import { PageBody } from '@kit/ui/page';
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
|
|
||||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
import { PageBody, PageHeader } from '@kit/ui/page';
|
||||||
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
import { toTitleCase } from '@/lib/utils';
|
||||||
|
|
||||||
import Dashboard from '../_components/dashboard';
|
import Dashboard from '../_components/dashboard';
|
||||||
// local imports
|
import { loadCurrentUserAccount } from '../_lib/server/load-user-account';
|
||||||
import { HomeLayoutPageHeader } from '../_components/home-page-header';
|
|
||||||
import { loadUserWorkspace } from '../_lib/server/load-user-workspace';
|
|
||||||
|
|
||||||
export const generateMetadata = async () => {
|
export const generateMetadata = async () => {
|
||||||
const i18n = await createI18nServerInstance();
|
const i18n = await createI18nServerInstance();
|
||||||
@@ -20,15 +18,24 @@ export const generateMetadata = async () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function UserHomePage() {
|
async function UserHomePage() {
|
||||||
const { tempVisibleAccounts } = use(loadUserWorkspace());
|
const account = await loadCurrentUserAccount();
|
||||||
|
if (!account) {
|
||||||
|
redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HomeLayoutPageHeader
|
<PageHeader title={
|
||||||
title={<Trans i18nKey={'common:routes.home'} />}
|
<>
|
||||||
description={<></>}
|
<Trans i18nKey={'common:welcome'} />
|
||||||
|
{account.name ? `, ${toTitleCase(account.name)}` : ''}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
description={
|
||||||
|
<Trans i18nKey={'dashboard:recentlyCheckedDescription'} />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PageBody>
|
<PageBody>
|
||||||
<Dashboard />
|
<Dashboard />
|
||||||
</PageBody>
|
</PageBody>
|
||||||
|
|||||||
@@ -18,12 +18,10 @@ import {
|
|||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from '@kit/ui/table';
|
} from '@kit/ui/table';
|
||||||
|
|
||||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
import { PackageHeader } from '@/components/package-header';
|
||||||
import { PackageHeader } from '../../../../components/package-header';
|
import { InfoTooltip } from '@/components/ui/info-tooltip';
|
||||||
import { InfoTooltip } from '../../../../components/ui/info-tooltip';
|
|
||||||
|
|
||||||
const dummyCards = [
|
const dummyCards = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
import { InfoTooltip } from '@/components/ui/info-tooltip';
|
import { InfoTooltip } from '@/components/ui/info-tooltip';
|
||||||
import { toTitleCase } from '@/lib/utils';
|
|
||||||
import { BlendingModeIcon, RulerHorizontalIcon } from '@radix-ui/react-icons';
|
import { BlendingModeIcon, RulerHorizontalIcon } from '@radix-ui/react-icons';
|
||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
@@ -15,8 +15,6 @@ import {
|
|||||||
User,
|
User,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
import { usePersonalAccountData } from '@kit/accounts/hooks/use-personal-account-data';
|
|
||||||
import { useUserWorkspace } from '@kit/accounts/hooks/use-user-workspace';
|
|
||||||
import { Button } from '@kit/ui/button';
|
import { Button } from '@kit/ui/button';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -26,7 +24,6 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardProps,
|
CardProps,
|
||||||
} from '@kit/ui/card';
|
} from '@kit/ui/card';
|
||||||
import { PageDescription } from '@kit/ui/page';
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
import { Trans } from '@kit/ui/trans';
|
||||||
import { cn } from '@kit/ui/utils';
|
import { cn } from '@kit/ui/utils';
|
||||||
|
|
||||||
@@ -107,6 +104,7 @@ const dummyRecommendations = [
|
|||||||
tooltipContent: 'Selgitus',
|
tooltipContent: 'Selgitus',
|
||||||
price: '20,00 €',
|
price: '20,00 €',
|
||||||
buttonText: 'Telli',
|
buttonText: 'Telli',
|
||||||
|
href: '/home/booking',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <BlendingModeIcon className="size-4" />,
|
icon: <BlendingModeIcon className="size-4" />,
|
||||||
@@ -115,6 +113,7 @@ const dummyRecommendations = [
|
|||||||
tooltipContent: 'Selgitus',
|
tooltipContent: 'Selgitus',
|
||||||
description: 'LDL-Kolesterool',
|
description: 'LDL-Kolesterool',
|
||||||
buttonText: 'Broneeri',
|
buttonText: 'Broneeri',
|
||||||
|
href: '/home/booking',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Droplets />,
|
icon: <Droplets />,
|
||||||
@@ -124,24 +123,13 @@ const dummyRecommendations = [
|
|||||||
description: 'Score-Risk 2',
|
description: 'Score-Risk 2',
|
||||||
price: '20,00 €',
|
price: '20,00 €',
|
||||||
buttonText: 'Telli',
|
buttonText: 'Telli',
|
||||||
|
href: '/home/booking',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const userWorkspace = useUserWorkspace();
|
|
||||||
const account = usePersonalAccountData(userWorkspace.user.id);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
|
||||||
<h4>
|
|
||||||
<Trans i18nKey={'common:welcome'} />
|
|
||||||
{account?.data?.name ? `, ${toTitleCase(account.data.name)}` : ''}
|
|
||||||
</h4>
|
|
||||||
<PageDescription>
|
|
||||||
<Trans i18nKey={'dashboard:recentlyCheckedDescription'} />:
|
|
||||||
</PageDescription>
|
|
||||||
</div>
|
|
||||||
<div className="grid auto-rows-fr grid-cols-5 gap-3">
|
<div className="grid auto-rows-fr grid-cols-5 gap-3">
|
||||||
{dummyCards.map(
|
{dummyCards.map(
|
||||||
({
|
({
|
||||||
@@ -196,6 +184,7 @@ export default function Dashboard() {
|
|||||||
tooltipContent,
|
tooltipContent,
|
||||||
price,
|
price,
|
||||||
buttonText,
|
buttonText,
|
||||||
|
href,
|
||||||
},
|
},
|
||||||
index,
|
index,
|
||||||
) => {
|
) => {
|
||||||
@@ -222,9 +211,17 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid w-36 auto-rows-fr grid-cols-2 items-center gap-4">
|
<div className="grid w-36 auto-rows-fr grid-cols-2 items-center gap-4">
|
||||||
<p className="text-sm font-medium"> {price}</p>
|
<p className="text-sm font-medium"> {price}</p>
|
||||||
|
{href ? (
|
||||||
|
<Link href={href}>
|
||||||
<Button size="sm" variant="secondary">
|
<Button size="sm" variant="secondary">
|
||||||
{buttonText}
|
{buttonText}
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Button size="sm" variant="secondary">
|
||||||
|
{buttonText}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
<Link href='/home/cart'>
|
||||||
<Button variant="ghost" className='relative px-4 py-2 h-10 border-1 mr-0 cursor-pointer' >
|
<Button variant="ghost" className='relative px-4 py-2 h-10 border-1 mr-0 cursor-pointer' >
|
||||||
<ShoppingCart className="stroke-[1.5px]" />
|
<ShoppingCart className="stroke-[1.5px]" />
|
||||||
<Trans i18nKey="common:shoppingCart" /> (0)
|
<Trans i18nKey="common:shoppingCart" /> (0)
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
<UserNotifications userId={user.id} />
|
<UserNotifications userId={user.id} />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ export function HomeLayoutPageHeader(
|
|||||||
}>,
|
}>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<PageHeader description={props.description}>{props.children}</PageHeader>
|
<PageHeader description={props.description} title={props.title}>{props.children}</PageHeader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
77
app/home/(user)/_components/order-cards.tsx
Normal file
77
app/home/(user)/_components/order-cards.tsx
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ChevronRight, HeartPulse } from 'lucide-react';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
import { Button } from '@kit/ui/button';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardDescription,
|
||||||
|
CardProps,
|
||||||
|
CardFooter,
|
||||||
|
} from '@kit/ui/card';
|
||||||
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const dummyCards = [
|
||||||
|
{
|
||||||
|
title: 'booking:analysisPackages.title',
|
||||||
|
description: 'booking:analysisPackages.description',
|
||||||
|
descriptionColor: 'text-primary',
|
||||||
|
icon: (
|
||||||
|
<Link href={'/home/order-analysis-package'}>
|
||||||
|
<Button size="icon" variant="outline" className="px-2 text-black">
|
||||||
|
<ChevronRight className="size-4 stroke-2" />
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
cardVariant: 'gradient-success' as CardProps['variant'],
|
||||||
|
iconBg: 'bg-warning',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function OrderCards() {
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-3 gap-6 mt-4">
|
||||||
|
{dummyCards.map(({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon,
|
||||||
|
cardVariant,
|
||||||
|
descriptionColor,
|
||||||
|
iconBg,
|
||||||
|
}) => (
|
||||||
|
<Card
|
||||||
|
key={title}
|
||||||
|
variant={cardVariant}
|
||||||
|
className="flex flex-col justify-between"
|
||||||
|
>
|
||||||
|
<CardHeader className="items-end-safe">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex size-8 items-center-safe justify-center-safe rounded-full text-white',
|
||||||
|
iconBg,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardFooter className="flex flex-col items-start gap-2">
|
||||||
|
<div
|
||||||
|
className={'flex size-8 items-center-safe justify-center-safe rounded-full text-white bg-primary\/10 mb-6'}
|
||||||
|
>
|
||||||
|
<HeartPulse className="size-4 fill-green-500" />
|
||||||
|
</div>
|
||||||
|
<h5>
|
||||||
|
<Trans i18nKey={title} />
|
||||||
|
</h5>
|
||||||
|
<CardDescription className={descriptionColor}>
|
||||||
|
<Trans i18nKey={description} />
|
||||||
|
</CardDescription>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { cache } from 'react';
|
|||||||
|
|
||||||
import { createAccountsApi } from '@kit/accounts/api';
|
import { createAccountsApi } from '@kit/accounts/api';
|
||||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||||
|
import { requireUserInServerComponent } from '@/lib/server/require-user-in-server-component';
|
||||||
|
|
||||||
export type UserAccount = Awaited<ReturnType<typeof loadUserAccount>>;
|
export type UserAccount = Awaited<ReturnType<typeof loadUserAccount>>;
|
||||||
|
|
||||||
@@ -13,6 +14,13 @@ export type UserAccount = Awaited<ReturnType<typeof loadUserAccount>>;
|
|||||||
*/
|
*/
|
||||||
export const loadUserAccount = cache(accountLoader);
|
export const loadUserAccount = cache(accountLoader);
|
||||||
|
|
||||||
|
export async function loadCurrentUserAccount() {
|
||||||
|
const user = await requireUserInServerComponent();
|
||||||
|
return user?.identities?.[0]?.id
|
||||||
|
? await loadUserAccount(user?.identities?.[0]?.id)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
async function accountLoader(accountId: string) {
|
async function accountLoader(accountId: string) {
|
||||||
const client = getSupabaseServerClient();
|
const client = getSupabaseServerClient();
|
||||||
const api = createAccountsApi(client);
|
const api = createAccountsApi(client);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { requireUserInServerComponent } from '../../lib/server/require-user-in-server-component';
|
import { requireUserInServerComponent } from '@/lib/server/require-user-in-server-component';
|
||||||
import ConsentDialog from './(user)/_components/consent-dialog';
|
import ConsentDialog from './(user)/_components/consent-dialog';
|
||||||
import { loadUserAccount } from './(user)/_lib/server/load-user-account';
|
import { loadCurrentUserAccount } from './(user)/_lib/server/load-user-account';
|
||||||
|
|
||||||
export default async function HomeLayout({
|
export default async function HomeLayout({
|
||||||
children,
|
children,
|
||||||
@@ -8,9 +8,7 @@ export default async function HomeLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const user = await requireUserInServerComponent();
|
const user = await requireUserInServerComponent();
|
||||||
const account = user?.identities?.[0]?.id
|
const account = await loadCurrentUserAccount()
|
||||||
? await loadUserAccount(user?.identities?.[0]?.id)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (account && account?.has_consent_anonymized_company_statistics === null) {
|
if (account && account?.has_consent_anonymized_company_statistics === null) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,24 +1,16 @@
|
|||||||
import Image from 'next/image';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { CaretRightIcon } from '@radix-ui/react-icons';
|
import { CaretRightIcon } from '@radix-ui/react-icons';
|
||||||
import { Scale } from 'lucide-react';
|
import { Scale } from 'lucide-react';
|
||||||
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
|
||||||
import { Button } from '@kit/ui/button';
|
import { Button } from '@kit/ui/button';
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardFooter,
|
|
||||||
CardHeader,
|
|
||||||
} from '@kit/ui/card';
|
|
||||||
|
|
||||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
import SelectAnalysisPackages from '@/components/select-analysis-packages';
|
||||||
|
|
||||||
import { MedReportLogo } from '../../components/med-report-logo';
|
import { MedReportLogo } from '../../components/med-report-logo';
|
||||||
import { PackageHeader } from '../../components/package-header';
|
|
||||||
import { ButtonTooltip } from '../../components/ui/button-tooltip';
|
|
||||||
import pathsConfig from '../../config/paths.config';
|
import pathsConfig from '../../config/paths.config';
|
||||||
import ComparePackagesModal from '../home/(user)/_components/compare-packages-modal';
|
import ComparePackagesModal from '../home/(user)/_components/compare-packages-modal';
|
||||||
|
|
||||||
@@ -30,101 +22,31 @@ export const generateMetadata = async () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const dummyCards = [
|
|
||||||
{
|
|
||||||
titleKey: 'product:standard.label',
|
|
||||||
price: 40,
|
|
||||||
nrOfAnalyses: 4,
|
|
||||||
tagColor: 'bg-cyan',
|
|
||||||
descriptionKey: 'marketing:standard.description',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleKey: 'product:standardPlus.label',
|
|
||||||
price: 85,
|
|
||||||
nrOfAnalyses: 10,
|
|
||||||
|
|
||||||
tagColor: 'bg-warning',
|
|
||||||
descriptionKey: 'product:standardPlus.description',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleKey: 'product:premium.label',
|
|
||||||
price: 140,
|
|
||||||
nrOfAnalyses: '12+',
|
|
||||||
|
|
||||||
tagColor: 'bg-purple',
|
|
||||||
descriptionKey: 'product:premium.description',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
async function SelectPackagePage() {
|
async function SelectPackagePage() {
|
||||||
const { t, language } = await createI18nServerInstance();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto my-24 flex flex-col items-center space-y-12">
|
<div className="container mx-auto my-24 flex flex-col items-center space-y-12">
|
||||||
<MedReportLogo />
|
<MedReportLogo />
|
||||||
<div className="space-y-3 text-center">
|
<div className="space-y-3 text-center">
|
||||||
<h3>{t('marketing:selectPackage')}</h3>
|
<h3>
|
||||||
|
<Trans i18nKey={'marketing:selectPackage'} />
|
||||||
|
</h3>
|
||||||
<ComparePackagesModal
|
<ComparePackagesModal
|
||||||
triggerElement={
|
triggerElement={
|
||||||
<Button variant="secondary" className="gap-2">
|
<Button variant="secondary" className="gap-2">
|
||||||
{t('marketing:comparePackages')}
|
<Trans i18nKey={'marketing:comparePackages'} />
|
||||||
<Scale className="size-4 stroke-[1.5px]" />
|
<Scale className="size-4 stroke-[1.5px]" />
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-3 gap-6">
|
<SelectAnalysisPackages />
|
||||||
{dummyCards.map(
|
|
||||||
(
|
|
||||||
{ titleKey, price, nrOfAnalyses, tagColor, descriptionKey },
|
|
||||||
index,
|
|
||||||
) => {
|
|
||||||
return (
|
|
||||||
<Card key={index}>
|
|
||||||
<CardHeader className="relative">
|
|
||||||
<ButtonTooltip
|
|
||||||
content="Content pending"
|
|
||||||
className="absolute top-5 right-5 z-10"
|
|
||||||
/>
|
|
||||||
<Image
|
|
||||||
src="/assets/card-image.png"
|
|
||||||
alt="background"
|
|
||||||
width={326}
|
|
||||||
height={195}
|
|
||||||
className="max-h-48 w-full opacity-10"
|
|
||||||
/>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-1 text-center">
|
|
||||||
<PackageHeader
|
|
||||||
title={t(titleKey)}
|
|
||||||
tagColor={tagColor}
|
|
||||||
analysesNr={t('product:nrOfAnalyses', { nr: nrOfAnalyses })}
|
|
||||||
language={language}
|
|
||||||
price={price}
|
|
||||||
/>
|
|
||||||
<CardDescription>{t(descriptionKey)}</CardDescription>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter>
|
|
||||||
<Button className="w-full">
|
|
||||||
{t('marketing:selectThisPackage')}
|
|
||||||
</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
<div className="col-span-3 grid grid-cols-subgrid">
|
|
||||||
<div className="col-start-2 justify-self-center-safe">
|
|
||||||
<Link href={pathsConfig.app.home}>
|
<Link href={pathsConfig.app.home}>
|
||||||
<Button variant="secondary" className="align-center">
|
<Button variant="secondary" className="align-center">
|
||||||
{t('marketing:notInterestedInAudit')}{' '}
|
<Trans i18nKey={'marketing:notInterestedInAudit'} />{' '}
|
||||||
<CaretRightIcon className="size-4" />
|
<CaretRightIcon className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
46
app/store/[countryCode]/(main)/layout2.tsx
Normal file
46
app/store/[countryCode]/(main)/layout2.tsx
Normal 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 />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
108
components/select-analysis-packages.tsx
Normal file
108
components/select-analysis-packages.tsx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Image from 'next/image';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { Button } from '@kit/ui/button';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
} from '@kit/ui/card';
|
||||||
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
|
||||||
|
import { PackageHeader } from './package-header';
|
||||||
|
import { ButtonTooltip } from './ui/button-tooltip';
|
||||||
|
|
||||||
|
export interface IAnalysisPackage {
|
||||||
|
titleKey: string;
|
||||||
|
price: number;
|
||||||
|
nrOfAnalyses: number | string;
|
||||||
|
tagColor: string;
|
||||||
|
descriptionKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const analysisPackages = [
|
||||||
|
{
|
||||||
|
titleKey: 'product:standard.label',
|
||||||
|
price: 40,
|
||||||
|
nrOfAnalyses: 4,
|
||||||
|
tagColor: 'bg-cyan',
|
||||||
|
descriptionKey: 'marketing:standard.description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
titleKey: 'product:standardPlus.label',
|
||||||
|
price: 85,
|
||||||
|
nrOfAnalyses: 10,
|
||||||
|
|
||||||
|
tagColor: 'bg-warning',
|
||||||
|
descriptionKey: 'product:standardPlus.description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
titleKey: 'product:premium.label',
|
||||||
|
price: 140,
|
||||||
|
nrOfAnalyses: '12+',
|
||||||
|
|
||||||
|
tagColor: 'bg-purple',
|
||||||
|
descriptionKey: 'product:premium.description',
|
||||||
|
},
|
||||||
|
] satisfies IAnalysisPackage[];
|
||||||
|
|
||||||
|
export default function SelectAnalysisPackages() {
|
||||||
|
const {
|
||||||
|
t,
|
||||||
|
i18n: { language },
|
||||||
|
} = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-3 gap-6">
|
||||||
|
{analysisPackages.length > 0 ? analysisPackages.map(
|
||||||
|
(
|
||||||
|
{ titleKey, price, nrOfAnalyses, tagColor, descriptionKey },
|
||||||
|
index,
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<Card key={index}>
|
||||||
|
<CardHeader className="relative">
|
||||||
|
<ButtonTooltip
|
||||||
|
content="Content pending"
|
||||||
|
className="absolute top-5 right-5 z-10"
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
src="/assets/card-image.png"
|
||||||
|
alt="background"
|
||||||
|
width={326}
|
||||||
|
height={195}
|
||||||
|
className="max-h-48 w-full opacity-10"
|
||||||
|
/>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-1 text-center">
|
||||||
|
<PackageHeader
|
||||||
|
title={t(titleKey)}
|
||||||
|
tagColor={tagColor}
|
||||||
|
analysesNr={t('product:nrOfAnalyses', { nr: nrOfAnalyses })}
|
||||||
|
language={language}
|
||||||
|
price={price}
|
||||||
|
/>
|
||||||
|
<CardDescription>
|
||||||
|
<Trans i18nKey={descriptionKey} />
|
||||||
|
</CardDescription>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<Button className="w-full">
|
||||||
|
<Trans i18nKey='order-analysis-package:selectThisPackage' />
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
) : (
|
||||||
|
<h4>
|
||||||
|
<Trans i18nKey='order-analysis-package:noPackagesAvailable' />
|
||||||
|
</h4>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -57,11 +57,11 @@ const pathsConfig = PathsSchema.parse({
|
|||||||
accountBillingReturn: `/home/[account]/billing/return`,
|
accountBillingReturn: `/home/[account]/billing/return`,
|
||||||
joinTeam: '/join',
|
joinTeam: '/join',
|
||||||
selectPackage: '/select-package',
|
selectPackage: '/select-package',
|
||||||
|
booking: '/home/booking',
|
||||||
|
orderAnalysisPackage: '/home/order-analysis-package',
|
||||||
// these routes are added as placeholders and can be changed when the pages are added
|
// these routes are added as placeholders and can be changed when the pages are added
|
||||||
booking: '/booking',
|
|
||||||
myOrders: '/my-orders',
|
myOrders: '/my-orders',
|
||||||
analysisResults: '/analysis-results',
|
analysisResults: '/analysis-results',
|
||||||
orderAnalysisPackage: '/order-analysis-package',
|
|
||||||
orderAnalysis: '/order-analysis',
|
orderAnalysis: '/order-analysis',
|
||||||
orderHealthAnalysis: '/order-health-analysis',
|
orderHealthAnalysis: '/order-health-analysis',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export const defaultI18nNamespaces = [
|
|||||||
'marketing',
|
'marketing',
|
||||||
'dashboard',
|
'dashboard',
|
||||||
'product',
|
'product',
|
||||||
|
'booking',
|
||||||
|
'order-analysis-package',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { cn } from '@kit/ui/utils';
|
|||||||
|
|
||||||
import { usePersonalAccountData } from '../hooks/use-personal-account-data';
|
import { usePersonalAccountData } from '../hooks/use-personal-account-data';
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@kit/ui/avatar';
|
import { Avatar, AvatarFallback, AvatarImage } from '@kit/ui/avatar';
|
||||||
|
import { toTitleCase } from '~/lib/utils';
|
||||||
|
|
||||||
const PERSONAL_ACCOUNT_SLUG = 'personal';
|
const PERSONAL_ACCOUNT_SLUG = 'personal';
|
||||||
|
|
||||||
@@ -124,7 +125,7 @@ export function PersonalAccountDropdown({
|
|||||||
data-test={'account-dropdown-display-name'}
|
data-test={'account-dropdown-display-name'}
|
||||||
className={'truncate text-sm'}
|
className={'truncate text-sm'}
|
||||||
>
|
>
|
||||||
{displayName}
|
{toTitleCase(displayName)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ class AccountsApi {
|
|||||||
*/
|
*/
|
||||||
async getSubscription(accountId: string) {
|
async getSubscription(accountId: string) {
|
||||||
const response = await this.client
|
const response = await this.client
|
||||||
|
.schema('medreport')
|
||||||
.from('subscriptions')
|
.from('subscriptions')
|
||||||
.select('*, items: subscription_items !inner (*)')
|
.select('*, items: subscription_items !inner (*)')
|
||||||
.eq('account_id', accountId)
|
.eq('account_id', accountId)
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
{
|
{
|
||||||
"extends": "@kit/tsconfig/base.json",
|
"extends": "@kit/tsconfig/base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json",
|
||||||
|
"paths": {
|
||||||
|
"~/lib/utils": ["../../../lib/utils.ts"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["*.ts", "*.tsx", "src"],
|
"include": ["*.ts", "*.tsx", "src"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ export function PasswordSignUpForm({
|
|||||||
data-test={'personal-code-input'}
|
data-test={'personal-code-input'}
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={t('personalCodePlaceholder')}
|
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export const getRegion = async (countryCode: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const regions = await listRegions()
|
const regions = await listRegions()
|
||||||
|
console.log("regions", regions)
|
||||||
if (!regions) {
|
if (!regions) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ export const getRegion = async (countryCode: string) => {
|
|||||||
|
|
||||||
const region = countryCode
|
const region = countryCode
|
||||||
? regionMap.get(countryCode)
|
? regionMap.get(countryCode)
|
||||||
: regionMap.get("us")
|
: regionMap.get("et")
|
||||||
|
|
||||||
return region
|
return region
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|||||||
@@ -1,23 +1,6 @@
|
|||||||
import { Heading, Text } from "@medusajs/ui"
|
|
||||||
|
|
||||||
import InteractiveLink from "@modules/common/components/interactive-link"
|
|
||||||
|
|
||||||
const EmptyCartMessage = () => {
|
const EmptyCartMessage = () => {
|
||||||
return (
|
return (
|
||||||
<div className="py-48 px-2 flex flex-col justify-center items-start" data-testid="empty-cart-message">
|
<div className="py-48 px-2 flex flex-col justify-center items-start" data-testid="empty-cart-message">
|
||||||
<Heading
|
|
||||||
level="h1"
|
|
||||||
className="flex flex-row text-3xl-regular gap-x-2 items-baseline"
|
|
||||||
>
|
|
||||||
Cart
|
|
||||||
</Heading>
|
|
||||||
<Text className="text-base-regular mt-4 mb-6 max-w-[32rem]">
|
|
||||||
You don't have anything in your cart. Let's change that, use
|
|
||||||
the link below to start browsing our products.
|
|
||||||
</Text>
|
|
||||||
<div>
|
|
||||||
<InteractiveLink href="/store">Explore products</InteractiveLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ const CartTemplate = ({
|
|||||||
{cart?.items?.length ? (
|
{cart?.items?.length ? (
|
||||||
<div className="grid grid-cols-1 small:grid-cols-[1fr_360px] gap-x-40">
|
<div className="grid grid-cols-1 small:grid-cols-[1fr_360px] gap-x-40">
|
||||||
<div className="flex flex-col bg-white py-6 gap-y-6">
|
<div className="flex flex-col bg-white py-6 gap-y-6">
|
||||||
{!customer && (
|
{/* {!customer && (
|
||||||
<>
|
<>
|
||||||
<SignInPrompt />
|
<SignInPrompt />
|
||||||
<Divider />
|
<Divider />
|
||||||
</>
|
</>
|
||||||
)}
|
)} */}
|
||||||
<ItemsTemplate cart={cart} />
|
<ItemsTemplate cart={cart} />
|
||||||
</div>
|
</div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ const ItemsTemplate = ({ cart }: ItemsTemplateProps) => {
|
|||||||
const items = cart?.items
|
const items = cart?.items
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="pb-3 flex items-center">
|
{/* <div className="pb-3 flex items-center">
|
||||||
<Heading className="text-[2rem] leading-[2.75rem]">Cart</Heading>
|
<Heading className="text-[2rem] leading-[2.75rem]">Cart</Heading>
|
||||||
</div>
|
</div> */}
|
||||||
<Table>
|
<Table>
|
||||||
<Table.Header className="border-t-0">
|
<Table.Header className="border-t-0">
|
||||||
<Table.Row className="text-ui-fg-subtle txt-medium-plus">
|
<Table.Row className="text-ui-fg-subtle txt-medium-plus">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export function formatCurrency(params: {
|
|||||||
locale: string;
|
locale: string;
|
||||||
value: string | number;
|
value: string | number;
|
||||||
}) {
|
}) {
|
||||||
const [lang, region] = params.locale.split('-');
|
const [lang, region] = (params.locale ?? 'et-ET').split('-');
|
||||||
|
|
||||||
return new Intl.NumberFormat(region ?? lang, {
|
return new Intl.NumberFormat(region ?? lang, {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
|
|||||||
@@ -133,13 +133,13 @@ export function PageDescription(props: React.PropsWithChildren) {
|
|||||||
|
|
||||||
export function PageTitle(props: React.PropsWithChildren) {
|
export function PageTitle(props: React.PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<h1
|
<h4
|
||||||
className={
|
className={
|
||||||
'font-heading text-base leading-none font-bold tracking-tight dark:text-white'
|
'font-heading leading-none font-bold tracking-tight dark:text-white'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</h1>
|
</h4>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +167,10 @@ export function PageHeader({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={'flex flex-col gap-y-2'}>
|
<div className={'flex flex-col gap-y-2'}>
|
||||||
|
<If condition={title}>
|
||||||
|
<PageTitle>{title}</PageTitle>
|
||||||
|
</If>
|
||||||
|
|
||||||
<div className="flex items-center gap-x-2.5">
|
<div className="flex items-center gap-x-2.5">
|
||||||
{displaySidebarTrigger ? (
|
{displaySidebarTrigger ? (
|
||||||
<SidebarTrigger className="text-muted-foreground hover:text-secondary-foreground hidden h-4.5 w-4.5 cursor-pointer lg:inline-flex" />
|
<SidebarTrigger className="text-muted-foreground hover:text-secondary-foreground hidden h-4.5 w-4.5 cursor-pointer lg:inline-flex" />
|
||||||
@@ -183,10 +187,6 @@ export function PageHeader({
|
|||||||
<PageDescription>{description}</PageDescription>
|
<PageDescription>{description}</PageDescription>
|
||||||
</If>
|
</If>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<If condition={title}>
|
|
||||||
<PageTitle>{title}</PageTitle>
|
|
||||||
</If>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
8
public/locales/en/booking.json
Normal file
8
public/locales/en/booking.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"title": "Select service",
|
||||||
|
"description": "Select the appropriate service or package according to your health needs or goals.",
|
||||||
|
"analysisPackages": {
|
||||||
|
"title": "Analysis packages",
|
||||||
|
"description": "Get to know the personal analysis packages and order"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,8 +37,5 @@
|
|||||||
"footerDescription": "Here you can add a description about your company or product",
|
"footerDescription": "Here you can add a description about your company or product",
|
||||||
"copyright": "© Copyright {{year}} {{product}}. All Rights Reserved.",
|
"copyright": "© Copyright {{year}} {{product}}. All Rights Reserved.",
|
||||||
"heroSubtitle": "A simple, convenient, and quick overview of your health condition",
|
"heroSubtitle": "A simple, convenient, and quick overview of your health condition",
|
||||||
"selectPackage": "Select package",
|
|
||||||
"selectThisPackage": "Select this package",
|
|
||||||
"comparePackages": "Compare packages",
|
|
||||||
"notInterestedInAudit": "Currently not interested in a health audit"
|
"notInterestedInAudit": "Currently not interested in a health audit"
|
||||||
}
|
}
|
||||||
7
public/locales/en/order-analysis-package.json
Normal file
7
public/locales/en/order-analysis-package.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"title": "Select analysis package",
|
||||||
|
"noPackagesAvailable": "No packages available",
|
||||||
|
"selectThisPackage": "Select this package",
|
||||||
|
"selectPackage": "Select package",
|
||||||
|
"comparePackages": "Compare packages"
|
||||||
|
}
|
||||||
8
public/locales/et/booking.json
Normal file
8
public/locales/et/booking.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"title": "Vali teenus",
|
||||||
|
"description": "Vali sobiv teenus või pakett vastavalt oma tervisemurele või -eesmärgile.",
|
||||||
|
"analysisPackages": {
|
||||||
|
"title": "Analüüside paketid",
|
||||||
|
"description": "Tutvu personaalsete analüüsi pakettidega ja telli"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"search": "Otsi{{end}}",
|
"search": "Otsi{{end}}",
|
||||||
"myActions": "Minu toimingud",
|
"myActions": "Minu toimingud",
|
||||||
"healthPackageComparison": {
|
"healthPackageComparison": {
|
||||||
"label": "Tervisepakketide võrdlus",
|
"label": "Tervisepakettide võrdlus",
|
||||||
"description": "Alljärgnevalt on antud eelinfo (sugu, vanus ja kehamassiindeksi) põhjal tehtud personalne terviseauditi valik. Tabelis on võimalik soovitatud terviseuuringute paketile lisada üksikuid uuringuid juurde."
|
"description": "Alljärgnevalt on antud eelinfo (sugu, vanus ja kehamassiindeksi) põhjal tehtud personalne terviseauditi valik. Tabelis on võimalik soovitatud terviseuuringute paketile lisada üksikuid uuringuid juurde."
|
||||||
},
|
},
|
||||||
"routes": {
|
"routes": {
|
||||||
|
|||||||
@@ -37,8 +37,5 @@
|
|||||||
"footerDescription": "Here you can add a description about your company or product",
|
"footerDescription": "Here you can add a description about your company or product",
|
||||||
"copyright": "© Copyright {{year}} {{product}}. All Rights Reserved.",
|
"copyright": "© Copyright {{year}} {{product}}. All Rights Reserved.",
|
||||||
"heroSubtitle": "Lihtne, mugav ja kiire ülevaade oma tervisest",
|
"heroSubtitle": "Lihtne, mugav ja kiire ülevaade oma tervisest",
|
||||||
"selectPackage": "Vali pakett",
|
|
||||||
"selectThisPackage": "Vali see pakett",
|
|
||||||
"comparePackages": "Võrdle pakette",
|
|
||||||
"notInterestedInAudit": "Ei soovi hetkel terviseauditit"
|
"notInterestedInAudit": "Ei soovi hetkel terviseauditit"
|
||||||
}
|
}
|
||||||
7
public/locales/et/order-analysis-package.json
Normal file
7
public/locales/et/order-analysis-package.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"title": "Vali analüüsi pakett",
|
||||||
|
"noPackagesAvailable": "Teenuste loetelu ei leitud, proovi hiljem uuesti",
|
||||||
|
"selectThisPackage": "Vali see pakett",
|
||||||
|
"selectPackage": "Vali pakett",
|
||||||
|
"comparePackages": "Võrdle pakette"
|
||||||
|
}
|
||||||
8
public/locales/ru/booking.json
Normal file
8
public/locales/ru/booking.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"title": "Select service",
|
||||||
|
"description": "Select the appropriate service or package according to your health needs or goals.",
|
||||||
|
"analysisPackages": {
|
||||||
|
"title": "Analysis packages",
|
||||||
|
"description": "Get to know the personal analysis packages and order"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,8 +37,5 @@
|
|||||||
"footerDescription": "Here you can add a description about your company or product",
|
"footerDescription": "Here you can add a description about your company or product",
|
||||||
"copyright": "© Copyright {{year}} {{product}}. All Rights Reserved.",
|
"copyright": "© Copyright {{year}} {{product}}. All Rights Reserved.",
|
||||||
"heroSubtitle": "A simple, convenient, and quick overview of your health condition",
|
"heroSubtitle": "A simple, convenient, and quick overview of your health condition",
|
||||||
"selectPackage": "Select package",
|
|
||||||
"selectThisPackage": "Select this package",
|
|
||||||
"comparePackages": "Compare packages",
|
|
||||||
"notInterestedInAudit": "Currently not interested in a health audit"
|
"notInterestedInAudit": "Currently not interested in a health audit"
|
||||||
}
|
}
|
||||||
7
public/locales/ru/order-analysis-package.json
Normal file
7
public/locales/ru/order-analysis-package.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"title": "Select analysis package",
|
||||||
|
"noPackagesAvailable": "No packages available",
|
||||||
|
"selectThisPackage": "Select this package",
|
||||||
|
"selectPackage": "Select package",
|
||||||
|
"comparePackages": "Compare packages"
|
||||||
|
}
|
||||||
@@ -89,4 +89,8 @@
|
|||||||
.lucide {
|
.lucide {
|
||||||
@apply size-4;
|
@apply size-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user