Compare commits
4 Commits
feature/ME
...
MED-122
| Author | SHA1 | Date | |
|---|---|---|---|
| 615dde52e6 | |||
| 7f2c6f2374 | |||
| 6368a5b5ff | |||
| 8e82736f09 |
22
.env
22
.env
@@ -53,23 +53,5 @@ NEXT_PUBLIC_DEFAULT_LOCALE=et
|
|||||||
NEXT_PUBLIC_TEAM_NAVIGATION_STYLE=custom
|
NEXT_PUBLIC_TEAM_NAVIGATION_STYLE=custom
|
||||||
NEXT_PUBLIC_USER_NAVIGATION_STYLE=custom
|
NEXT_PUBLIC_USER_NAVIGATION_STYLE=custom
|
||||||
|
|
||||||
#### MEDUSA
|
# MEDUSA
|
||||||
#NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_0ec86252438b38ce18d5601f7877e4395d7e0a6afa8687dfea8d37af33015633
|
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=
|
||||||
#MEDUSA_BACKEND_URL=http://5.181.51.38:9000
|
|
||||||
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_e23a820689a07d55aa0a0ad187268559f5d6288ecb0768ff4520516285bdef84
|
|
||||||
MEDUSA_BACKEND_URL=http://localhost:9000
|
|
||||||
# NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_068d930c33fea53608a410d84a51935f6ce2ccec5bef8e0ecf75eaee602ac486
|
|
||||||
# MEDUSA_BACKEND_URL=https://backoffice-test.medreport.ee:443
|
|
||||||
|
|
||||||
#### MONTONIO
|
|
||||||
NEXT_PUBLIC_MONTONIO_ACCESS_KEY=7da5d7fa-3383-4997-9435-46aa818f4ead
|
|
||||||
MONTONIO_SECRET_KEY=rNZkzwxOiH93mzkdV53AvhSsbGidrgO2Kl5lE/IT7cvo
|
|
||||||
MONTONIO_API_URL=https://sandbox-stargate.montonio.com
|
|
||||||
|
|
||||||
#### SUPABASE
|
|
||||||
# NEXT_PUBLIC_SUPABASE_URL=https://oqsdacktkhmbylmzstjq.supabase.co
|
|
||||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9xc2RhY2t0a2htYnlsbXpzdGpxIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDY1MjgxMjMsImV4cCI6MjA2MjEwNDEyM30.LdHCTWxijFmhXdnT9KVuLRAVbtSwY7OO-oLtpd8GmO0
|
|
||||||
# SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9xc2RhY2t0a2htYnlsbXpzdGpxIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc0NjUyODEyMywiZXhwIjoyMDYyMTA0MTIzfQ.KVcnkZ21Pd0XkJho23dZqFHawVTLQqfvF7l2RxsELLk
|
|
||||||
NEXT_PUBLIC_SUPABASE_URL=http://5.181.51.38:54321
|
|
||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
|
|
||||||
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
# These values are only used when running the app in development mode.
|
# These values are only used when running the app in development mode.
|
||||||
|
|
||||||
# SUPABASE
|
# SUPABASE
|
||||||
NEXT_PUBLIC_SUPABASE_URL=http://5.181.51.38:54321
|
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
|
||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
|
||||||
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
|
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,4 @@ EMAIL_HOST= # refer to your email provider's documentation
|
|||||||
EMAIL_PORT= # or 465 for SSL
|
EMAIL_PORT= # or 465 for SSL
|
||||||
EMAIL_TLS= # or false for SSL (see provider documentation)
|
EMAIL_TLS= # or false for SSL (see provider documentation)
|
||||||
|
|
||||||
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=
|
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=
|
||||||
|
|
||||||
NEXT_PUBLIC_MONTONIO_ACCESS_KEY=7da5d7fa-3383-4997-9435-46aa818f4ead
|
|
||||||
MONTONIO_SECRET_KEY=rNZkzwxOiH93mzkdV53AvhSsbGidrgO2Kl5lE/IT7cvo
|
|
||||||
MONTONIO_API_URL=https://sandbox-stargate.montonio.com
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
import { NextResponse } from 'next/server';
|
|
||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { enhanceRouteHandler } from '@kit/next/routes';
|
|
||||||
import { getLogger } from '@kit/shared/logger';
|
|
||||||
|
|
||||||
interface MontonioOrderToken {
|
|
||||||
uuid: string;
|
|
||||||
accessKey: string;
|
|
||||||
merchantReference: string;
|
|
||||||
merchantReferenceDisplay: string;
|
|
||||||
paymentStatus:
|
|
||||||
| 'PAID'
|
|
||||||
| 'FAILED'
|
|
||||||
| 'CANCELLED'
|
|
||||||
| 'PENDING'
|
|
||||||
| 'EXPIRED'
|
|
||||||
| 'REFUNDED';
|
|
||||||
paymentMethod: string;
|
|
||||||
grandTotal: number;
|
|
||||||
currency: string;
|
|
||||||
senderIban?: string;
|
|
||||||
senderName?: string;
|
|
||||||
paymentProviderName?: string;
|
|
||||||
paymentLinkUuid: string;
|
|
||||||
iat: number;
|
|
||||||
exp: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BodySchema = z.object({
|
|
||||||
token: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const POST = enhanceRouteHandler(
|
|
||||||
async ({ request }) => {
|
|
||||||
const logger = await getLogger();
|
|
||||||
const body = await request.json();
|
|
||||||
const namespace = 'montonio.verify-token';
|
|
||||||
|
|
||||||
const activeCartId = request.cookies.get('_medusa_cart_id')?.value;
|
|
||||||
console.info('cart id', activeCartId);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { token } = BodySchema.parse(body);
|
|
||||||
|
|
||||||
const secretKey = process.env.MONTONIO_SECRET_KEY as string;
|
|
||||||
|
|
||||||
if (!secretKey) {
|
|
||||||
logger.error(
|
|
||||||
{
|
|
||||||
name: namespace,
|
|
||||||
},
|
|
||||||
`Missing MONTONIO_SECRET_KEY`,
|
|
||||||
);
|
|
||||||
|
|
||||||
throw new Error('Server misconfiguration.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const decoded = jwt.verify(token, secretKey, {
|
|
||||||
algorithms: ['HS256'],
|
|
||||||
}) as MontonioOrderToken;
|
|
||||||
|
|
||||||
const [, cartId] = decoded.merchantReferenceDisplay.split(':');
|
|
||||||
console.info('active cart id parsed', {cartId, activeCartId, decoded:decoded.merchantReferenceDisplay});
|
|
||||||
if (cartId !== activeCartId) {
|
|
||||||
throw new Error('Invalid cart id');
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
{
|
|
||||||
name: namespace,
|
|
||||||
status: decoded.paymentStatus,
|
|
||||||
orderId: decoded.uuid,
|
|
||||||
},
|
|
||||||
`Successfully verified Montonio token.`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
status: decoded.paymentStatus,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(
|
|
||||||
{
|
|
||||||
name: namespace,
|
|
||||||
error,
|
|
||||||
},
|
|
||||||
`Failed to verify Montonio token`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const message = error instanceof Error ? error.message : 'Invalid token';
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
error: message,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 400,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
auth: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { PageBody, PageHeader } from '@/packages/ui/src/makerkit/page';
|
|
||||||
import { MontonioCheckoutCallback } from '../../../../_components/cart/montonio-checkout-callback';
|
|
||||||
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
|
|
||||||
export async function generateMetadata() {
|
|
||||||
const { t } = await createI18nServerInstance();
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: t('cart:montonioCallback.title'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function MontonioCheckoutCallbackPage() {
|
|
||||||
return (
|
|
||||||
<div className={'flex h-full flex-1 flex-col'}>
|
|
||||||
<PageHeader title={<Trans i18nKey="cart:montonioCallback.title" />} />
|
|
||||||
<PageBody>
|
|
||||||
<MontonioCheckoutCallback />
|
|
||||||
</PageBody>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,47 +1,59 @@
|
|||||||
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
|
|
||||||
import { PageBody, PageHeader } from '@/packages/ui/src/makerkit/page';
|
import { PageBody, PageHeader } from '@/packages/ui/src/makerkit/page';
|
||||||
|
import { Metadata } from 'next';
|
||||||
|
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
import { retrieveCart } from '~/medusa/lib/data/cart';
|
import { retrieveCart } from '~/medusa/lib/data/cart';
|
||||||
import Cart from '../../_components/cart';
|
import { retrieveCustomer } from '~/medusa/lib/data/customer';
|
||||||
import { listCollections } from '@lib/data';
|
import CartTemplate from '~/medusa/modules/cart/templates';
|
||||||
import CartTimer from '../../_components/cart/cart-timer';
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
|
|
||||||
export async function generateMetadata() {
|
export const metadata: Metadata = {
|
||||||
const { t } = await createI18nServerInstance();
|
title: 'Cart',
|
||||||
|
description: 'View your cart',
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
export default async function Cart() {
|
||||||
title: t('cart:title'),
|
const cart2 = await retrieveCart().catch((error) => {
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function CartPage() {
|
|
||||||
const cart = await retrieveCart().catch((error) => {
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return notFound();
|
return notFound();
|
||||||
});
|
});
|
||||||
|
|
||||||
const { collections } = await listCollections({
|
const customer = await retrieveCustomer();
|
||||||
limit: "100",
|
|
||||||
});
|
|
||||||
|
|
||||||
const analysisPackagesCollection = collections.find(({ handle }) => handle === 'analysis-packages');
|
const cart: NonNullable<typeof cart2> = {
|
||||||
const analysisPackages = analysisPackagesCollection && cart?.items
|
items: [
|
||||||
? cart.items.filter((item) => item.product?.collection_id === analysisPackagesCollection.id)
|
{
|
||||||
: [];
|
id: '1',
|
||||||
const otherItems = cart?.items?.filter((item) => item.product?.collection_id !== analysisPackagesCollection?.id) ?? [];
|
quantity: 1,
|
||||||
|
cart: cart2!,
|
||||||
const otherItemsSorted = otherItems.sort((a, b) => (a.updated_at ?? "") > (b.updated_at ?? "") ? -1 : 1);
|
item_total: 100,
|
||||||
const item = otherItemsSorted[0];
|
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 (
|
return (
|
||||||
<PageBody>
|
<PageBody>
|
||||||
<PageHeader title={<Trans i18nKey="cart:title" />}>
|
<PageHeader title={`Ostukorv`} description={`Vali kalendrist sobiv kuupäev ja broneeri endale vastuvõtuaeg.`} />
|
||||||
{item && item.updated_at && <CartTimer cartItem={item} />}
|
|
||||||
</PageHeader>
|
<CartTemplate cart={cart} customer={customer} />
|
||||||
<Cart cart={cart} analysisPackages={analysisPackages} otherItems={otherItems} />
|
|
||||||
</PageBody>
|
</PageBody>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import { HomeMenuNavigation } from '../_components/home-menu-navigation';
|
|||||||
import { HomeMobileNavigation } from '../_components/home-mobile-navigation';
|
import { HomeMobileNavigation } from '../_components/home-mobile-navigation';
|
||||||
import { HomeSidebar } from '../_components/home-sidebar';
|
import { HomeSidebar } from '../_components/home-sidebar';
|
||||||
import { loadUserWorkspace } from '../_lib/server/load-user-workspace';
|
import { loadUserWorkspace } from '../_lib/server/load-user-workspace';
|
||||||
import { retrieveCart } from '@lib/data';
|
|
||||||
|
|
||||||
function UserHomeLayout({ children }: React.PropsWithChildren) {
|
function UserHomeLayout({ children }: React.PropsWithChildren) {
|
||||||
const state = use(getLayoutState());
|
const state = use(getLayoutState());
|
||||||
@@ -56,13 +55,12 @@ function SidebarLayout({ children }: React.PropsWithChildren) {
|
|||||||
|
|
||||||
function HeaderLayout({ children }: React.PropsWithChildren) {
|
function HeaderLayout({ children }: React.PropsWithChildren) {
|
||||||
const workspace = use(loadUserWorkspace());
|
const workspace = use(loadUserWorkspace());
|
||||||
const cart = use(retrieveCart());
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UserWorkspaceContextProvider value={workspace}>
|
<UserWorkspaceContextProvider value={workspace}>
|
||||||
<Page style={'header'}>
|
<Page style={'header'}>
|
||||||
<PageNavigation>
|
<PageNavigation>
|
||||||
<HomeMenuNavigation workspace={workspace} cart={cart} />
|
<HomeMenuNavigation workspace={workspace} />
|
||||||
</PageNavigation>
|
</PageNavigation>
|
||||||
|
|
||||||
<PageMobileNavigation className={'flex items-center justify-between'}>
|
<PageMobileNavigation className={'flex items-center justify-between'}>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { PageBody } from '@kit/ui/page';
|
|||||||
import { Trans } from '@kit/ui/trans';
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
|
||||||
import ComparePackagesModal from '../../_components/compare-packages-modal';
|
import ComparePackagesModal from '../../_components/compare-packages-modal';
|
||||||
import { loadAnalysisPackages } from '../../_lib/server/load-analysis-packages';
|
|
||||||
|
|
||||||
export const generateMetadata = async () => {
|
export const generateMetadata = async () => {
|
||||||
const i18n = await createI18nServerInstance();
|
const i18n = await createI18nServerInstance();
|
||||||
@@ -20,8 +19,6 @@ export const generateMetadata = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function OrderAnalysisPackagePage() {
|
async function OrderAnalysisPackagePage() {
|
||||||
const { analysisPackages, countryCode } = await loadAnalysisPackages();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageBody>
|
<PageBody>
|
||||||
<div className="space-y-3 text-center">
|
<div className="space-y-3 text-center">
|
||||||
@@ -29,7 +26,6 @@ async function OrderAnalysisPackagePage() {
|
|||||||
<Trans i18nKey={'marketing:selectPackage'} />
|
<Trans i18nKey={'marketing:selectPackage'} />
|
||||||
</h3>
|
</h3>
|
||||||
<ComparePackagesModal
|
<ComparePackagesModal
|
||||||
analysisPackages={analysisPackages}
|
|
||||||
triggerElement={
|
triggerElement={
|
||||||
<Button variant="secondary" className="gap-2">
|
<Button variant="secondary" className="gap-2">
|
||||||
<Trans i18nKey={'marketing:comparePackages'} />
|
<Trans i18nKey={'marketing:comparePackages'} />
|
||||||
@@ -38,7 +34,7 @@ async function OrderAnalysisPackagePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SelectAnalysisPackages analysisPackages={analysisPackages} countryCode={countryCode} />
|
<SelectAnalysisPackages />
|
||||||
</PageBody>
|
</PageBody>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import { notFound } from 'next/navigation';
|
|
||||||
|
|
||||||
import { retrieveOrder } from '~/medusa/lib/data/orders';
|
|
||||||
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
|
|
||||||
import OrderCompleted from '@/app/home/(user)/_components/order/order-completed';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
params: Promise<{ orderId: string }>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function generateMetadata() {
|
|
||||||
const { t } = await createI18nServerInstance();
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: t('cart:orderConfirmed.title'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function OrderConfirmedPage(props: Props) {
|
|
||||||
const params = await props.params;
|
|
||||||
const order = await retrieveOrder(params.orderId).catch(() => null);
|
|
||||||
|
|
||||||
if (!order) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
return <OrderCompleted order={order} />;
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { Trash } from "lucide-react";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { toast } from 'sonner';
|
|
||||||
|
|
||||||
import { deleteLineItem } from "@lib/data/cart";
|
|
||||||
import { Spinner } from "@medusajs/icons";
|
|
||||||
|
|
||||||
const CartItemDelete = ({
|
|
||||||
id,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
}) => {
|
|
||||||
const [isDeleting, setIsDeleting] = useState(false);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const handleDelete = async () => {
|
|
||||||
setIsDeleting(true);
|
|
||||||
|
|
||||||
const promise = async () => {
|
|
||||||
await deleteLineItem(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
toast.promise(promise, {
|
|
||||||
success: t(`cart:items.delete.success`),
|
|
||||||
loading: t(`cart:items.delete.loading`),
|
|
||||||
error: t(`cart:items.delete.error`),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center justify-between text-small-regular">
|
|
||||||
<button
|
|
||||||
className="flex gap-x-1 text-ui-fg-subtle hover:text-ui-fg-base cursor-pointer"
|
|
||||||
onClick={() => handleDelete()}
|
|
||||||
>
|
|
||||||
{isDeleting ? <Spinner className="animate-spin" /> : <Trash />}
|
|
||||||
<span>{children}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CartItemDelete;
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { HttpTypes } from "@medusajs/types"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import {
|
|
||||||
TableCell,
|
|
||||||
TableRow,
|
|
||||||
} from '@kit/ui/table';
|
|
||||||
import { formatCurrency } from "@/packages/shared/src/utils"
|
|
||||||
import CartItemDelete from "./cart-item-delete";
|
|
||||||
|
|
||||||
export default function CartItem({ item, currencyCode }: {
|
|
||||||
item: HttpTypes.StoreCartLineItem
|
|
||||||
currencyCode: string
|
|
||||||
}) {
|
|
||||||
const { i18n: { language } } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow className="w-full" data-testid="product-row">
|
|
||||||
<TableCell className="text-left w-[100%] px-6">
|
|
||||||
<p
|
|
||||||
className="txt-medium-plus text-ui-fg-base"
|
|
||||||
data-testid="product-title"
|
|
||||||
>
|
|
||||||
{item.product_title}
|
|
||||||
</p>
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell className="px-6">
|
|
||||||
{item.quantity}
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell className="min-w-[80px] px-6">
|
|
||||||
{formatCurrency({
|
|
||||||
value: item.unit_price,
|
|
||||||
currencyCode,
|
|
||||||
locale: language,
|
|
||||||
})}
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell className="min-w-[80px] px-6">
|
|
||||||
{formatCurrency({
|
|
||||||
value: item.total,
|
|
||||||
currencyCode,
|
|
||||||
locale: language,
|
|
||||||
})}
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell className="text-right px-6">
|
|
||||||
<span className="flex gap-x-1 justify-end w-[60px]">
|
|
||||||
<CartItemDelete id={item.id} />
|
|
||||||
</span>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import { StoreCart, StoreCartLineItem } from "@medusajs/types"
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
import CartItem from "./cart-item";
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
TableHeader,
|
|
||||||
} from '@kit/ui/table';
|
|
||||||
|
|
||||||
export default function CartItems({ cart, items, productColumnLabelKey }: {
|
|
||||||
cart: StoreCart;
|
|
||||||
items: StoreCartLineItem[];
|
|
||||||
productColumnLabelKey: string;
|
|
||||||
}) {
|
|
||||||
if (!items || items.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table className="rounded-lg border border-separate">
|
|
||||||
<TableHeader className="text-ui-fg-subtle txt-medium-plus">
|
|
||||||
<TableRow>
|
|
||||||
<TableHead className="px-6">
|
|
||||||
<Trans i18nKey={productColumnLabelKey} />
|
|
||||||
</TableHead>
|
|
||||||
<TableHead className="px-6">
|
|
||||||
<Trans i18nKey="cart:table.quantity" />
|
|
||||||
</TableHead>
|
|
||||||
<TableHead className="px-6 min-w-[100px]">
|
|
||||||
<Trans i18nKey="cart:table.price" />
|
|
||||||
</TableHead>
|
|
||||||
<TableHead className="px-6 min-w-[100px]">
|
|
||||||
<Trans i18nKey="cart:table.total" />
|
|
||||||
</TableHead>
|
|
||||||
<TableHead className="px-6">
|
|
||||||
</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{items
|
|
||||||
.sort((a, b) => (a.created_at ?? "") > (b.created_at ?? "") ? -1 : 1)
|
|
||||||
.map((item) => (
|
|
||||||
<CartItem
|
|
||||||
key={item.id}
|
|
||||||
item={item}
|
|
||||||
currencyCode={cart.currency_code}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { Button } from '@kit/ui/button';
|
|
||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle
|
|
||||||
} from "@kit/ui/alert-dialog";
|
|
||||||
|
|
||||||
import { Timer } from 'lucide-react';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { StoreCartLineItem } from '@medusajs/types';
|
|
||||||
import { handleLineItemTimeout } from '@/lib/services/medusaCart.service';
|
|
||||||
|
|
||||||
const TIMEOUT_MINUTES = 15;
|
|
||||||
|
|
||||||
export default function CartTimer({ cartItem }: { cartItem: StoreCartLineItem }) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [timeLeft, setTimeLeft] = useState<number | null>(null);
|
|
||||||
const [isDialogOpen, setDialogOpen] = useState(false);
|
|
||||||
|
|
||||||
const updatedAt = cartItem.updated_at!;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const date = new Date(updatedAt);
|
|
||||||
date.setMinutes(date.getMinutes() + TIMEOUT_MINUTES);
|
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
const now = new Date();
|
|
||||||
const diff = date.getTime() - now.getTime();
|
|
||||||
setTimeLeft(diff);
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, [updatedAt]);
|
|
||||||
|
|
||||||
const minutes = timeLeft ? Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60)) : 0;
|
|
||||||
const seconds = timeLeft ? Math.floor((timeLeft % (1000 * 60)) / 1000) : 0;
|
|
||||||
|
|
||||||
const isTimeLeftPositive = timeLeft === null || timeLeft > 0;
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isTimeLeftPositive) {
|
|
||||||
void handleLineItemTimeout({
|
|
||||||
lineItem: cartItem,
|
|
||||||
});
|
|
||||||
setDialogOpen(true);
|
|
||||||
}
|
|
||||||
}, [isTimeLeftPositive, cartItem.id]);
|
|
||||||
|
|
||||||
if (timeLeft === null) {
|
|
||||||
return <div className='min-h-[40px]' />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="ml-auto">
|
|
||||||
<Button variant="outline" className="flex items-center gap-x-2 bg-accent hover:bg-accent px-4 cursor-default">
|
|
||||||
<Timer />
|
|
||||||
<span className="text-sm">
|
|
||||||
{t('cart:checkout.timeLeft', {
|
|
||||||
timeLeft: `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`,
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AlertDialog open={isDialogOpen} onOpenChange={setDialogOpen}>
|
|
||||||
<AlertDialogContent>
|
|
||||||
<AlertDialogHeader>
|
|
||||||
<AlertDialogTitle>
|
|
||||||
{t('cart:checkout.timeoutTitle')}
|
|
||||||
</AlertDialogTitle>
|
|
||||||
<AlertDialogDescription>
|
|
||||||
{t('cart:checkout.timeoutDescription', { productTitle: cartItem.product?.title })}
|
|
||||||
</AlertDialogDescription>
|
|
||||||
</AlertDialogHeader>
|
|
||||||
<AlertDialogFooter>
|
|
||||||
<AlertDialogAction onClick={() => setDialogOpen(false)}>
|
|
||||||
{t('cart:checkout.timeoutAction')}
|
|
||||||
</AlertDialogAction>
|
|
||||||
</AlertDialogFooter>
|
|
||||||
</AlertDialogContent>
|
|
||||||
</AlertDialog>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { Badge, Heading, Text } from "@medusajs/ui"
|
|
||||||
import React, { useActionState } from "react";
|
|
||||||
|
|
||||||
import { applyPromotions, submitPromotionForm } from "@lib/data/cart"
|
|
||||||
import { convertToLocale } from "@lib/util/money"
|
|
||||||
import { StoreCart, StorePromotion } from "@medusajs/types"
|
|
||||||
import Trash from "@modules/common/icons/trash"
|
|
||||||
import { Button } from '@kit/ui/button';
|
|
||||||
import { Form, FormControl, FormField, FormItem } from "@kit/ui/form";
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
import { Input } from "@kit/ui/input";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
const DiscountCodeSchema = z.object({
|
|
||||||
code: z.string().min(1),
|
|
||||||
})
|
|
||||||
|
|
||||||
export default function DiscountCode({ cart }: {
|
|
||||||
cart: StoreCart & {
|
|
||||||
promotions: StorePromotion[]
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation('cart');
|
|
||||||
|
|
||||||
const { promotions = [] } = cart;
|
|
||||||
|
|
||||||
const removePromotionCode = async (code: string) => {
|
|
||||||
const validPromotions = promotions.filter(
|
|
||||||
(promotion) => promotion.code !== code
|
|
||||||
)
|
|
||||||
|
|
||||||
await applyPromotions(
|
|
||||||
validPromotions.filter((p) => p.code === undefined).map((p) => p.code!)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const addPromotionCode = async (code: string) => {
|
|
||||||
const codes = promotions
|
|
||||||
.filter((p) => p.code === undefined)
|
|
||||||
.map((p) => p.code!)
|
|
||||||
codes.push(code.toString())
|
|
||||||
|
|
||||||
await applyPromotions(codes)
|
|
||||||
|
|
||||||
form.reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
const [message, formAction] = useActionState(submitPromotionForm, null)
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof DiscountCodeSchema>>({
|
|
||||||
defaultValues: {
|
|
||||||
code: '',
|
|
||||||
},
|
|
||||||
resolver: zodResolver(DiscountCodeSchema),
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full bg-white flex flex-col txt-medium">
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit((data) => addPromotionCode(data.code))}
|
|
||||||
className="w-full mb-2 flex gap-x-2"
|
|
||||||
>
|
|
||||||
<FormField
|
|
||||||
name={'code'}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex-1">
|
|
||||||
<FormControl>
|
|
||||||
<Input required type="text" {...field} placeholder={t('cart:discountCode.placeholder')} />
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
variant="secondary"
|
|
||||||
className="h-full"
|
|
||||||
>
|
|
||||||
<Trans i18nKey={'cart:discountCode.apply'} />
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
<Trans i18nKey={'cart:discountCode.subtitle'} />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{promotions.length > 0 && (
|
|
||||||
<div className="w-full flex items-center">
|
|
||||||
<div className="flex flex-col w-full">
|
|
||||||
<Heading className="txt-medium mb-2">
|
|
||||||
Promotion(s) applied:
|
|
||||||
</Heading>
|
|
||||||
|
|
||||||
{promotions.map((promotion) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={promotion.id}
|
|
||||||
className="flex items-center justify-between w-full max-w-full mb-2"
|
|
||||||
data-testid="discount-row"
|
|
||||||
>
|
|
||||||
<Text className="flex gap-x-1 items-baseline txt-small-plus w-4/5 pr-1">
|
|
||||||
<span className="truncate" data-testid="discount-code">
|
|
||||||
<Badge
|
|
||||||
color={promotion.is_automatic ? "green" : "grey"}
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
{promotion.code}
|
|
||||||
</Badge>{" "}
|
|
||||||
(
|
|
||||||
{promotion.application_method?.value !== undefined &&
|
|
||||||
promotion.application_method.currency_code !==
|
|
||||||
undefined && (
|
|
||||||
<>
|
|
||||||
{promotion.application_method.type ===
|
|
||||||
"percentage"
|
|
||||||
? `${promotion.application_method.value}%`
|
|
||||||
: convertToLocale({
|
|
||||||
amount: promotion.application_method.value,
|
|
||||||
currency_code:
|
|
||||||
promotion.application_method
|
|
||||||
.currency_code,
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
)
|
|
||||||
{/* {promotion.is_automatic && (
|
|
||||||
<Tooltip content="This promotion is automatically applied">
|
|
||||||
<InformationCircleSolid className="inline text-zinc-400" />
|
|
||||||
</Tooltip>
|
|
||||||
)} */}
|
|
||||||
</span>
|
|
||||||
</Text>
|
|
||||||
{!promotion.is_automatic && (
|
|
||||||
<button
|
|
||||||
className="flex items-center"
|
|
||||||
onClick={() => {
|
|
||||||
if (!promotion.code) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
removePromotionCode(promotion.code)
|
|
||||||
}}
|
|
||||||
data-testid="remove-discount-button"
|
|
||||||
>
|
|
||||||
<Trash size={14} />
|
|
||||||
<span className="sr-only">
|
|
||||||
Remove discount code from order
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { StoreCart, StoreCartLineItem } from "@medusajs/types"
|
|
||||||
import CartItems from "./cart-items"
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
import { Button } from '@kit/ui/button';
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardHeader,
|
|
||||||
} from '@kit/ui/card';
|
|
||||||
import DiscountCode from "./discount-code";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { initiatePaymentSession } from "@lib/data/cart";
|
|
||||||
import { formatCurrency } from "@/packages/shared/src/utils";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { handleNavigateToPayment } from "@/lib/services/medusaCart.service";
|
|
||||||
|
|
||||||
const IS_DISCOUNT_SHOWN = false as boolean;
|
|
||||||
|
|
||||||
export default function Cart({
|
|
||||||
cart,
|
|
||||||
analysisPackages,
|
|
||||||
otherItems,
|
|
||||||
}: {
|
|
||||||
cart: StoreCart | null
|
|
||||||
analysisPackages: StoreCartLineItem[];
|
|
||||||
otherItems: StoreCartLineItem[];
|
|
||||||
}) {
|
|
||||||
const router = useRouter();
|
|
||||||
const { i18n: { language } } = useTranslation();
|
|
||||||
|
|
||||||
const items = cart?.items ?? [];
|
|
||||||
|
|
||||||
if (!cart || items.length === 0) {
|
|
||||||
return (
|
|
||||||
<div className="content-container py-5 lg:px-4">
|
|
||||||
<div>
|
|
||||||
<div className="flex flex-col justify-center items-center" data-testid="empty-cart-message">
|
|
||||||
<h4 className="text-center">
|
|
||||||
<Trans i18nKey="cart:emptyCartMessage" />
|
|
||||||
</h4>
|
|
||||||
<p className="text-center">
|
|
||||||
<Trans i18nKey="cart:emptyCartMessageDescription" />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handlePayment() {
|
|
||||||
const response = await initiatePaymentSession(cart!, {
|
|
||||||
provider_id: 'pp_system_default',
|
|
||||||
});
|
|
||||||
if (response.payment_collection) {
|
|
||||||
const url = await handleNavigateToPayment({ language });
|
|
||||||
router.push(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid grid-cols-1 small:grid-cols-[1fr_360px] gap-x-40 lg:px-4">
|
|
||||||
<div className="flex flex-col bg-white gap-y-6">
|
|
||||||
<CartItems cart={cart} items={analysisPackages} productColumnLabelKey="cart:items.analysisPackages.productColumnLabel" />
|
|
||||||
<CartItems cart={cart} items={otherItems} productColumnLabelKey="cart:items.services.productColumnLabel" />
|
|
||||||
</div>
|
|
||||||
{Array.isArray(cart.items) && cart.items.length > 0 && (
|
|
||||||
<div className="flex justify-end gap-x-4 px-6 py-4">
|
|
||||||
<div className="mr-[36px]">
|
|
||||||
<p className="ml-0 font-bold text-sm">
|
|
||||||
<Trans i18nKey="cart:total" />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="mr-[116px]">
|
|
||||||
<p className="text-sm">
|
|
||||||
{formatCurrency({
|
|
||||||
value: cart.total,
|
|
||||||
currencyCode: cart.currency_code,
|
|
||||||
locale: language,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex gap-y-6 py-8">
|
|
||||||
{IS_DISCOUNT_SHOWN && (
|
|
||||||
<Card
|
|
||||||
className="flex flex-col justify-between w-1/2"
|
|
||||||
>
|
|
||||||
<CardHeader className="pb-4">
|
|
||||||
<h5>
|
|
||||||
<Trans i18nKey="cart:discountCode.title" />
|
|
||||||
</h5>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<DiscountCode cart={{ ...cart }} />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Button className="h-10" onClick={handlePayment}>
|
|
||||||
<Trans i18nKey="cart:checkout.goToCheckout" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useRouter, useSearchParams } from 'next/navigation';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
|
||||||
import { Button } from '@kit/ui/button';
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
import { placeOrder } from "@lib/data/cart"
|
|
||||||
import Link from 'next/link';
|
|
||||||
import GlobalLoader from '../../loading';
|
|
||||||
|
|
||||||
enum Status {
|
|
||||||
LOADING = 'LOADING',
|
|
||||||
ERROR = 'ERROR',
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MontonioCheckoutCallback() {
|
|
||||||
const router = useRouter();
|
|
||||||
const [status, setStatus] = useState<Status>(Status.LOADING);
|
|
||||||
const [isFinalized, setIsFinalized] = useState(false);
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isFinalized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = searchParams.get('order-token');
|
|
||||||
if (!token) {
|
|
||||||
//router.push('/home/cart');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function verifyToken() {
|
|
||||||
setStatus(Status.LOADING);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/montonio/verify-token', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ token }),
|
|
||||||
});
|
|
||||||
setIsFinalized(true);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const body = await response.json();
|
|
||||||
throw new Error(body.error ?? 'Failed to verify payment status.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = await response.json();
|
|
||||||
const paymentStatus = body.status as string;
|
|
||||||
if (paymentStatus === 'PAID') {
|
|
||||||
try {
|
|
||||||
await placeOrder();
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error placing order", e);
|
|
||||||
router.push('/home/cart');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error('Payment failed or pending');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error verifying token", e);
|
|
||||||
setStatus(Status.ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void verifyToken();
|
|
||||||
}, [searchParams, isFinalized]);
|
|
||||||
|
|
||||||
if (status === Status.ERROR) {
|
|
||||||
return (
|
|
||||||
<div className={'flex flex-col space-y-4'}>
|
|
||||||
<Alert variant={'destructive'}>
|
|
||||||
<AlertTitle>
|
|
||||||
<Trans i18nKey={'checkout.error.title'} />
|
|
||||||
</AlertTitle>
|
|
||||||
|
|
||||||
<AlertDescription>
|
|
||||||
<p>
|
|
||||||
<Trans i18nKey={'checkout.error.description'} />
|
|
||||||
</p>
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
|
|
||||||
<div className={'flex'}>
|
|
||||||
<Button asChild>
|
|
||||||
<Link href={'/home'}>
|
|
||||||
<Trans i18nKey={'checkout.goToDashboard'} />
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <GlobalLoader />;
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,6 @@ 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';
|
||||||
import { StoreProduct } from '@medusajs/types';
|
|
||||||
|
|
||||||
const dummyCards = [
|
const dummyCards = [
|
||||||
{
|
{
|
||||||
@@ -106,10 +105,8 @@ const CheckWithBackground = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ComparePackagesModal = async ({
|
const ComparePackagesModal = async ({
|
||||||
analysisPackages,
|
|
||||||
triggerElement,
|
triggerElement,
|
||||||
}: {
|
}: {
|
||||||
analysisPackages: StoreProduct[];
|
|
||||||
triggerElement: JSX.Element;
|
triggerElement: JSX.Element;
|
||||||
}) => {
|
}) => {
|
||||||
const { t, language } = await createI18nServerInstance();
|
const { t, language } = await createI18nServerInstance();
|
||||||
@@ -143,25 +140,21 @@ const ComparePackagesModal = async ({
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead></TableHead>
|
<TableHead></TableHead>
|
||||||
{analysisPackages.map(
|
{dummyCards.map(
|
||||||
(product) => {
|
({ titleKey, price, nrOfAnalyses, tagColor }) => (
|
||||||
const variant = product.variants?.[0];
|
<TableHead key={titleKey} className="py-2">
|
||||||
const titleKey = product.title;
|
<PackageHeader
|
||||||
const price = variant?.calculated_price?.calculated_amount ?? 0;
|
title={t(titleKey)}
|
||||||
return (
|
tagColor={tagColor}
|
||||||
<TableHead key={titleKey} className="py-2">
|
analysesNr={t('product:nrOfAnalyses', {
|
||||||
<PackageHeader
|
nr: nrOfAnalyses,
|
||||||
title={t(titleKey)}
|
})}
|
||||||
tagColor='bg-cyan'
|
language={language}
|
||||||
analysesNr={t('product:nrOfAnalyses', {
|
price={price}
|
||||||
nr: product?.metadata?.nrOfAnalyses ?? 0,
|
/>
|
||||||
})}
|
</TableHead>
|
||||||
language={language}
|
),
|
||||||
price={price}
|
)}
|
||||||
/>
|
|
||||||
</TableHead>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
|
|||||||
@@ -10,21 +10,9 @@ import { Button } from '@kit/ui/button';
|
|||||||
|
|
||||||
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 { StoreCart } from '@medusajs/types';
|
|
||||||
import { formatCurrency } from '@/packages/shared/src/utils';
|
|
||||||
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
|
|
||||||
|
|
||||||
export async function HomeMenuNavigation(props: { workspace: UserWorkspace, cart: StoreCart | null }) {
|
export function HomeMenuNavigation(props: { workspace: UserWorkspace }) {
|
||||||
const { language } = await createI18nServerInstance();
|
|
||||||
const { workspace, user, accounts } = props.workspace;
|
const { workspace, user, accounts } = props.workspace;
|
||||||
const totalValue = props.cart?.total ? formatCurrency({
|
|
||||||
currencyCode: props.cart.currency_code,
|
|
||||||
locale: language,
|
|
||||||
value: props.cart.total,
|
|
||||||
}) : 0;
|
|
||||||
|
|
||||||
const cartItemsCount = props.cart?.items?.length ?? 0;
|
|
||||||
const hasCartItems = cartItemsCount > 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'flex w-full flex-1 items-center justify-between gap-3'}>
|
<div className={'flex w-full flex-1 items-center justify-between gap-3'}>
|
||||||
@@ -38,15 +26,13 @@ export async function HomeMenuNavigation(props: { workspace: UserWorkspace, cart
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex items-center justify-end gap-3">
|
<div className="flex items-center justify-end gap-3">
|
||||||
{hasCartItems && (
|
<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'>{totalValue}</span>
|
</Button>
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Link href='/home/cart'>
|
<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" /> ({hasCartItems ? cartItemsCount : 0})
|
<Trans i18nKey="common:shoppingCart" /> (0)
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<UserNotifications userId={user.id} />
|
<UserNotifications userId={user.id} />
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { formatCurrency } from "@/packages/shared/src/utils"
|
|
||||||
import { StoreOrder } from "@medusajs/types"
|
|
||||||
import React from "react"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
|
|
||||||
export default function CartTotals({ order }: {
|
|
||||||
order: StoreOrder
|
|
||||||
}) {
|
|
||||||
const { i18n: { language } } = useTranslation()
|
|
||||||
const {
|
|
||||||
currency_code,
|
|
||||||
total,
|
|
||||||
subtotal,
|
|
||||||
tax_total,
|
|
||||||
discount_total,
|
|
||||||
gift_card_total,
|
|
||||||
} = order
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="flex flex-col gap-y-2 txt-medium text-ui-fg-subtle ">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="flex gap-x-1 items-center">
|
|
||||||
<Trans i18nKey="cart:orderConfirmed.subtotal" />
|
|
||||||
</span>
|
|
||||||
<span data-testid="cart-subtotal" data-value={subtotal || 0}>
|
|
||||||
{formatCurrency({ value: subtotal ?? 0, currencyCode: currency_code, locale: language })}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{!!discount_total && (
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span><Trans i18nKey="cart:orderConfirmed.discount" /></span>
|
|
||||||
<span
|
|
||||||
className="text-ui-fg-interactive"
|
|
||||||
data-testid="cart-discount"
|
|
||||||
data-value={discount_total || 0}
|
|
||||||
>
|
|
||||||
-{" "}
|
|
||||||
{formatCurrency({ value: discount_total ?? 0, currencyCode: currency_code, locale: language })}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="flex gap-x-1 items-center ">
|
|
||||||
<Trans i18nKey="cart:orderConfirmed.taxes" />
|
|
||||||
</span>
|
|
||||||
<span data-testid="cart-taxes" data-value={tax_total || 0}>
|
|
||||||
{formatCurrency({ value: tax_total ?? 0, currencyCode: currency_code, locale: language })}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{!!gift_card_total && (
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span><Trans i18nKey="cart:orderConfirmed.giftCard" /></span>
|
|
||||||
<span
|
|
||||||
className="text-ui-fg-interactive"
|
|
||||||
data-testid="cart-gift-card-amount"
|
|
||||||
data-value={gift_card_total || 0}
|
|
||||||
>
|
|
||||||
-{" "}
|
|
||||||
{formatCurrency({ value: gift_card_total ?? 0, currencyCode: currency_code, locale: language })}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="h-px w-full border-b border-gray-200 my-4" />
|
|
||||||
<div className="flex items-center justify-between text-ui-fg-base mb-2 txt-medium ">
|
|
||||||
<span className="font-bold"><Trans i18nKey="cart:orderConfirmed.total" /></span>
|
|
||||||
<span
|
|
||||||
className="txt-xlarge-plus"
|
|
||||||
data-testid="cart-total"
|
|
||||||
data-value={total || 0}
|
|
||||||
>
|
|
||||||
{formatCurrency({ value: total ?? 0, currencyCode: currency_code, locale: language })}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="h-px w-full border-b border-gray-200 mt-4" />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
import { PageBody, PageHeader } from '@kit/ui/page';
|
|
||||||
import { StoreOrder } from "@medusajs/types"
|
|
||||||
import Divider from "@modules/common/components/divider"
|
|
||||||
|
|
||||||
import CartTotals from "./cart-totals"
|
|
||||||
import OrderDetails from "./order-details"
|
|
||||||
import OrderItems from "./order-items"
|
|
||||||
|
|
||||||
export default async function OrderCompleted({
|
|
||||||
order,
|
|
||||||
}: {
|
|
||||||
order: StoreOrder,
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<PageBody>
|
|
||||||
<PageHeader title={<Trans i18nKey="cart:orderConfirmed.title" />} />
|
|
||||||
<Divider />
|
|
||||||
<div className="grid grid-cols-1 small:grid-cols-[1fr_360px] gap-x-40 lg:px-4 gap-y-6">
|
|
||||||
<OrderDetails order={order} />
|
|
||||||
<Divider />
|
|
||||||
<OrderItems order={order} />
|
|
||||||
<CartTotals order={order} />
|
|
||||||
</div>
|
|
||||||
</PageBody>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import { StoreOrder } from "@medusajs/types"
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
|
|
||||||
export default function OrderDetails({ order, showStatus }: {
|
|
||||||
order: StoreOrder
|
|
||||||
showStatus?: boolean
|
|
||||||
}) {
|
|
||||||
const formatStatus = (str: string) => {
|
|
||||||
const formatted = str.split("_").join(" ")
|
|
||||||
|
|
||||||
return formatted.slice(0, 1).toUpperCase() + formatted.slice(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col gap-y-2">
|
|
||||||
<span>
|
|
||||||
<Trans i18nKey="cart:orderConfirmed.orderDate" />:{" "}
|
|
||||||
<span>
|
|
||||||
{new Date(order.created_at).toLocaleDateString()}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span className="text-ui-fg-interactive">
|
|
||||||
<Trans i18nKey="cart:orderConfirmed.orderNumber" />: <span data-testid="order-id">{order.display_id}</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{showStatus && (
|
|
||||||
<>
|
|
||||||
<span>
|
|
||||||
<Trans i18nKey="cart:orderConfirmed.orderStatus" />:{" "}
|
|
||||||
<span className="text-ui-fg-subtle">
|
|
||||||
{formatStatus(order.fulfillment_status)}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<Trans i18nKey="cart:orderConfirmed.paymentStatus" />:{" "}
|
|
||||||
<span
|
|
||||||
className="text-ui-fg-subtle "
|
|
||||||
data-testid="order-payment-status"
|
|
||||||
>
|
|
||||||
{formatStatus(order.payment_status)}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import { StoreCartLineItem, StoreOrderLineItem } from "@medusajs/types"
|
|
||||||
import { TableCell, TableRow } from "@kit/ui/table"
|
|
||||||
|
|
||||||
import LineItemOptions from "@modules/common/components/line-item-options"
|
|
||||||
import LineItemPrice from "@modules/common/components/line-item-price"
|
|
||||||
import LineItemUnitPrice from "@modules/common/components/line-item-unit-price"
|
|
||||||
|
|
||||||
export default function OrderItem({ item, currencyCode }: {
|
|
||||||
item: StoreCartLineItem | StoreOrderLineItem
|
|
||||||
currencyCode: string
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<TableRow className="w-full" data-testid="product-row">
|
|
||||||
{/* <TableCell className="px-6 w-24">
|
|
||||||
<div className="flex w-16">
|
|
||||||
<Thumbnail thumbnail={item.thumbnail} size="square" />
|
|
||||||
</div>
|
|
||||||
</TableCell> */}
|
|
||||||
|
|
||||||
<TableCell className="text-left px-6">
|
|
||||||
<span
|
|
||||||
className="txt-medium-plus text-ui-fg-base"
|
|
||||||
data-testid="product-name"
|
|
||||||
>
|
|
||||||
{item.product_title}
|
|
||||||
</span>
|
|
||||||
<LineItemOptions variant={item.variant} data-testid="product-variant" />
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell className="px-6">
|
|
||||||
<span className="flex flex-col items-end h-full justify-center">
|
|
||||||
<span className="flex gap-x-1 ">
|
|
||||||
<span className="text-ui-fg-muted">
|
|
||||||
{item.quantity}x{" "}
|
|
||||||
</span>
|
|
||||||
<LineItemUnitPrice
|
|
||||||
item={item}
|
|
||||||
style="tight"
|
|
||||||
currencyCode={currencyCode}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<LineItemPrice
|
|
||||||
item={item}
|
|
||||||
style="tight"
|
|
||||||
currencyCode={currencyCode}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import repeat from "@lib/util/repeat"
|
|
||||||
import { StoreOrder } from "@medusajs/types"
|
|
||||||
import { Table, TableBody } from "@kit/ui/table"
|
|
||||||
|
|
||||||
import SkeletonLineItem from "@modules/skeletons/components/skeleton-line-item"
|
|
||||||
import OrderItem from "./order-item"
|
|
||||||
import { Heading } from "@kit/ui/heading"
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
|
|
||||||
export default function OrderItems({ order }: {
|
|
||||||
order: StoreOrder
|
|
||||||
}) {
|
|
||||||
const items = order.items
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col gap-y-4">
|
|
||||||
<Heading level={5} className="flex flex-row text-3xl-regular">
|
|
||||||
<Trans i18nKey="cart:orderConfirmed.summary" />
|
|
||||||
</Heading>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<Table className="rounded-lg border border-separate">
|
|
||||||
<TableBody data-testid="products-table">
|
|
||||||
{items?.length
|
|
||||||
? items
|
|
||||||
.sort((a, b) => (a.created_at ?? "") > (b.created_at ?? "") ? -1 : 1)
|
|
||||||
.map((item) => (
|
|
||||||
<OrderItem
|
|
||||||
key={item.id}
|
|
||||||
item={item}
|
|
||||||
currencyCode={order.currency_code}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
: repeat(5).map((i) => <SkeletonLineItem key={i} />)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { cache } from 'react';
|
|
||||||
|
|
||||||
import { listCollections, listProducts, listRegions } from "@lib/data";
|
|
||||||
|
|
||||||
async function countryCodesLoader() {
|
|
||||||
const countryCodes = await listRegions().then((regions) =>
|
|
||||||
regions?.map((r) => r.countries?.map((c) => c.iso_2)).flat(),
|
|
||||||
);
|
|
||||||
return countryCodes ?? [];
|
|
||||||
}
|
|
||||||
export const loadCountryCodes = cache(countryCodesLoader);
|
|
||||||
|
|
||||||
async function collectionsLoader() {
|
|
||||||
const { collections } = await listCollections({
|
|
||||||
fields: 'id, handle',
|
|
||||||
});
|
|
||||||
return collections ?? [];
|
|
||||||
}
|
|
||||||
export const loadCollections = cache(collectionsLoader);
|
|
||||||
|
|
||||||
async function analysisPackagesLoader() {
|
|
||||||
const [countryCodes, collections] = await Promise.all([loadCountryCodes(), loadCollections()]);
|
|
||||||
const countryCode = countryCodes[0]!;
|
|
||||||
|
|
||||||
const collection = collections.find(({ handle }) => handle === 'analysis-packages');
|
|
||||||
if (!collection) {
|
|
||||||
return { analysisPackages: [], countryCode };
|
|
||||||
}
|
|
||||||
|
|
||||||
const { response } = await listProducts({
|
|
||||||
countryCode,
|
|
||||||
queryParams: { limit: 100, collection_id: collection?.id },
|
|
||||||
});
|
|
||||||
return { analysisPackages: response.products, countryCode };
|
|
||||||
}
|
|
||||||
export const loadAnalysisPackages = cache(analysisPackagesLoader);
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import SelectAnalysisPackages from '@/components/select-analysis-packages';
|
|||||||
import { MedReportLogo } from '../../components/med-report-logo';
|
import { MedReportLogo } from '../../components/med-report-logo';
|
||||||
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';
|
||||||
import { loadAnalysisPackages } from '../home/(user)/_lib/server/load-analysis-packages';
|
|
||||||
|
|
||||||
export const generateMetadata = async () => {
|
export const generateMetadata = async () => {
|
||||||
const { t } = await createI18nServerInstance();
|
const { t } = await createI18nServerInstance();
|
||||||
@@ -24,8 +23,6 @@ export const generateMetadata = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function SelectPackagePage() {
|
async function SelectPackagePage() {
|
||||||
const { analysisPackages, countryCode } = await loadAnalysisPackages();
|
|
||||||
|
|
||||||
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 />
|
||||||
@@ -34,7 +31,6 @@ async function SelectPackagePage() {
|
|||||||
<Trans i18nKey={'marketing:selectPackage'} />
|
<Trans i18nKey={'marketing:selectPackage'} />
|
||||||
</h3>
|
</h3>
|
||||||
<ComparePackagesModal
|
<ComparePackagesModal
|
||||||
analysisPackages={analysisPackages}
|
|
||||||
triggerElement={
|
triggerElement={
|
||||||
<Button variant="secondary" className="gap-2">
|
<Button variant="secondary" className="gap-2">
|
||||||
<Trans i18nKey={'marketing:comparePackages'} />
|
<Trans i18nKey={'marketing:comparePackages'} />
|
||||||
@@ -43,7 +39,7 @@ async function SelectPackagePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SelectAnalysisPackages analysisPackages={analysisPackages} countryCode={countryCode} />
|
<SelectAnalysisPackages />
|
||||||
<Link href={pathsConfig.app.home}>
|
<Link href={pathsConfig.app.home}>
|
||||||
<Button variant="secondary" className="align-center">
|
<Button variant="secondary" className="align-center">
|
||||||
<Trans i18nKey={'marketing:notInterestedInAudit'} />{' '}
|
<Trans i18nKey={'marketing:notInterestedInAudit'} />{' '}
|
||||||
|
|||||||
@@ -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 />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import Image from 'next/image';
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardFooter,
|
|
||||||
CardHeader,
|
|
||||||
} from '@kit/ui/card';
|
|
||||||
import { Trans } from '@kit/ui/trans';
|
|
||||||
import { StoreProduct, StoreProductVariant } from '@medusajs/types';
|
|
||||||
import { Button } from '@medusajs/ui';
|
|
||||||
import { handleAddToCart } from '@/lib/services/medusaCart.service';
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SelectAnalysisPackage({
|
|
||||||
analysisPackage,
|
|
||||||
countryCode,
|
|
||||||
}: {
|
|
||||||
analysisPackage: StoreProduct
|
|
||||||
countryCode: string,
|
|
||||||
}) {
|
|
||||||
const router = useRouter();
|
|
||||||
const { t, i18n: { language } } = useTranslation();
|
|
||||||
|
|
||||||
const [isAddingToCart, setIsAddingToCart] = useState(false);
|
|
||||||
const handleSelect = async (selectedVariant: StoreProductVariant) => {
|
|
||||||
if (!selectedVariant?.id) return null
|
|
||||||
|
|
||||||
setIsAddingToCart(true);
|
|
||||||
await handleAddToCart({
|
|
||||||
selectedVariant,
|
|
||||||
countryCode,
|
|
||||||
});
|
|
||||||
setIsAddingToCart(false);
|
|
||||||
router.push('/home/cart');
|
|
||||||
}
|
|
||||||
|
|
||||||
const titleKey = analysisPackage.title;
|
|
||||||
const nrOfAnalyses = analysisPackage?.metadata?.nrOfAnalyses ?? 0;
|
|
||||||
const description = analysisPackage.description ?? '';
|
|
||||||
const subtitle = analysisPackage.subtitle ?? '';
|
|
||||||
const variant = analysisPackage.variants?.[0];
|
|
||||||
|
|
||||||
if (!variant) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const price = variant.calculated_price?.calculated_amount ?? 0;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card key={titleKey}>
|
|
||||||
<CardHeader className="relative">
|
|
||||||
{description && (
|
|
||||||
<ButtonTooltip
|
|
||||||
content={description}
|
|
||||||
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='bg-cyan'
|
|
||||||
analysesNr={t('product:nrOfAnalyses', { nr: nrOfAnalyses })}
|
|
||||||
language={language}
|
|
||||||
price={price}
|
|
||||||
/>
|
|
||||||
<CardDescription>
|
|
||||||
{subtitle}
|
|
||||||
</CardDescription>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter>
|
|
||||||
<Button className="w-full" onClick={() => handleSelect(variant)} isLoading={isAddingToCart}>
|
|
||||||
{!isAddingToCart && <Trans i18nKey='order-analysis-package:selectThisPackage' />}
|
|
||||||
</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,104 @@
|
|||||||
|
'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 { Trans } from '@kit/ui/trans';
|
||||||
import { StoreProduct } from '@medusajs/types';
|
|
||||||
|
|
||||||
import SelectAnalysisPackage from './select-analysis-package';
|
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();
|
||||||
|
|
||||||
export default function SelectAnalysisPackages({ analysisPackages, countryCode }: { analysisPackages: StoreProduct[], countryCode: string }) {
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-3 gap-6">
|
<div className="grid grid-cols-3 gap-6">
|
||||||
{analysisPackages.length > 0 ? analysisPackages.map(
|
{analysisPackages.length > 0 ? analysisPackages.map(
|
||||||
(product) => (
|
(
|
||||||
<SelectAnalysisPackage key={product.title} analysisPackage={product} countryCode={countryCode} />
|
{ 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>
|
<h4>
|
||||||
<Trans i18nKey='order-analysis-package:noPackagesAvailable' />
|
<Trans i18nKey='order-analysis-package:noPackagesAvailable' />
|
||||||
</h4>
|
</h4>
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ export async function register() {
|
|||||||
* @param err
|
* @param err
|
||||||
*/
|
*/
|
||||||
export const onRequestError: Instrumentation.onRequestError = async (err) => {
|
export const onRequestError: Instrumentation.onRequestError = async (err) => {
|
||||||
// const { getServerMonitoringService } = await import('@kit/monitoring/server');
|
const { getServerMonitoringService } = await import('@kit/monitoring/server');
|
||||||
|
|
||||||
// const service = await getServerMonitoringService();
|
const service = await getServerMonitoringService();
|
||||||
|
|
||||||
// await service.ready();
|
await service.ready();
|
||||||
// await service.captureException(err as Error);
|
await service.captureException(err as Error);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ export const defaultI18nNamespaces = [
|
|||||||
'product',
|
'product',
|
||||||
'booking',
|
'booking',
|
||||||
'order-analysis-package',
|
'order-analysis-package',
|
||||||
'cart',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,127 +0,0 @@
|
|||||||
'use server';
|
|
||||||
|
|
||||||
import { loadCurrentUserAccount } from '@/app/home/(user)/_lib/server/load-user-account';
|
|
||||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
|
||||||
import { addToCart, deleteLineItem, retrieveCart } from '@lib/data/cart';
|
|
||||||
import { StoreCartLineItem, StoreProductVariant } from '@medusajs/types';
|
|
||||||
import { MontonioOrderHandlerService } from '@/packages/billing/montonio/src';
|
|
||||||
import { headers } from 'next/headers';
|
|
||||||
import { requireUserInServerComponent } from '../server/require-user-in-server-component';
|
|
||||||
|
|
||||||
export async function handleAddToCart({
|
|
||||||
selectedVariant,
|
|
||||||
countryCode,
|
|
||||||
}: {
|
|
||||||
selectedVariant: StoreProductVariant
|
|
||||||
countryCode: string
|
|
||||||
}) {
|
|
||||||
const supabase = getSupabaseServerClient();
|
|
||||||
const user = await requireUserInServerComponent();
|
|
||||||
const account = await loadCurrentUserAccount()
|
|
||||||
if (!account) {
|
|
||||||
throw new Error('Account not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
const quantity = 1;
|
|
||||||
const cart = await addToCart({
|
|
||||||
variantId: selectedVariant.id,
|
|
||||||
quantity,
|
|
||||||
countryCode,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { error } = await supabase
|
|
||||||
.schema('audit')
|
|
||||||
.from('cart_entries')
|
|
||||||
.insert({
|
|
||||||
variant_id: selectedVariant.id,
|
|
||||||
operation: 'ADD_TO_CART',
|
|
||||||
account_id: account.id,
|
|
||||||
cart_id: cart.id,
|
|
||||||
changed_by: user.id,
|
|
||||||
});
|
|
||||||
if (error) {
|
|
||||||
throw new Error('Error logging cart entry: ' + error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cart;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function handleNavigateToPayment({ language }: { language: string }) {
|
|
||||||
const supabase = getSupabaseServerClient();
|
|
||||||
const user = await requireUserInServerComponent();
|
|
||||||
const account = await loadCurrentUserAccount()
|
|
||||||
if (!account) {
|
|
||||||
throw new Error('Account not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
const cart = await retrieveCart();
|
|
||||||
if (!cart) {
|
|
||||||
throw new Error("No cart found");
|
|
||||||
}
|
|
||||||
|
|
||||||
const headersList = await headers();
|
|
||||||
const host = "webhook.site:3000";
|
|
||||||
const proto = "http";
|
|
||||||
// const host = headersList.get('host');
|
|
||||||
// const proto = headersList.get('x-forwarded-proto') ?? 'http';
|
|
||||||
const publicUrl = `${proto}://${host}`;
|
|
||||||
|
|
||||||
const paymentLink = await new MontonioOrderHandlerService().getMontonioPaymentLink({
|
|
||||||
notificationUrl: `${publicUrl}/api/billing/webhook`,
|
|
||||||
returnUrl: `${publicUrl}/home/cart/montonio-callback`,
|
|
||||||
amount: cart.total,
|
|
||||||
currency: cart.currency_code.toUpperCase(),
|
|
||||||
description: `Order from Medreport`,
|
|
||||||
locale: language,
|
|
||||||
merchantReference: `${account.id}:${cart.id}:${Date.now()}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { error } = await supabase
|
|
||||||
.schema('audit')
|
|
||||||
.from('cart_entries')
|
|
||||||
.insert({
|
|
||||||
operation: 'NAVIGATE_TO_PAYMENT',
|
|
||||||
account_id: account.id,
|
|
||||||
cart_id: cart.id,
|
|
||||||
changed_by: user.id,
|
|
||||||
});
|
|
||||||
if (error) {
|
|
||||||
throw new Error('Error logging cart entry: ' + error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return paymentLink;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function handleLineItemTimeout({
|
|
||||||
lineItem,
|
|
||||||
}: {
|
|
||||||
lineItem: StoreCartLineItem
|
|
||||||
}) {
|
|
||||||
const supabase = getSupabaseServerClient();
|
|
||||||
const user = await requireUserInServerComponent();
|
|
||||||
const account = await loadCurrentUserAccount()
|
|
||||||
if (!account) {
|
|
||||||
throw new Error('Account not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lineItem.updated_at) {
|
|
||||||
const updatedAt = new Date(lineItem.updated_at);
|
|
||||||
const now = new Date();
|
|
||||||
const diff = now.getTime() - updatedAt.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
await deleteLineItem(lineItem.id);
|
|
||||||
|
|
||||||
const { error } = await supabase
|
|
||||||
.schema('audit')
|
|
||||||
.from('cart_entries')
|
|
||||||
.insert({
|
|
||||||
operation: 'LINE_ITEM_TIMEOUT',
|
|
||||||
account_id: account.id,
|
|
||||||
cart_id: lineItem.cart_id,
|
|
||||||
changed_by: user.id,
|
|
||||||
});
|
|
||||||
if (error) {
|
|
||||||
throw new Error('Error logging cart entry: ' + error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -68,7 +68,6 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"fast-xml-parser": "^5.2.5",
|
"fast-xml-parser": "^5.2.5",
|
||||||
"jsonwebtoken": "9.0.2",
|
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.510.0",
|
"lucide-react": "^0.510.0",
|
||||||
"next": "15.3.2",
|
"next": "15.3.2",
|
||||||
@@ -93,7 +92,6 @@
|
|||||||
"@medusajs/ui-preset": "latest",
|
"@medusajs/ui-preset": "latest",
|
||||||
"@next/bundle-analyzer": "15.3.2",
|
"@next/bundle-analyzer": "15.3.2",
|
||||||
"@tailwindcss/postcss": "^4.1.10",
|
"@tailwindcss/postcss": "^4.1.10",
|
||||||
"@types/jsonwebtoken": "9.0.10",
|
|
||||||
"@types/lodash": "^4.17.17",
|
"@types/lodash": "^4.17.17",
|
||||||
"@types/node": "^22.15.32",
|
"@types/node": "^22.15.32",
|
||||||
"@types/react": "19.1.4",
|
"@types/react": "19.1.4",
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export const BillingProviderSchema = z.enum([
|
|||||||
'stripe',
|
'stripe',
|
||||||
'paddle',
|
'paddle',
|
||||||
'lemon-squeezy',
|
'lemon-squeezy',
|
||||||
'montonio',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const PaymentTypeSchema = z.enum(['one-time', 'recurring']);
|
export const PaymentTypeSchema = z.enum(['one-time', 'recurring']);
|
||||||
|
|||||||
@@ -1,37 +1,5 @@
|
|||||||
import { UpsertOrderParams, UpsertSubscriptionParams } from '../types';
|
import { UpsertOrderParams, UpsertSubscriptionParams } from '../types';
|
||||||
|
|
||||||
export interface IHandleWebhookEventParams {
|
|
||||||
// this method is called when a checkout session is completed
|
|
||||||
onCheckoutSessionCompleted: (
|
|
||||||
subscription: UpsertSubscriptionParams | UpsertOrderParams,
|
|
||||||
) => Promise<unknown>;
|
|
||||||
|
|
||||||
// this method is called when a subscription is updated
|
|
||||||
onSubscriptionUpdated: (
|
|
||||||
subscription: UpsertSubscriptionParams,
|
|
||||||
) => Promise<unknown>;
|
|
||||||
|
|
||||||
// this method is called when a subscription is deleted
|
|
||||||
onSubscriptionDeleted: (subscriptionId: string) => Promise<unknown>;
|
|
||||||
|
|
||||||
// this method is called when a payment is succeeded. This is used for
|
|
||||||
// one-time payments
|
|
||||||
onPaymentSucceeded: (sessionId: string) => Promise<unknown>;
|
|
||||||
|
|
||||||
// this method is called when a payment is failed. This is used for
|
|
||||||
// one-time payments
|
|
||||||
onPaymentFailed: (sessionId: string) => Promise<unknown>;
|
|
||||||
|
|
||||||
// this method is called when an invoice is paid. We don't have a specific use case for this
|
|
||||||
// but it's extremely common for credit-based systems
|
|
||||||
onInvoicePaid: (
|
|
||||||
subscription: UpsertSubscriptionParams,
|
|
||||||
) => Promise<unknown>;
|
|
||||||
|
|
||||||
// generic handler for any event
|
|
||||||
onEvent?: (data: unknown) => Promise<unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name BillingWebhookHandlerService
|
* @name BillingWebhookHandlerService
|
||||||
* @description Represents an abstract class for handling billing webhook events.
|
* @description Represents an abstract class for handling billing webhook events.
|
||||||
@@ -52,6 +20,36 @@ export abstract class BillingWebhookHandlerService {
|
|||||||
*/
|
*/
|
||||||
abstract handleWebhookEvent(
|
abstract handleWebhookEvent(
|
||||||
event: unknown,
|
event: unknown,
|
||||||
params: IHandleWebhookEventParams,
|
params: {
|
||||||
|
// this method is called when a checkout session is completed
|
||||||
|
onCheckoutSessionCompleted: (
|
||||||
|
subscription: UpsertSubscriptionParams | UpsertOrderParams,
|
||||||
|
) => Promise<unknown>;
|
||||||
|
|
||||||
|
// this method is called when a subscription is updated
|
||||||
|
onSubscriptionUpdated: (
|
||||||
|
subscription: UpsertSubscriptionParams,
|
||||||
|
) => Promise<unknown>;
|
||||||
|
|
||||||
|
// this method is called when a subscription is deleted
|
||||||
|
onSubscriptionDeleted: (subscriptionId: string) => Promise<unknown>;
|
||||||
|
|
||||||
|
// this method is called when a payment is succeeded. This is used for
|
||||||
|
// one-time payments
|
||||||
|
onPaymentSucceeded: (sessionId: string) => Promise<unknown>;
|
||||||
|
|
||||||
|
// this method is called when a payment is failed. This is used for
|
||||||
|
// one-time payments
|
||||||
|
onPaymentFailed: (sessionId: string) => Promise<unknown>;
|
||||||
|
|
||||||
|
// this method is called when an invoice is paid. We don't have a specific use case for this
|
||||||
|
// but it's extremely common for credit-based systems
|
||||||
|
onInvoicePaid: (
|
||||||
|
subscription: UpsertSubscriptionParams,
|
||||||
|
) => Promise<unknown>;
|
||||||
|
|
||||||
|
// generic handler for any event
|
||||||
|
onEvent?: (data: unknown) => Promise<unknown>;
|
||||||
|
},
|
||||||
): Promise<unknown>;
|
): Promise<unknown>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/shared": "workspace:*",
|
"@kit/shared": "workspace:*",
|
||||||
"@kit/stripe": "workspace:*",
|
"@kit/stripe": "workspace:*",
|
||||||
"@kit/montonio": "workspace:*",
|
|
||||||
"@kit/supabase": "workspace:*",
|
"@kit/supabase": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@kit/ui": "workspace:*",
|
"@kit/ui": "workspace:*",
|
||||||
|
|||||||
@@ -30,13 +30,6 @@ export function createBillingEventHandlerFactoryService(
|
|||||||
return new StripeWebhookHandlerService(planTypesMap);
|
return new StripeWebhookHandlerService(planTypesMap);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register the Montonio webhook handler
|
|
||||||
billingWebhookHandlerRegistry.register('montonio', async () => {
|
|
||||||
const { MontonioWebhookHandlerService } = await import('@kit/montonio');
|
|
||||||
|
|
||||||
return new MontonioWebhookHandlerService();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Register the Lemon Squeezy webhook handler
|
// Register the Lemon Squeezy webhook handler
|
||||||
billingWebhookHandlerRegistry.register('lemon-squeezy', async () => {
|
billingWebhookHandlerRegistry.register('lemon-squeezy', async () => {
|
||||||
const { LemonSqueezyWebhookHandlerService } = await import(
|
const { LemonSqueezyWebhookHandlerService } = await import(
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
# Billing / Montonio - @kit/montonio
|
|
||||||
|
|
||||||
This package is responsible for handling all billing related operations using Montonio.
|
|
||||||
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import eslintConfigBase from '@kit/eslint-config/base.js';
|
|
||||||
|
|
||||||
export default eslintConfigBase;
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@kit/montonio",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.1.0",
|
|
||||||
"scripts": {
|
|
||||||
"clean": "git clean -xdf .turbo node_modules",
|
|
||||||
"format": "prettier --check \"**/*.{ts,tsx}\"",
|
|
||||||
"lint": "eslint .",
|
|
||||||
"typecheck": "tsc --noEmit"
|
|
||||||
},
|
|
||||||
"prettier": "@kit/prettier-config",
|
|
||||||
"exports": {
|
|
||||||
".": "./src/index.ts"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@kit/billing": "workspace:*",
|
|
||||||
"@kit/eslint-config": "workspace:*",
|
|
||||||
"@kit/prettier-config": "workspace:*",
|
|
||||||
"@kit/shared": "workspace:*",
|
|
||||||
"@kit/supabase": "workspace:*",
|
|
||||||
"@kit/tsconfig": "workspace:*",
|
|
||||||
"@kit/ui": "workspace:*",
|
|
||||||
"@types/react": "19.1.4",
|
|
||||||
"date-fns": "^4.1.0",
|
|
||||||
"next": "15.3.2",
|
|
||||||
"react": "19.1.0"
|
|
||||||
},
|
|
||||||
"typesVersions": {
|
|
||||||
"*": {
|
|
||||||
"*": [
|
|
||||||
"src/*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export { MontonioWebhookHandlerService } from './services/montonio-webhook-handler.service';
|
|
||||||
export { MontonioOrderHandlerService } from './services/montonio-order-handler.service';
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
export const MontonioClientEnvSchema = z
|
|
||||||
.object({
|
|
||||||
accessKey: z.string().min(1),
|
|
||||||
});
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
export const MontonioServerEnvSchema = z
|
|
||||||
.object({
|
|
||||||
secretKey: z
|
|
||||||
.string({
|
|
||||||
required_error: `Please provide the variable MONTONIO_SECRET_KEY`,
|
|
||||||
})
|
|
||||||
.min(1),
|
|
||||||
apiUrl: z
|
|
||||||
.string({
|
|
||||||
required_error: `Please provide the variable MONTONIO_API_URL`,
|
|
||||||
})
|
|
||||||
.min(1),
|
|
||||||
});
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import axios, { AxiosError } from 'axios';
|
|
||||||
import { MontonioClientEnvSchema } from '../schema/montonio-client-env.schema';
|
|
||||||
import { MontonioServerEnvSchema } from '../schema/montonio-server-env.schema';
|
|
||||||
|
|
||||||
const { accessKey } = MontonioClientEnvSchema.parse({
|
|
||||||
accessKey: process.env.NEXT_PUBLIC_MONTONIO_ACCESS_KEY,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { apiUrl, secretKey } = MontonioServerEnvSchema.parse({
|
|
||||||
apiUrl: process.env.MONTONIO_API_URL,
|
|
||||||
secretKey: process.env.MONTONIO_SECRET_KEY,
|
|
||||||
});
|
|
||||||
|
|
||||||
export class MontonioOrderHandlerService {
|
|
||||||
public async getMontonioPaymentLink({
|
|
||||||
notificationUrl,
|
|
||||||
returnUrl,
|
|
||||||
amount,
|
|
||||||
currency,
|
|
||||||
description,
|
|
||||||
locale,
|
|
||||||
merchantReference,
|
|
||||||
}: {
|
|
||||||
notificationUrl: string;
|
|
||||||
returnUrl: string;
|
|
||||||
amount: number;
|
|
||||||
currency: string;
|
|
||||||
description: string;
|
|
||||||
locale: string;
|
|
||||||
merchantReference: string;
|
|
||||||
}) {
|
|
||||||
const token = jwt.sign({
|
|
||||||
accessKey,
|
|
||||||
description,
|
|
||||||
currency,
|
|
||||||
amount,
|
|
||||||
locale,
|
|
||||||
// 15 minutes
|
|
||||||
expiresAt: new Date(Date.now() + 15 * 60 * 1000).toISOString(),
|
|
||||||
notificationUrl,
|
|
||||||
returnUrl,
|
|
||||||
askAdditionalInfo: false,
|
|
||||||
merchantReference,
|
|
||||||
type: "one_time",
|
|
||||||
}, secretKey, {
|
|
||||||
algorithm: "HS256",
|
|
||||||
expiresIn: "10m",
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { data } = await axios.post(`${apiUrl}/api/payment-links`, { data: token });
|
|
||||||
return data.url;
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof AxiosError) {
|
|
||||||
console.error(error.response?.data);
|
|
||||||
}
|
|
||||||
console.error(error);
|
|
||||||
throw new Error("Failed to create payment link");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
import type { BillingWebhookHandlerService, IHandleWebhookEventParams } from '@kit/billing';
|
|
||||||
import { getLogger } from '@kit/shared/logger';
|
|
||||||
import { Database, Enums } from '@kit/supabase/database';
|
|
||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import { MontonioServerEnvSchema } from '../schema/montonio-server-env.schema';
|
|
||||||
|
|
||||||
type UpsertOrderParams =
|
|
||||||
Database['medreport']['Functions']['upsert_order']['Args'];
|
|
||||||
|
|
||||||
type BillingProvider = Enums<{ schema: 'medreport' }, 'billing_provider'>;
|
|
||||||
|
|
||||||
interface MontonioOrderToken {
|
|
||||||
uuid: string;
|
|
||||||
accessKey: string;
|
|
||||||
merchantReference: string;
|
|
||||||
merchantReferenceDisplay: string;
|
|
||||||
paymentStatus: 'PAID' | 'FAILED' | 'CANCELLED' | 'PENDING' | 'EXPIRED' | 'REFUNDED';
|
|
||||||
paymentMethod: string;
|
|
||||||
grandTotal: number;
|
|
||||||
currency: string;
|
|
||||||
senderIban?: string;
|
|
||||||
senderName?: string;
|
|
||||||
paymentProviderName?: string;
|
|
||||||
paymentLinkUuid: string;
|
|
||||||
iat: number;
|
|
||||||
exp: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { secretKey } = MontonioServerEnvSchema.parse({
|
|
||||||
apiUrl: process.env.MONTONIO_API_URL,
|
|
||||||
secretKey: process.env.MONTONIO_SECRET_KEY,
|
|
||||||
});
|
|
||||||
|
|
||||||
export class MontonioWebhookHandlerService
|
|
||||||
implements BillingWebhookHandlerService
|
|
||||||
{
|
|
||||||
private readonly provider: BillingProvider = 'montonio';
|
|
||||||
private readonly namespace = 'billing.montonio';
|
|
||||||
|
|
||||||
async verifyWebhookSignature(request: Request) {
|
|
||||||
const logger = await getLogger();
|
|
||||||
|
|
||||||
let token: string;
|
|
||||||
try {
|
|
||||||
const url = new URL(request.url);
|
|
||||||
const searchParams = url.searchParams;
|
|
||||||
console.info("searchParams", searchParams, url);
|
|
||||||
const tokenParam = searchParams.get('order-token') as string | null;
|
|
||||||
if (!tokenParam) {
|
|
||||||
throw new Error('Missing order-token');
|
|
||||||
}
|
|
||||||
token = tokenParam;
|
|
||||||
} catch (error) {
|
|
||||||
logger.error({
|
|
||||||
error,
|
|
||||||
name: this.namespace,
|
|
||||||
}, `Failed to parse Montonio webhook request`);
|
|
||||||
throw new Error('Invalid request');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const decoded = jwt.verify(token, secretKey, {
|
|
||||||
algorithms: ['HS256'],
|
|
||||||
});
|
|
||||||
return decoded as MontonioOrderToken;
|
|
||||||
} catch (error) {
|
|
||||||
logger.error({
|
|
||||||
error,
|
|
||||||
name: this.namespace,
|
|
||||||
}, `Failed to verify Montonio webhook signature`);
|
|
||||||
throw new Error('Invalid signature');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleWebhookEvent(
|
|
||||||
event: MontonioOrderToken,
|
|
||||||
params: IHandleWebhookEventParams
|
|
||||||
) {
|
|
||||||
const logger = await getLogger();
|
|
||||||
|
|
||||||
logger.info({
|
|
||||||
name: this.namespace,
|
|
||||||
event,
|
|
||||||
}, `Received Montonio webhook event`);
|
|
||||||
|
|
||||||
if (event.paymentStatus === 'PAID') {
|
|
||||||
const [accountId] = event.merchantReferenceDisplay.split(':');
|
|
||||||
if (!accountId) {
|
|
||||||
throw new Error('Invalid merchant reference');
|
|
||||||
}
|
|
||||||
const order: UpsertOrderParams = {
|
|
||||||
target_account_id: accountId,
|
|
||||||
target_customer_id: '',
|
|
||||||
target_order_id: event.uuid,
|
|
||||||
status: 'succeeded',
|
|
||||||
billing_provider: this.provider,
|
|
||||||
total_amount: event.grandTotal,
|
|
||||||
currency: event.currency,
|
|
||||||
line_items: [],
|
|
||||||
};
|
|
||||||
return params.onCheckoutSessionCompleted(order);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.paymentStatus === 'FAILED' || event.paymentStatus === 'CANCELLED') {
|
|
||||||
return params.onPaymentFailed(event.uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@kit/tsconfig/base.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
|
||||||
},
|
|
||||||
"include": ["*.ts", "src"],
|
|
||||||
"exclude": ["node_modules"]
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/shared": "workspace:*",
|
"@kit/shared": "workspace:*",
|
||||||
"@kit/stripe": "workspace:*",
|
"@kit/stripe": "workspace:*",
|
||||||
"@kit/montonio": "workspace:*",
|
|
||||||
"@kit/supabase": "workspace:*",
|
"@kit/supabase": "workspace:*",
|
||||||
"@kit/team-accounts": "workspace:*",
|
"@kit/team-accounts": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
|
|||||||
@@ -31,16 +31,15 @@ export function UpdateAccountForm({ user }: { user: User }) {
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
firstName: '',
|
firstName: '',
|
||||||
lastName: '',
|
lastName: '',
|
||||||
personalCode: user.user_metadata.personalCode ?? '',
|
personalCode: '',
|
||||||
email: user.email,
|
email: user.email,
|
||||||
phone: '',
|
phone: '',
|
||||||
city: '',
|
city: '',
|
||||||
weight: user.user_metadata.weight ?? undefined,
|
weight: 0,
|
||||||
height: user.user_metadata.height ?? undefined,
|
height: 0,
|
||||||
userConsent: false,
|
userConsent: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { enhanceAction } from '@kit/next/actions';
|
|||||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||||
|
|
||||||
import pathsConfig from '~/config/paths.config';
|
import pathsConfig from '~/config/paths.config';
|
||||||
import { updateCustomer } from '@lib/data/customer';
|
|
||||||
|
|
||||||
import { UpdateAccountSchema } from '../../schemas/update-account.schema';
|
import { UpdateAccountSchema } from '../../schemas/update-account.schema';
|
||||||
import { createAuthApi } from '../api';
|
import { createAuthApi } from '../api';
|
||||||
@@ -37,13 +36,6 @@ export const onUpdateAccount = enhanceAction(
|
|||||||
}
|
}
|
||||||
console.warn('On update account error: ', err);
|
console.warn('On update account error: ', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateCustomer({
|
|
||||||
first_name: params.firstName,
|
|
||||||
last_name: params.lastName,
|
|
||||||
phone: params.phone,
|
|
||||||
});
|
|
||||||
|
|
||||||
const hasUnseenMembershipConfirmation =
|
const hasUnseenMembershipConfirmation =
|
||||||
await api.hasUnseenMembershipConfirmation();
|
await api.hasUnseenMembershipConfirmation();
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export async function retrieveCart(cartId?: string) {
|
|||||||
},
|
},
|
||||||
headers,
|
headers,
|
||||||
next,
|
next,
|
||||||
//cache: "force-cache",
|
cache: "force-cache",
|
||||||
})
|
})
|
||||||
.then(({ cart }) => cart)
|
.then(({ cart }) => cart)
|
||||||
.catch(() => null);
|
.catch(() => null);
|
||||||
@@ -154,8 +154,6 @@ export async function addToCart({
|
|||||||
revalidateTag(fulfillmentCacheTag);
|
revalidateTag(fulfillmentCacheTag);
|
||||||
})
|
})
|
||||||
.catch(medusaError);
|
.catch(medusaError);
|
||||||
|
|
||||||
return cart;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateLineItem({
|
export async function updateLineItem({
|
||||||
@@ -413,14 +411,17 @@ export async function placeOrder(cartId?: string) {
|
|||||||
.catch(medusaError);
|
.catch(medusaError);
|
||||||
|
|
||||||
if (cartRes?.type === "order") {
|
if (cartRes?.type === "order") {
|
||||||
|
const countryCode =
|
||||||
|
cartRes.order.shipping_address?.country_code?.toLowerCase();
|
||||||
|
|
||||||
const orderCacheTag = await getCacheTag("orders");
|
const orderCacheTag = await getCacheTag("orders");
|
||||||
revalidateTag(orderCacheTag);
|
revalidateTag(orderCacheTag);
|
||||||
|
|
||||||
removeCartId();
|
removeCartId();
|
||||||
redirect(`/home/order/${cartRes?.order.id}/confirmed`);
|
redirect(`/${countryCode}/order/${cartRes?.order.id}/confirmed`);
|
||||||
} else {
|
|
||||||
throw new Error("Cart is not an order");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return cartRes.cart;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,23 +18,6 @@ export const getAuthHeaders = async (): Promise<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMedusaCustomerId = async (): Promise<
|
|
||||||
{ customerId: string | null }
|
|
||||||
> => {
|
|
||||||
try {
|
|
||||||
const cookies = await nextCookies()
|
|
||||||
const customerId = cookies.get("_medusa_customer_id")?.value
|
|
||||||
|
|
||||||
if (!customerId) {
|
|
||||||
return { customerId: null }
|
|
||||||
}
|
|
||||||
|
|
||||||
return { customerId }
|
|
||||||
} catch {
|
|
||||||
return { customerId: null}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getCacheTag = async (tag: string): Promise<string> => {
|
export const getCacheTag = async (tag: string): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
const cookies = await nextCookies()
|
const cookies = await nextCookies()
|
||||||
@@ -76,16 +59,6 @@ export const setAuthToken = async (token: string) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setMedusaCustomerId = async (customerId: string) => {
|
|
||||||
const cookies = await nextCookies()
|
|
||||||
cookies.set("_medusa_customer_id", customerId, {
|
|
||||||
maxAge: 60 * 60 * 24 * 7,
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: "strict",
|
|
||||||
secure: process.env.NODE_ENV === "production",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const removeAuthToken = async () => {
|
export const removeAuthToken = async () => {
|
||||||
const cookies = await nextCookies()
|
const cookies = await nextCookies()
|
||||||
cookies.set("_medusa_jwt", "", {
|
cookies.set("_medusa_jwt", "", {
|
||||||
|
|||||||
@@ -259,51 +259,3 @@ export const updateCustomerAddress = async (
|
|||||||
return { success: false, error: err.toString() }
|
return { success: false, error: err.toString() }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function medusaLoginOrRegister(credentials: {
|
|
||||||
email: string
|
|
||||||
password?: string
|
|
||||||
}) {
|
|
||||||
const { email, password } = credentials;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const token = await sdk.auth.login("customer", "emailpass", {
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
});
|
|
||||||
await setAuthToken(token as string);
|
|
||||||
await transferCart();
|
|
||||||
|
|
||||||
const customerCacheTag = await getCacheTag("customers");
|
|
||||||
revalidateTag(customerCacheTag);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to login customer, attempting to register", error);
|
|
||||||
try {
|
|
||||||
const registerToken = await sdk.auth.register("customer", "emailpass", {
|
|
||||||
email: email,
|
|
||||||
password: password,
|
|
||||||
})
|
|
||||||
|
|
||||||
await setAuthToken(registerToken as string);
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
...(await getAuthHeaders()),
|
|
||||||
};
|
|
||||||
|
|
||||||
await sdk.store.customer.create({ email }, {}, headers);
|
|
||||||
|
|
||||||
const loginToken = await sdk.auth.login("customer", "emailpass", {
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
});
|
|
||||||
|
|
||||||
await setAuthToken(loginToken as string);
|
|
||||||
|
|
||||||
const customerCacheTag = await getCacheTag("customers");
|
|
||||||
revalidateTag(customerCacheTag);
|
|
||||||
await transferCart();
|
|
||||||
} catch (registerError) {
|
|
||||||
throw medusaError(registerError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const listProducts = async ({
|
|||||||
regionId,
|
regionId,
|
||||||
}: {
|
}: {
|
||||||
pageParam?: number
|
pageParam?: number
|
||||||
queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductParams & { collection_id?: string }
|
queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductParams
|
||||||
countryCode?: string
|
countryCode?: string
|
||||||
regionId?: string
|
regionId?: string
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -3,34 +3,24 @@
|
|||||||
import { deleteLineItem } from "@lib/data/cart";
|
import { deleteLineItem } from "@lib/data/cart";
|
||||||
import { Spinner, Trash } from "@medusajs/icons";
|
import { Spinner, Trash } from "@medusajs/icons";
|
||||||
import { clx } from "@medusajs/ui";
|
import { clx } from "@medusajs/ui";
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
const DeleteButton = ({
|
const DeleteButton = ({
|
||||||
id,
|
id,
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
Icon,
|
|
||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
Icon?: React.ReactNode;
|
|
||||||
}) => {
|
}) => {
|
||||||
const [isDeleting, setIsDeleting] = useState(false);
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const handleDelete = async (id: string) => {
|
const handleDelete = async (id: string) => {
|
||||||
setIsDeleting(true);
|
setIsDeleting(true);
|
||||||
|
await deleteLineItem(id).catch((err) => {
|
||||||
try {
|
|
||||||
await deleteLineItem(id);
|
|
||||||
|
|
||||||
router.refresh();
|
|
||||||
} catch (err) {
|
|
||||||
// TODO: display a toast notification with the error
|
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -44,7 +34,7 @@ const DeleteButton = ({
|
|||||||
className="flex gap-x-1 text-ui-fg-subtle hover:text-ui-fg-base cursor-pointer"
|
className="flex gap-x-1 text-ui-fg-subtle hover:text-ui-fg-base cursor-pointer"
|
||||||
onClick={() => handleDelete(id)}
|
onClick={() => handleDelete(id)}
|
||||||
>
|
>
|
||||||
{isDeleting ? <Spinner className="animate-spin" /> : (Icon ?? <Trash />)}
|
{isDeleting ? <Spinner className="animate-spin" /> : <Trash />}
|
||||||
<span>{children}</span>
|
<span>{children}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ type ShippingDetailsProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ShippingDetails = ({ order }: ShippingDetailsProps) => {
|
const ShippingDetails = ({ order }: ShippingDetailsProps) => {
|
||||||
if (!order.shipping_methods || order.shipping_methods.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Heading level="h2" className="flex flex-row text-3xl-regular my-6">
|
<Heading level="h2" className="flex flex-row text-3xl-regular my-6">
|
||||||
@@ -62,7 +58,7 @@ const ShippingDetails = ({ order }: ShippingDetailsProps) => {
|
|||||||
<Text className="txt-medium text-ui-fg-subtle">
|
<Text className="txt-medium text-ui-fg-subtle">
|
||||||
{(order as any).shipping_methods[0]?.name} (
|
{(order as any).shipping_methods[0]?.name} (
|
||||||
{convertToLocale({
|
{convertToLocale({
|
||||||
amount: order.shipping_methods?.[0]?.total ?? 0,
|
amount: order.shipping_methods?.[0].total ?? 0,
|
||||||
currency_code: order.currency_code,
|
currency_code: order.currency_code,
|
||||||
})
|
})
|
||||||
.replace(/,/g, "")
|
.replace(/,/g, "")
|
||||||
|
|||||||
@@ -116,29 +116,7 @@ export type Database = {
|
|||||||
status?: string
|
status?: string
|
||||||
}
|
}
|
||||||
Relationships: []
|
Relationships: []
|
||||||
},
|
}
|
||||||
cart_entries: {
|
|
||||||
Row: {
|
|
||||||
id: number
|
|
||||||
account_id: string
|
|
||||||
cart_id: string
|
|
||||||
operation: string
|
|
||||||
variant_id: string
|
|
||||||
comment: string | null
|
|
||||||
created_at: string
|
|
||||||
changed_by: string | null
|
|
||||||
}
|
|
||||||
Insert: {
|
|
||||||
id: number
|
|
||||||
account_id: string
|
|
||||||
cart_id: string
|
|
||||||
operation: string
|
|
||||||
variant_id: string
|
|
||||||
comment: string | null
|
|
||||||
created_at: string
|
|
||||||
changed_by: string | null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
Views: {
|
Views: {
|
||||||
[_ in never]: never
|
[_ in never]: never
|
||||||
@@ -1758,7 +1736,7 @@ export type Database = {
|
|||||||
| "settings.manage"
|
| "settings.manage"
|
||||||
| "members.manage"
|
| "members.manage"
|
||||||
| "invites.manage"
|
| "invites.manage"
|
||||||
billing_provider: "stripe" | "lemon-squeezy" | "paddle" | "montonio"
|
billing_provider: "stripe" | "lemon-squeezy" | "paddle"
|
||||||
notification_channel: "in_app" | "email"
|
notification_channel: "in_app" | "email"
|
||||||
notification_type: "info" | "warning" | "error"
|
notification_type: "info" | "warning" | "error"
|
||||||
payment_status: "pending" | "succeeded" | "failed"
|
payment_status: "pending" | "succeeded" | "failed"
|
||||||
@@ -1960,7 +1938,7 @@ export const Constants = {
|
|||||||
"members.manage",
|
"members.manage",
|
||||||
"invites.manage",
|
"invites.manage",
|
||||||
],
|
],
|
||||||
billing_provider: ["stripe", "lemon-squeezy", "paddle", "montonio"],
|
billing_provider: ["stripe", "lemon-squeezy", "paddle"],
|
||||||
notification_channel: ["in_app", "email"],
|
notification_channel: ["in_app", "email"],
|
||||||
notification_type: ["info", "warning", "error"],
|
notification_type: ["info", "warning", "error"],
|
||||||
payment_status: ["pending", "succeeded", "failed"],
|
payment_status: ["pending", "succeeded", "failed"],
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type { SignInWithPasswordCredentials } from '@supabase/supabase-js';
|
|||||||
|
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { medusaLoginOrRegister } from '../../../features/medusa-storefront/src/lib/data/customer';
|
|
||||||
import { useSupabase } from './use-supabase';
|
import { useSupabase } from './use-supabase';
|
||||||
|
|
||||||
export function useSignInWithEmailPassword() {
|
export function useSignInWithEmailPassword() {
|
||||||
@@ -19,22 +18,11 @@ export function useSignInWithEmailPassword() {
|
|||||||
const user = response.data?.user;
|
const user = response.data?.user;
|
||||||
const identities = user?.identities ?? [];
|
const identities = user?.identities ?? [];
|
||||||
|
|
||||||
|
// if the user has no identities, it means that the email is taken
|
||||||
if (identities.length === 0) {
|
if (identities.length === 0) {
|
||||||
throw new Error('User already registered');
|
throw new Error('User already registered');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('email' in credentials) {
|
|
||||||
try {
|
|
||||||
await medusaLoginOrRegister({
|
|
||||||
email: credentials.email,
|
|
||||||
password: credentials.password,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
await client.auth.signOut();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { useSupabase } from './use-supabase';
|
import { useSupabase } from './use-supabase';
|
||||||
import { medusaLoginOrRegister } from '../../../features/medusa-storefront/src/lib/data/customer';
|
|
||||||
|
|
||||||
interface Credentials {
|
interface Credentials {
|
||||||
personalCode: string;
|
personalCode: string;
|
||||||
@@ -42,18 +41,6 @@ export function useSignUpWithEmailAndPassword() {
|
|||||||
throw new Error('User already registered');
|
throw new Error('User already registered');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('email' in credentials) {
|
|
||||||
try {
|
|
||||||
await medusaLoginOrRegister({
|
|
||||||
email: credentials.email,
|
|
||||||
password: credentials.password,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
await client.auth.signOut();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -171,24 +171,22 @@ export function PageHeader({
|
|||||||
<PageTitle>{title}</PageTitle>
|
<PageTitle>{title}</PageTitle>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
<If condition={displaySidebarTrigger || description}>
|
<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" />
|
) : null}
|
||||||
) : null}
|
|
||||||
|
|
||||||
<If condition={description}>
|
<If condition={description}>
|
||||||
<If condition={displaySidebarTrigger}>
|
<If condition={displaySidebarTrigger}>
|
||||||
<Separator
|
<Separator
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
className="hidden h-4 w-px lg:group-data-[minimized]:block"
|
className="hidden h-4 w-px lg:group-data-[minimized]:block"
|
||||||
/>
|
/>
|
||||||
</If>
|
|
||||||
|
|
||||||
<PageDescription>{description}</PageDescription>
|
|
||||||
</If>
|
</If>
|
||||||
</div>
|
|
||||||
</If>
|
<PageDescription>{description}</PageDescription>
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -674,6 +674,11 @@ const SidebarMenuSkeleton: React.FC<
|
|||||||
showIcon?: boolean;
|
showIcon?: boolean;
|
||||||
}
|
}
|
||||||
> = ({ className, showIcon = false, ...props }) => {
|
> = ({ className, showIcon = false, ...props }) => {
|
||||||
|
// Random width between 50 to 90%.
|
||||||
|
const width = React.useMemo(() => {
|
||||||
|
return `${Math.floor(Math.random() * 40) + 50}%`;
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-sidebar="menu-skeleton"
|
data-sidebar="menu-skeleton"
|
||||||
@@ -691,7 +696,7 @@ const SidebarMenuSkeleton: React.FC<
|
|||||||
data-sidebar="menu-skeleton-text"
|
data-sidebar="menu-skeleton-text"
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
'--skeleton-width': '70%',
|
'--skeleton-width': width,
|
||||||
} as React.CSSProperties
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
417
pnpm-lock.yaml
generated
417
pnpm-lock.yaml
generated
@@ -82,10 +82,10 @@ importers:
|
|||||||
version: 2.8.6(react@19.1.0)
|
version: 2.8.6(react@19.1.0)
|
||||||
'@medusajs/js-sdk':
|
'@medusajs/js-sdk':
|
||||||
specifier: latest
|
specifier: latest
|
||||||
version: 2.8.7(awilix@8.0.1)
|
version: 2.8.6(awilix@8.0.1)
|
||||||
'@medusajs/ui':
|
'@medusajs/ui':
|
||||||
specifier: latest
|
specifier: latest
|
||||||
version: 4.0.17(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
|
version: 4.0.16(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
|
||||||
'@nosecone/next':
|
'@nosecone/next':
|
||||||
specifier: 1.0.0-beta.7
|
specifier: 1.0.0-beta.7
|
||||||
version: 1.0.0-beta.7(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
|
version: 1.0.0-beta.7(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
|
||||||
@@ -119,9 +119,6 @@ importers:
|
|||||||
fast-xml-parser:
|
fast-xml-parser:
|
||||||
specifier: ^5.2.5
|
specifier: ^5.2.5
|
||||||
version: 5.2.5
|
version: 5.2.5
|
||||||
jsonwebtoken:
|
|
||||||
specifier: 9.0.2
|
|
||||||
version: 9.0.2
|
|
||||||
lodash:
|
lodash:
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
@@ -176,19 +173,16 @@ importers:
|
|||||||
version: link:tooling/typescript
|
version: link:tooling/typescript
|
||||||
'@medusajs/types':
|
'@medusajs/types':
|
||||||
specifier: latest
|
specifier: latest
|
||||||
version: 2.8.7(awilix@8.0.1)
|
version: 2.8.6(awilix@8.0.1)
|
||||||
'@medusajs/ui-preset':
|
'@medusajs/ui-preset':
|
||||||
specifier: latest
|
specifier: latest
|
||||||
version: 2.8.7(tailwindcss@4.1.7)
|
version: 2.8.6(tailwindcss@4.1.7)
|
||||||
'@next/bundle-analyzer':
|
'@next/bundle-analyzer':
|
||||||
specifier: 15.3.2
|
specifier: 15.3.2
|
||||||
version: 15.3.2
|
version: 15.3.2
|
||||||
'@tailwindcss/postcss':
|
'@tailwindcss/postcss':
|
||||||
specifier: ^4.1.10
|
specifier: ^4.1.10
|
||||||
version: 4.1.10
|
version: 4.1.10
|
||||||
'@types/jsonwebtoken':
|
|
||||||
specifier: 9.0.10
|
|
||||||
version: 9.0.10
|
|
||||||
'@types/lodash':
|
'@types/lodash':
|
||||||
specifier: ^4.17.17
|
specifier: ^4.17.17
|
||||||
version: 4.17.17
|
version: 4.17.17
|
||||||
@@ -279,9 +273,6 @@ importers:
|
|||||||
'@kit/lemon-squeezy':
|
'@kit/lemon-squeezy':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../lemon-squeezy
|
version: link:../lemon-squeezy
|
||||||
'@kit/montonio':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../montonio
|
|
||||||
'@kit/prettier-config':
|
'@kit/prettier-config':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../../tooling/prettier
|
version: link:../../../tooling/prettier
|
||||||
@@ -359,42 +350,6 @@ importers:
|
|||||||
specifier: 19.1.0
|
specifier: 19.1.0
|
||||||
version: 19.1.0
|
version: 19.1.0
|
||||||
|
|
||||||
packages/billing/montonio:
|
|
||||||
devDependencies:
|
|
||||||
'@kit/billing':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../core
|
|
||||||
'@kit/eslint-config':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../../tooling/eslint
|
|
||||||
'@kit/prettier-config':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../../tooling/prettier
|
|
||||||
'@kit/shared':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../shared
|
|
||||||
'@kit/supabase':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../supabase
|
|
||||||
'@kit/tsconfig':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../../tooling/typescript
|
|
||||||
'@kit/ui':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../ui
|
|
||||||
'@types/react':
|
|
||||||
specifier: 19.1.4
|
|
||||||
version: 19.1.4
|
|
||||||
date-fns:
|
|
||||||
specifier: ^4.1.0
|
|
||||||
version: 4.1.0
|
|
||||||
next:
|
|
||||||
specifier: 15.3.2
|
|
||||||
version: 15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
|
||||||
react:
|
|
||||||
specifier: 19.1.0
|
|
||||||
version: 19.1.0
|
|
||||||
|
|
||||||
packages/billing/stripe:
|
packages/billing/stripe:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@stripe/react-stripe-js':
|
'@stripe/react-stripe-js':
|
||||||
@@ -472,10 +427,10 @@ importers:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@keystatic/core':
|
'@keystatic/core':
|
||||||
specifier: 0.5.47
|
specifier: 0.5.47
|
||||||
version: 0.5.47(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 0.5.47(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
'@keystatic/next':
|
'@keystatic/next':
|
||||||
specifier: ^5.0.4
|
specifier: ^5.0.4
|
||||||
version: 5.0.4(@keystatic/core@0.5.47(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 5.0.4(@keystatic/core@0.5.47(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
'@markdoc/markdoc':
|
'@markdoc/markdoc':
|
||||||
specifier: ^0.5.1
|
specifier: ^0.5.1
|
||||||
version: 0.5.2(@types/react@19.1.4)(react@19.1.0)
|
version: 0.5.2(@types/react@19.1.4)(react@19.1.0)
|
||||||
@@ -555,9 +510,6 @@ importers:
|
|||||||
'@kit/eslint-config':
|
'@kit/eslint-config':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../tooling/eslint
|
version: link:../../tooling/eslint
|
||||||
'@kit/montonio':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../billing/montonio
|
|
||||||
'@kit/prettier-config':
|
'@kit/prettier-config':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../tooling/prettier
|
version: link:../../tooling/prettier
|
||||||
@@ -799,10 +751,10 @@ importers:
|
|||||||
version: 2.2.4(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)
|
version: 2.2.4(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)
|
||||||
'@medusajs/js-sdk':
|
'@medusajs/js-sdk':
|
||||||
specifier: latest
|
specifier: latest
|
||||||
version: 2.8.7(awilix@8.0.1)
|
version: 2.8.6(awilix@8.0.1)
|
||||||
'@medusajs/ui':
|
'@medusajs/ui':
|
||||||
specifier: latest
|
specifier: latest
|
||||||
version: 4.0.17(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)(typescript@5.8.3)
|
version: 4.0.16(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)(typescript@5.8.3)
|
||||||
'@radix-ui/react-accordion':
|
'@radix-ui/react-accordion':
|
||||||
specifier: ^1.2.1
|
specifier: ^1.2.1
|
||||||
version: 1.2.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)
|
version: 1.2.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)
|
||||||
@@ -848,10 +800,10 @@ importers:
|
|||||||
version: 7.27.4
|
version: 7.27.4
|
||||||
'@medusajs/types':
|
'@medusajs/types':
|
||||||
specifier: latest
|
specifier: latest
|
||||||
version: 2.8.7(awilix@8.0.1)
|
version: 2.8.6(awilix@8.0.1)
|
||||||
'@medusajs/ui-preset':
|
'@medusajs/ui-preset':
|
||||||
specifier: latest
|
specifier: latest
|
||||||
version: 2.8.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.8.3)))
|
version: 2.8.6(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.8.3)))
|
||||||
'@types/lodash':
|
'@types/lodash':
|
||||||
specifier: ^4.14.195
|
specifier: ^4.14.195
|
||||||
version: 4.17.17
|
version: 4.17.17
|
||||||
@@ -1221,7 +1173,7 @@ importers:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@sentry/nextjs':
|
'@sentry/nextjs':
|
||||||
specifier: ^9.19.0
|
specifier: ^9.19.0
|
||||||
version: 9.27.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.99.9)
|
version: 9.27.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.99.9)
|
||||||
import-in-the-middle:
|
import-in-the-middle:
|
||||||
specifier: 1.13.2
|
specifier: 1.13.2
|
||||||
version: 1.13.2
|
version: 1.13.2
|
||||||
@@ -2179,17 +2131,12 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
|
||||||
'@medusajs/icons@2.8.7':
|
'@medusajs/js-sdk@2.8.6':
|
||||||
resolution: {integrity: sha512-zGkAokqWBNJ1PcTktCPSMT5spIIjv8Pba88BXvfcbblG5cUbMSvvJ2v/BRODMFejQ9NqlboIeP0fo/9RzLpPHg==}
|
resolution: {integrity: sha512-nrecLAeo+uHfL8u7WhUleysSRPjcgPLSgWbiRIq1oFeJU91M8xVlvNZgz+MSgwiDWCJsMYBr/8bgEu7+PxSy0w==}
|
||||||
peerDependencies:
|
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
|
||||||
|
|
||||||
'@medusajs/js-sdk@2.8.7':
|
|
||||||
resolution: {integrity: sha512-ZGYMQOM7GHuKtxOvJ+wgKyC/fzLlyMu5nij4hIWIf2osZy7d6dpvEglcV6w9B0UgSEADJh1SZ7a22HOJdjjJ9A==}
|
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
'@medusajs/types@2.8.7':
|
'@medusajs/types@2.8.6':
|
||||||
resolution: {integrity: sha512-8m/H9KkDUQz4YD+XkD/C63RfE/2elcdWf5G/KOK2QViTK0Jsd/Iw8Yy+T60pm0Lq/QQ925AfGH/Ji8UYNXjT8g==}
|
resolution: {integrity: sha512-SAkRVASQL+Rd+snaq2kEXJCfJBUjB6U4PBKvS+yHWdTVnvGUxkD725I4W+hWORjCK85po0lNdhv5Hx5YVe2BoQ==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
awilix: ^8.0.1
|
awilix: ^8.0.1
|
||||||
@@ -2201,13 +2148,13 @@ packages:
|
|||||||
vite:
|
vite:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@medusajs/ui-preset@2.8.7':
|
'@medusajs/ui-preset@2.8.6':
|
||||||
resolution: {integrity: sha512-ro8BrYlqHh7iZvYKrxmJtLweJYYet+wYQQv0R3pyfxkkP0aQ09KDPo8yTwls11iuMC4cQHljekdaOyXtSR6ZiQ==}
|
resolution: {integrity: sha512-t5LKe7rOZeP8D87d1nzc3wjDSzVypMzasM3CbUlQVZ78xgfnewjqkbVlFacSQy4X36sIKHV4eHW8/E6cxdGbng==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
tailwindcss: '>=3.0.0'
|
tailwindcss: '>=3.0.0'
|
||||||
|
|
||||||
'@medusajs/ui@4.0.17':
|
'@medusajs/ui@4.0.16':
|
||||||
resolution: {integrity: sha512-N5KtZXvns13jDiCE3ZgZLINQnlECYLf4Q4GFdbRhCjAFKFBRGyyeNKX+Zo2wBUZA2Oi4kockdxFfsZfBHh/ZhA==}
|
resolution: {integrity: sha512-eAziJhmVM4mQPbAE1qXFKF3oTk+j32XFjXU8LEjuw5TN8gXuWHEu+5/aNxycV8N01ooO41Bms5rcGzPuj8kcGw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
@@ -2224,9 +2171,6 @@ packages:
|
|||||||
'@next/env@15.3.2':
|
'@next/env@15.3.2':
|
||||||
resolution: {integrity: sha512-xURk++7P7qR9JG1jJtLzPzf0qEvqCN0A/T3DXf8IPMKo9/6FfjxtEffRJIIew/bIL4T3C2jLLqBor8B/zVlx6g==}
|
resolution: {integrity: sha512-xURk++7P7qR9JG1jJtLzPzf0qEvqCN0A/T3DXf8IPMKo9/6FfjxtEffRJIIew/bIL4T3C2jLLqBor8B/zVlx6g==}
|
||||||
|
|
||||||
'@next/env@15.4.0-canary.128':
|
|
||||||
resolution: {integrity: sha512-o1L2iI/6zHvYQo4hwwf7a3eu5lEOLBChl79Apreub/EwUFHAoRHfmaMYlVfk4uWo3XUNvMRxOG+dRAWmxn4mJQ==}
|
|
||||||
|
|
||||||
'@next/eslint-plugin-next@15.0.3':
|
'@next/eslint-plugin-next@15.0.3':
|
||||||
resolution: {integrity: sha512-3Ln/nHq2V+v8uIaxCR6YfYo7ceRgZNXfTd3yW1ukTaFbO+/I8jNakrjYWODvG9BuR2v5kgVtH/C8r0i11quOgw==}
|
resolution: {integrity: sha512-3Ln/nHq2V+v8uIaxCR6YfYo7ceRgZNXfTd3yW1ukTaFbO+/I8jNakrjYWODvG9BuR2v5kgVtH/C8r0i11quOgw==}
|
||||||
|
|
||||||
@@ -2239,96 +2183,48 @@ packages:
|
|||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@next/swc-darwin-arm64@15.4.0-canary.128':
|
|
||||||
resolution: {integrity: sha512-3W+dQHTO4baj4iMfWCcXDrrpYsQmuLE1rqLjx3UjMswrJx5DSOPre2haAH5W6CtLvTVEB8x/xCCzJ8ZlqfF5CA==}
|
|
||||||
engines: {node: '>= 10'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [darwin]
|
|
||||||
|
|
||||||
'@next/swc-darwin-x64@15.3.2':
|
'@next/swc-darwin-x64@15.3.2':
|
||||||
resolution: {integrity: sha512-ro/fdqaZWL6k1S/5CLv1I0DaZfDVJkWNaUU3un8Lg6m0YENWlDulmIWzV96Iou2wEYyEsZq51mwV8+XQXqMp3w==}
|
resolution: {integrity: sha512-ro/fdqaZWL6k1S/5CLv1I0DaZfDVJkWNaUU3un8Lg6m0YENWlDulmIWzV96Iou2wEYyEsZq51mwV8+XQXqMp3w==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@next/swc-darwin-x64@15.4.0-canary.128':
|
|
||||||
resolution: {integrity: sha512-BlXLOSoc9Xubx/ZRB1k76Akd7ildo98Ypn+IglZiapheAfA//euQZUHZXD66ZVyWHYqdCEw7vg4EzpnXA8wlpg==}
|
|
||||||
engines: {node: '>= 10'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [darwin]
|
|
||||||
|
|
||||||
'@next/swc-linux-arm64-gnu@15.3.2':
|
'@next/swc-linux-arm64-gnu@15.3.2':
|
||||||
resolution: {integrity: sha512-covwwtZYhlbRWK2HlYX9835qXum4xYZ3E2Mra1mdQ+0ICGoMiw1+nVAn4d9Bo7R3JqSmK1grMq/va+0cdh7bJA==}
|
resolution: {integrity: sha512-covwwtZYhlbRWK2HlYX9835qXum4xYZ3E2Mra1mdQ+0ICGoMiw1+nVAn4d9Bo7R3JqSmK1grMq/va+0cdh7bJA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-arm64-gnu@15.4.0-canary.128':
|
|
||||||
resolution: {integrity: sha512-ZUnD74X5yTa/De0s1x/SG6Rv+MGEx6nuz+Th3qmKOUCmwzlxE4UBF7+Vwti4Wg8aEEzxebJUR8WRrmWwvmui8g==}
|
|
||||||
engines: {node: '>= 10'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@15.3.2':
|
'@next/swc-linux-arm64-musl@15.3.2':
|
||||||
resolution: {integrity: sha512-KQkMEillvlW5Qk5mtGA/3Yz0/tzpNlSw6/3/ttsV1lNtMuOHcGii3zVeXZyi4EJmmLDKYcTcByV2wVsOhDt/zg==}
|
resolution: {integrity: sha512-KQkMEillvlW5Qk5mtGA/3Yz0/tzpNlSw6/3/ttsV1lNtMuOHcGii3zVeXZyi4EJmmLDKYcTcByV2wVsOhDt/zg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@15.4.0-canary.128':
|
|
||||||
resolution: {integrity: sha512-pMJ79xdufMeLuOyHY6F0LE/GgHRZZiLVch7YntB7dNZ8HZbV+br4UzssAgr3JfAGyuRnNY0dWgreKwGyGDNSfw==}
|
|
||||||
engines: {node: '>= 10'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@15.3.2':
|
'@next/swc-linux-x64-gnu@15.3.2':
|
||||||
resolution: {integrity: sha512-uRBo6THWei0chz+Y5j37qzx+BtoDRFIkDzZjlpCItBRXyMPIg079eIkOCl3aqr2tkxL4HFyJ4GHDes7W8HuAUg==}
|
resolution: {integrity: sha512-uRBo6THWei0chz+Y5j37qzx+BtoDRFIkDzZjlpCItBRXyMPIg079eIkOCl3aqr2tkxL4HFyJ4GHDes7W8HuAUg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@15.4.0-canary.128':
|
|
||||||
resolution: {integrity: sha512-33xXJqrbQO/qNO1n9tLbz6o38j7bs4VnOMmWVHPZH9IAHA99gHFThUZZ4TXgVa8Yj6lgIOLb7MuymaN55FZeZA==}
|
|
||||||
engines: {node: '>= 10'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@15.3.2':
|
'@next/swc-linux-x64-musl@15.3.2':
|
||||||
resolution: {integrity: sha512-+uxFlPuCNx/T9PdMClOqeE8USKzj8tVz37KflT3Kdbx/LOlZBRI2yxuIcmx1mPNK8DwSOMNCr4ureSet7eyC0w==}
|
resolution: {integrity: sha512-+uxFlPuCNx/T9PdMClOqeE8USKzj8tVz37KflT3Kdbx/LOlZBRI2yxuIcmx1mPNK8DwSOMNCr4ureSet7eyC0w==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@15.4.0-canary.128':
|
|
||||||
resolution: {integrity: sha512-WJv6LPLKJXqvHTaX6WgliI9enlgm4tpwDE6pL619zgGSI5HXHrDsr4LzlDCD7rmuG+n937umVFESmUrnwTTrGw==}
|
|
||||||
engines: {node: '>= 10'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@15.3.2':
|
'@next/swc-win32-arm64-msvc@15.3.2':
|
||||||
resolution: {integrity: sha512-LLTKmaI5cfD8dVzh5Vt7+OMo+AIOClEdIU/TSKbXXT2iScUTSxOGoBhfuv+FU8R9MLmrkIL1e2fBMkEEjYAtPQ==}
|
resolution: {integrity: sha512-LLTKmaI5cfD8dVzh5Vt7+OMo+AIOClEdIU/TSKbXXT2iScUTSxOGoBhfuv+FU8R9MLmrkIL1e2fBMkEEjYAtPQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@15.4.0-canary.128':
|
|
||||||
resolution: {integrity: sha512-97Wrx1M3MJSGbiNNbscb/W6avnRCp/9NX9vNTp58caYxk7U7S2e3HJoWS8yeHW9193ci5kHAIBfNDr/GvFws7A==}
|
|
||||||
engines: {node: '>= 10'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [win32]
|
|
||||||
|
|
||||||
'@next/swc-win32-x64-msvc@15.3.2':
|
'@next/swc-win32-x64-msvc@15.3.2':
|
||||||
resolution: {integrity: sha512-aW5B8wOPioJ4mBdMDXkt5f3j8pUr9W8AnlX0Df35uRWNT1Y6RIybxjnSUe+PhM+M1bwgyY8PHLmXZC6zT1o5tA==}
|
resolution: {integrity: sha512-aW5B8wOPioJ4mBdMDXkt5f3j8pUr9W8AnlX0Df35uRWNT1Y6RIybxjnSUe+PhM+M1bwgyY8PHLmXZC6zT1o5tA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@next/swc-win32-x64-msvc@15.4.0-canary.128':
|
|
||||||
resolution: {integrity: sha512-NbpGc9eZjQkjwDst5WsAlv+GFKayojuy7sz+01FJ6og8LXRJvzMCTh5uFTdaHIZr47RpZvewIlJW7mjWyvAHjg==}
|
|
||||||
engines: {node: '>= 10'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [win32]
|
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -5466,9 +5362,6 @@ packages:
|
|||||||
'@types/json5@0.0.29':
|
'@types/json5@0.0.29':
|
||||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||||
|
|
||||||
'@types/jsonwebtoken@9.0.10':
|
|
||||||
resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==}
|
|
||||||
|
|
||||||
'@types/linkify-it@3.0.5':
|
'@types/linkify-it@3.0.5':
|
||||||
resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==}
|
resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==}
|
||||||
|
|
||||||
@@ -6059,9 +5952,6 @@ packages:
|
|||||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
buffer-equal-constant-time@1.0.1:
|
|
||||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
|
||||||
|
|
||||||
buffer-from@1.1.2:
|
buffer-from@1.1.2:
|
||||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
|
||||||
@@ -6485,9 +6375,6 @@ packages:
|
|||||||
eastasianwidth@0.2.0:
|
eastasianwidth@0.2.0:
|
||||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||||
|
|
||||||
ecdsa-sig-formatter@1.0.11:
|
|
||||||
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
|
||||||
|
|
||||||
electron-to-chromium@1.5.168:
|
electron-to-chromium@1.5.168:
|
||||||
resolution: {integrity: sha512-RUNQmFLNIWVW6+z32EJQ5+qx8ci6RGvdtDC0Ls+F89wz6I2AthpXF0w0DIrn2jpLX0/PU9ZCo+Qp7bg/EckJmA==}
|
resolution: {integrity: sha512-RUNQmFLNIWVW6+z32EJQ5+qx8ci6RGvdtDC0Ls+F89wz6I2AthpXF0w0DIrn2jpLX0/PU9ZCo+Qp7bg/EckJmA==}
|
||||||
|
|
||||||
@@ -7345,20 +7232,10 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
jsonwebtoken@9.0.2:
|
|
||||||
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
|
|
||||||
engines: {node: '>=12', npm: '>=6'}
|
|
||||||
|
|
||||||
jsx-ast-utils@3.3.5:
|
jsx-ast-utils@3.3.5:
|
||||||
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
|
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
|
||||||
engines: {node: '>=4.0'}
|
engines: {node: '>=4.0'}
|
||||||
|
|
||||||
jwa@1.4.2:
|
|
||||||
resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==}
|
|
||||||
|
|
||||||
jws@3.2.2:
|
|
||||||
resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
|
|
||||||
|
|
||||||
keyv@4.5.4:
|
keyv@4.5.4:
|
||||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||||
|
|
||||||
@@ -7474,33 +7351,12 @@ packages:
|
|||||||
lodash.deburr@4.1.0:
|
lodash.deburr@4.1.0:
|
||||||
resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==}
|
resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==}
|
||||||
|
|
||||||
lodash.includes@4.3.0:
|
|
||||||
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
|
||||||
|
|
||||||
lodash.isboolean@3.0.3:
|
|
||||||
resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
|
|
||||||
|
|
||||||
lodash.isinteger@4.0.4:
|
|
||||||
resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}
|
|
||||||
|
|
||||||
lodash.isnumber@3.0.3:
|
|
||||||
resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}
|
|
||||||
|
|
||||||
lodash.isplainobject@4.0.6:
|
|
||||||
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
|
||||||
|
|
||||||
lodash.isstring@4.0.1:
|
|
||||||
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
|
|
||||||
|
|
||||||
lodash.memoize@4.1.2:
|
lodash.memoize@4.1.2:
|
||||||
resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
|
resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
|
||||||
|
|
||||||
lodash.merge@4.6.2:
|
lodash.merge@4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
lodash.once@4.1.1:
|
|
||||||
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
|
||||||
|
|
||||||
lodash.uniq@4.5.0:
|
lodash.uniq@4.5.0:
|
||||||
resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
|
resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
|
||||||
|
|
||||||
@@ -7822,27 +7678,6 @@ packages:
|
|||||||
sass:
|
sass:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
next@15.4.0-canary.128:
|
|
||||||
resolution: {integrity: sha512-Oo1GjM7ToTkus3mEMnKI93NpFt3KgtTnVDyINHrvX/rjdtEHiabNQhgowOqv84h8uLfatV+vsy7gMkYR+UsV/A==}
|
|
||||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
|
||||||
hasBin: true
|
|
||||||
peerDependencies:
|
|
||||||
'@opentelemetry/api': ^1.1.0
|
|
||||||
'@playwright/test': ^1.51.1
|
|
||||||
babel-plugin-react-compiler: '*'
|
|
||||||
react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
|
|
||||||
react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
|
|
||||||
sass: ^1.3.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@opentelemetry/api':
|
|
||||||
optional: true
|
|
||||||
'@playwright/test':
|
|
||||||
optional: true
|
|
||||||
babel-plugin-react-compiler:
|
|
||||||
optional: true
|
|
||||||
sass:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
no-case@3.0.4:
|
no-case@3.0.4:
|
||||||
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
|
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
|
||||||
|
|
||||||
@@ -10135,7 +9970,7 @@ snapshots:
|
|||||||
|
|
||||||
'@juggle/resize-observer@3.4.0': {}
|
'@juggle/resize-observer@3.4.0': {}
|
||||||
|
|
||||||
'@keystar/ui@0.7.19(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
'@keystar/ui@0.7.19(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.27.6
|
'@babel/runtime': 7.27.6
|
||||||
'@emotion/css': 11.13.5
|
'@emotion/css': 11.13.5
|
||||||
@@ -10228,18 +10063,18 @@ snapshots:
|
|||||||
react: 19.1.0
|
react: 19.1.0
|
||||||
react-dom: 19.1.0(react@19.1.0)
|
react-dom: 19.1.0(react@19.1.0)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
next: 15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
next: 15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@keystatic/core@0.5.47(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
'@keystatic/core@0.5.47(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.27.6
|
'@babel/runtime': 7.27.6
|
||||||
'@braintree/sanitize-url': 6.0.4
|
'@braintree/sanitize-url': 6.0.4
|
||||||
'@emotion/weak-memoize': 0.3.1
|
'@emotion/weak-memoize': 0.3.1
|
||||||
'@floating-ui/react': 0.24.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
'@floating-ui/react': 0.24.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
'@internationalized/string': 3.2.7
|
'@internationalized/string': 3.2.7
|
||||||
'@keystar/ui': 0.7.19(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
'@keystar/ui': 0.7.19(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
'@markdoc/markdoc': 0.4.0(@types/react@19.1.4)(react@19.1.0)
|
'@markdoc/markdoc': 0.4.0(@types/react@19.1.4)(react@19.1.0)
|
||||||
'@react-aria/focus': 3.20.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
'@react-aria/focus': 3.20.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
'@react-aria/i18n': 3.12.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
'@react-aria/i18n': 3.12.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
@@ -10310,13 +10145,13 @@ snapshots:
|
|||||||
- next
|
- next
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@keystatic/next@5.0.4(@keystatic/core@0.5.47(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
'@keystatic/next@5.0.4(@keystatic/core@0.5.47(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.27.6
|
'@babel/runtime': 7.27.6
|
||||||
'@keystatic/core': 0.5.47(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
'@keystatic/core': 0.5.47(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
'@types/react': 19.1.4
|
'@types/react': 19.1.4
|
||||||
chokidar: 3.6.0
|
chokidar: 3.6.0
|
||||||
next: 15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
next: 15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
react: 19.1.0
|
react: 19.1.0
|
||||||
react-dom: 19.1.0(react@19.1.0)
|
react-dom: 19.1.0(react@19.1.0)
|
||||||
server-only: 0.0.1
|
server-only: 0.0.1
|
||||||
@@ -10357,21 +10192,17 @@ snapshots:
|
|||||||
react: 19.1.0
|
react: 19.1.0
|
||||||
react-dom: 19.1.0(react@19.1.0)
|
react-dom: 19.1.0(react@19.1.0)
|
||||||
|
|
||||||
|
'@medusajs/icons@2.8.6(react@19.0.0-rc-66855b96-20241106)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0-rc-66855b96-20241106
|
||||||
|
|
||||||
'@medusajs/icons@2.8.6(react@19.1.0)':
|
'@medusajs/icons@2.8.6(react@19.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.1.0
|
react: 19.1.0
|
||||||
|
|
||||||
'@medusajs/icons@2.8.7(react@19.0.0-rc-66855b96-20241106)':
|
'@medusajs/js-sdk@2.8.6(awilix@8.0.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.0.0-rc-66855b96-20241106
|
'@medusajs/types': 2.8.6(awilix@8.0.1)
|
||||||
|
|
||||||
'@medusajs/icons@2.8.7(react@19.1.0)':
|
|
||||||
dependencies:
|
|
||||||
react: 19.1.0
|
|
||||||
|
|
||||||
'@medusajs/js-sdk@2.8.7(awilix@8.0.1)':
|
|
||||||
dependencies:
|
|
||||||
'@medusajs/types': 2.8.7(awilix@8.0.1)
|
|
||||||
fetch-event-stream: 0.1.5
|
fetch-event-stream: 0.1.5
|
||||||
qs: 6.14.0
|
qs: 6.14.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -10379,26 +10210,26 @@ snapshots:
|
|||||||
- ioredis
|
- ioredis
|
||||||
- vite
|
- vite
|
||||||
|
|
||||||
'@medusajs/types@2.8.7(awilix@8.0.1)':
|
'@medusajs/types@2.8.6(awilix@8.0.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
awilix: 8.0.1
|
awilix: 8.0.1
|
||||||
bignumber.js: 9.3.0
|
bignumber.js: 9.3.0
|
||||||
|
|
||||||
'@medusajs/ui-preset@2.8.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.8.3)))':
|
'@medusajs/ui-preset@2.8.6(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.8.3)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tailwindcss/forms': 0.5.10(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.8.3)))
|
'@tailwindcss/forms': 0.5.10(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.8.3)))
|
||||||
tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.8.3))
|
tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.8.3))
|
||||||
tailwindcss-animate: 1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.8.3)))
|
tailwindcss-animate: 1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.8.3)))
|
||||||
|
|
||||||
'@medusajs/ui-preset@2.8.7(tailwindcss@4.1.7)':
|
'@medusajs/ui-preset@2.8.6(tailwindcss@4.1.7)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tailwindcss/forms': 0.5.10(tailwindcss@4.1.7)
|
'@tailwindcss/forms': 0.5.10(tailwindcss@4.1.7)
|
||||||
tailwindcss: 4.1.7
|
tailwindcss: 4.1.7
|
||||||
tailwindcss-animate: 1.0.7(tailwindcss@4.1.7)
|
tailwindcss-animate: 1.0.7(tailwindcss@4.1.7)
|
||||||
|
|
||||||
'@medusajs/ui@4.0.17(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)(typescript@5.8.3)':
|
'@medusajs/ui@4.0.16(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@medusajs/icons': 2.8.7(react@19.0.0-rc-66855b96-20241106)
|
'@medusajs/icons': 2.8.6(react@19.0.0-rc-66855b96-20241106)
|
||||||
'@tanstack/react-table': 8.20.5(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)
|
'@tanstack/react-table': 8.20.5(react-dom@19.0.0-rc-66855b96-20241106(react@19.0.0-rc-66855b96-20241106))(react@19.0.0-rc-66855b96-20241106)
|
||||||
clsx: 1.2.1
|
clsx: 1.2.1
|
||||||
copy-to-clipboard: 3.3.3
|
copy-to-clipboard: 3.3.3
|
||||||
@@ -10418,9 +10249,9 @@ snapshots:
|
|||||||
- '@types/react-dom'
|
- '@types/react-dom'
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
'@medusajs/ui@4.0.17(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)':
|
'@medusajs/ui@4.0.16(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@medusajs/icons': 2.8.7(react@19.1.0)
|
'@medusajs/icons': 2.8.6(react@19.1.0)
|
||||||
'@tanstack/react-table': 8.20.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
'@tanstack/react-table': 8.20.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
clsx: 1.2.1
|
clsx: 1.2.1
|
||||||
copy-to-clipboard: 3.3.3
|
copy-to-clipboard: 3.3.3
|
||||||
@@ -10458,8 +10289,6 @@ snapshots:
|
|||||||
|
|
||||||
'@next/env@15.3.2': {}
|
'@next/env@15.3.2': {}
|
||||||
|
|
||||||
'@next/env@15.4.0-canary.128': {}
|
|
||||||
|
|
||||||
'@next/eslint-plugin-next@15.0.3':
|
'@next/eslint-plugin-next@15.0.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-glob: 3.3.1
|
fast-glob: 3.3.1
|
||||||
@@ -10471,51 +10300,27 @@ snapshots:
|
|||||||
'@next/swc-darwin-arm64@15.3.2':
|
'@next/swc-darwin-arm64@15.3.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-darwin-arm64@15.4.0-canary.128':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@next/swc-darwin-x64@15.3.2':
|
'@next/swc-darwin-x64@15.3.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-darwin-x64@15.4.0-canary.128':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@next/swc-linux-arm64-gnu@15.3.2':
|
'@next/swc-linux-arm64-gnu@15.3.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-arm64-gnu@15.4.0-canary.128':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@15.3.2':
|
'@next/swc-linux-arm64-musl@15.3.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@15.4.0-canary.128':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@15.3.2':
|
'@next/swc-linux-x64-gnu@15.3.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@15.4.0-canary.128':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@15.3.2':
|
'@next/swc-linux-x64-musl@15.3.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@15.4.0-canary.128':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@15.3.2':
|
'@next/swc-win32-arm64-msvc@15.3.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@15.4.0-canary.128':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@next/swc-win32-x64-msvc@15.3.2':
|
'@next/swc-win32-x64-msvc@15.3.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/swc-win32-x64-msvc@15.4.0-canary.128':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nodelib/fs.stat': 2.0.5
|
'@nodelib/fs.stat': 2.0.5
|
||||||
@@ -15902,7 +15707,7 @@ snapshots:
|
|||||||
|
|
||||||
'@sentry/core@9.27.0': {}
|
'@sentry/core@9.27.0': {}
|
||||||
|
|
||||||
'@sentry/nextjs@9.27.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.99.9)':
|
'@sentry/nextjs@9.27.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.99.9)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@opentelemetry/api': 1.9.0
|
'@opentelemetry/api': 1.9.0
|
||||||
'@opentelemetry/semantic-conventions': 1.34.0
|
'@opentelemetry/semantic-conventions': 1.34.0
|
||||||
@@ -15915,7 +15720,7 @@ snapshots:
|
|||||||
'@sentry/vercel-edge': 9.27.0
|
'@sentry/vercel-edge': 9.27.0
|
||||||
'@sentry/webpack-plugin': 3.5.0(webpack@5.99.9)
|
'@sentry/webpack-plugin': 3.5.0(webpack@5.99.9)
|
||||||
chalk: 3.0.0
|
chalk: 3.0.0
|
||||||
next: 15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
next: 15.3.2(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
resolve: 1.22.8
|
resolve: 1.22.8
|
||||||
rollup: 4.35.0
|
rollup: 4.35.0
|
||||||
stacktrace-parser: 0.1.11
|
stacktrace-parser: 0.1.11
|
||||||
@@ -16312,11 +16117,6 @@ snapshots:
|
|||||||
|
|
||||||
'@types/json5@0.0.29': {}
|
'@types/json5@0.0.29': {}
|
||||||
|
|
||||||
'@types/jsonwebtoken@9.0.10':
|
|
||||||
dependencies:
|
|
||||||
'@types/ms': 2.1.0
|
|
||||||
'@types/node': 22.15.32
|
|
||||||
|
|
||||||
'@types/linkify-it@3.0.5':
|
'@types/linkify-it@3.0.5':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -17078,8 +16878,6 @@ snapshots:
|
|||||||
node-releases: 2.0.19
|
node-releases: 2.0.19
|
||||||
update-browserslist-db: 1.1.3(browserslist@4.25.0)
|
update-browserslist-db: 1.1.3(browserslist@4.25.0)
|
||||||
|
|
||||||
buffer-equal-constant-time@1.0.1: {}
|
|
||||||
|
|
||||||
buffer-from@1.1.2: {}
|
buffer-from@1.1.2: {}
|
||||||
|
|
||||||
busboy@1.6.0:
|
busboy@1.6.0:
|
||||||
@@ -17502,10 +17300,6 @@ snapshots:
|
|||||||
|
|
||||||
eastasianwidth@0.2.0: {}
|
eastasianwidth@0.2.0: {}
|
||||||
|
|
||||||
ecdsa-sig-formatter@1.0.11:
|
|
||||||
dependencies:
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
|
|
||||||
electron-to-chromium@1.5.168: {}
|
electron-to-chromium@1.5.168: {}
|
||||||
|
|
||||||
emery@1.4.4: {}
|
emery@1.4.4: {}
|
||||||
@@ -17650,8 +17444,8 @@ snapshots:
|
|||||||
'@typescript-eslint/parser': 8.33.1(eslint@8.10.0)(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.33.1(eslint@8.10.0)(typescript@5.8.3)
|
||||||
eslint: 8.10.0
|
eslint: 8.10.0
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0)
|
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.10.0)
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0))(eslint@8.10.0)
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0)
|
||||||
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.10.0)
|
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.10.0)
|
||||||
eslint-plugin-react: 7.37.5(eslint@8.10.0)
|
eslint-plugin-react: 7.37.5(eslint@8.10.0)
|
||||||
eslint-plugin-react-hooks: 5.2.0(eslint@8.10.0)
|
eslint-plugin-react-hooks: 5.2.0(eslint@8.10.0)
|
||||||
@@ -17670,8 +17464,8 @@ snapshots:
|
|||||||
'@typescript-eslint/parser': 8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)
|
||||||
eslint: 9.28.0(jiti@2.4.2)
|
eslint: 9.28.0(jiti@2.4.2)
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2))
|
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.28.0(jiti@2.4.2))
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2))
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2))
|
||||||
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.28.0(jiti@2.4.2))
|
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.28.0(jiti@2.4.2))
|
||||||
eslint-plugin-react: 7.37.5(eslint@9.28.0(jiti@2.4.2))
|
eslint-plugin-react: 7.37.5(eslint@9.28.0(jiti@2.4.2))
|
||||||
eslint-plugin-react-hooks: 5.2.0(eslint@9.28.0(jiti@2.4.2))
|
eslint-plugin-react-hooks: 5.2.0(eslint@9.28.0(jiti@2.4.2))
|
||||||
@@ -17696,22 +17490,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)):
|
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.10.0):
|
||||||
dependencies:
|
|
||||||
'@nolyfill/is-core-module': 1.0.39
|
|
||||||
debug: 4.4.1
|
|
||||||
eslint: 9.28.0(jiti@2.4.2)
|
|
||||||
get-tsconfig: 4.10.1
|
|
||||||
is-bun-module: 2.0.0
|
|
||||||
stable-hash: 0.0.5
|
|
||||||
tinyglobby: 0.2.14
|
|
||||||
unrs-resolver: 1.7.11
|
|
||||||
optionalDependencies:
|
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2))
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0):
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nolyfill/is-core-module': 1.0.39
|
'@nolyfill/is-core-module': 1.0.39
|
||||||
debug: 4.4.1
|
debug: 4.4.1
|
||||||
@@ -17722,33 +17501,48 @@ snapshots:
|
|||||||
tinyglobby: 0.2.14
|
tinyglobby: 0.2.14
|
||||||
unrs-resolver: 1.7.11
|
unrs-resolver: 1.7.11
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0))(eslint@8.10.0)
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0))(eslint@8.10.0):
|
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.28.0(jiti@2.4.2)):
|
||||||
|
dependencies:
|
||||||
|
'@nolyfill/is-core-module': 1.0.39
|
||||||
|
debug: 4.4.1
|
||||||
|
eslint: 9.28.0(jiti@2.4.2)
|
||||||
|
get-tsconfig: 4.10.1
|
||||||
|
is-bun-module: 2.0.0
|
||||||
|
stable-hash: 0.0.5
|
||||||
|
tinyglobby: 0.2.14
|
||||||
|
unrs-resolver: 1.7.11
|
||||||
|
optionalDependencies:
|
||||||
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2))
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.10.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.33.1(eslint@8.10.0)(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.33.1(eslint@8.10.0)(typescript@5.8.3)
|
||||||
eslint: 8.10.0
|
eslint: 8.10.0
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0)
|
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.10.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)):
|
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.28.0(jiti@2.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)
|
||||||
eslint: 9.28.0(jiti@2.4.2)
|
eslint: 9.28.0(jiti@2.4.2)
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2))
|
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.28.0(jiti@2.4.2))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0))(eslint@8.10.0):
|
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rtsao/scc': 1.1.0
|
'@rtsao/scc': 1.1.0
|
||||||
array-includes: 3.1.9
|
array-includes: 3.1.9
|
||||||
@@ -17757,9 +17551,9 @@ snapshots:
|
|||||||
array.prototype.flatmap: 1.3.3
|
array.prototype.flatmap: 1.3.3
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 8.10.0
|
eslint: 9.28.0(jiti@2.4.2)
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0))(eslint@8.10.0))(eslint@8.10.0)
|
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.28.0(jiti@2.4.2))
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
is-core-module: 2.16.1
|
is-core-module: 2.16.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -17771,13 +17565,13 @@ snapshots:
|
|||||||
string.prototype.trimend: 1.0.9
|
string.prototype.trimend: 1.0.9
|
||||||
tsconfig-paths: 3.15.0
|
tsconfig-paths: 3.15.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.33.1(eslint@8.10.0)(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- eslint-import-resolver-typescript
|
- eslint-import-resolver-typescript
|
||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)):
|
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint@8.10.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rtsao/scc': 1.1.0
|
'@rtsao/scc': 1.1.0
|
||||||
array-includes: 3.1.9
|
array-includes: 3.1.9
|
||||||
@@ -17786,9 +17580,9 @@ snapshots:
|
|||||||
array.prototype.flatmap: 1.3.3
|
array.prototype.flatmap: 1.3.3
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 9.28.0(jiti@2.4.2)
|
eslint: 8.10.0
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2)))(eslint@9.28.0(jiti@2.4.2))
|
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@8.10.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.10.0)
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
is-core-module: 2.16.1
|
is-core-module: 2.16.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -17800,7 +17594,7 @@ snapshots:
|
|||||||
string.prototype.trimend: 1.0.9
|
string.prototype.trimend: 1.0.9
|
||||||
tsconfig-paths: 3.15.0
|
tsconfig-paths: 3.15.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.33.1(eslint@8.10.0)(typescript@5.8.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- eslint-import-resolver-typescript
|
- eslint-import-resolver-typescript
|
||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
@@ -18636,19 +18430,6 @@ snapshots:
|
|||||||
|
|
||||||
json5@2.2.3: {}
|
json5@2.2.3: {}
|
||||||
|
|
||||||
jsonwebtoken@9.0.2:
|
|
||||||
dependencies:
|
|
||||||
jws: 3.2.2
|
|
||||||
lodash.includes: 4.3.0
|
|
||||||
lodash.isboolean: 3.0.3
|
|
||||||
lodash.isinteger: 4.0.4
|
|
||||||
lodash.isnumber: 3.0.3
|
|
||||||
lodash.isplainobject: 4.0.6
|
|
||||||
lodash.isstring: 4.0.1
|
|
||||||
lodash.once: 4.1.1
|
|
||||||
ms: 2.1.3
|
|
||||||
semver: 7.7.2
|
|
||||||
|
|
||||||
jsx-ast-utils@3.3.5:
|
jsx-ast-utils@3.3.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
array-includes: 3.1.9
|
array-includes: 3.1.9
|
||||||
@@ -18656,17 +18437,6 @@ snapshots:
|
|||||||
object.assign: 4.1.7
|
object.assign: 4.1.7
|
||||||
object.values: 1.2.1
|
object.values: 1.2.1
|
||||||
|
|
||||||
jwa@1.4.2:
|
|
||||||
dependencies:
|
|
||||||
buffer-equal-constant-time: 1.0.1
|
|
||||||
ecdsa-sig-formatter: 1.0.11
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
|
|
||||||
jws@3.2.2:
|
|
||||||
dependencies:
|
|
||||||
jwa: 1.4.2
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
|
|
||||||
keyv@4.5.4:
|
keyv@4.5.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
json-buffer: 3.0.1
|
json-buffer: 3.0.1
|
||||||
@@ -18757,24 +18527,10 @@ snapshots:
|
|||||||
|
|
||||||
lodash.deburr@4.1.0: {}
|
lodash.deburr@4.1.0: {}
|
||||||
|
|
||||||
lodash.includes@4.3.0: {}
|
|
||||||
|
|
||||||
lodash.isboolean@3.0.3: {}
|
|
||||||
|
|
||||||
lodash.isinteger@4.0.4: {}
|
|
||||||
|
|
||||||
lodash.isnumber@3.0.3: {}
|
|
||||||
|
|
||||||
lodash.isplainobject@4.0.6: {}
|
|
||||||
|
|
||||||
lodash.isstring@4.0.1: {}
|
|
||||||
|
|
||||||
lodash.memoize@4.1.2: {}
|
lodash.memoize@4.1.2: {}
|
||||||
|
|
||||||
lodash.merge@4.6.2: {}
|
lodash.merge@4.6.2: {}
|
||||||
|
|
||||||
lodash.once@4.1.1: {}
|
|
||||||
|
|
||||||
lodash.uniq@4.5.0: {}
|
lodash.uniq@4.5.0: {}
|
||||||
|
|
||||||
lodash@4.17.21: {}
|
lodash@4.17.21: {}
|
||||||
@@ -19315,31 +19071,6 @@ snapshots:
|
|||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
|
||||||
next@15.4.0-canary.128(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
|
||||||
dependencies:
|
|
||||||
'@next/env': 15.4.0-canary.128
|
|
||||||
'@swc/helpers': 0.5.15
|
|
||||||
caniuse-lite: 1.0.30001723
|
|
||||||
postcss: 8.4.31
|
|
||||||
react: 19.1.0
|
|
||||||
react-dom: 19.1.0(react@19.1.0)
|
|
||||||
styled-jsx: 5.1.6(react@19.1.0)
|
|
||||||
optionalDependencies:
|
|
||||||
'@next/swc-darwin-arm64': 15.4.0-canary.128
|
|
||||||
'@next/swc-darwin-x64': 15.4.0-canary.128
|
|
||||||
'@next/swc-linux-arm64-gnu': 15.4.0-canary.128
|
|
||||||
'@next/swc-linux-arm64-musl': 15.4.0-canary.128
|
|
||||||
'@next/swc-linux-x64-gnu': 15.4.0-canary.128
|
|
||||||
'@next/swc-linux-x64-musl': 15.4.0-canary.128
|
|
||||||
'@next/swc-win32-arm64-msvc': 15.4.0-canary.128
|
|
||||||
'@next/swc-win32-x64-msvc': 15.4.0-canary.128
|
|
||||||
'@opentelemetry/api': 1.9.0
|
|
||||||
babel-plugin-react-compiler: 19.1.0-rc.2
|
|
||||||
sharp: 0.34.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@babel/core'
|
|
||||||
- babel-plugin-macros
|
|
||||||
|
|
||||||
no-case@3.0.4:
|
no-case@3.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
lower-case: 2.0.2
|
lower-case: 2.0.2
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "Cart",
|
|
||||||
"description": "View your cart",
|
|
||||||
"emptyCartMessage": "Your cart is empty",
|
|
||||||
"emptyCartMessageDescription": "Add items to your cart to continue.",
|
|
||||||
"subtotal": "Subtotal",
|
|
||||||
"total": "Total",
|
|
||||||
"table": {
|
|
||||||
"item": "Item",
|
|
||||||
"quantity": "Quantity",
|
|
||||||
"price": "Price",
|
|
||||||
"total": "Total"
|
|
||||||
},
|
|
||||||
"checkout": {
|
|
||||||
"goToCheckout": "Go to checkout",
|
|
||||||
"goToDashboard": "Continue",
|
|
||||||
"error": {
|
|
||||||
"title": "Something went wrong",
|
|
||||||
"description": "Please try again later."
|
|
||||||
},
|
|
||||||
"timeLeft": "Time left {{timeLeft}}",
|
|
||||||
"timeoutTitle": "Reservation expired",
|
|
||||||
"timeoutDescription": "Reservation for {{productTitle}} in shopping cart has expired.",
|
|
||||||
"timeoutAction": "Continue"
|
|
||||||
},
|
|
||||||
"discountCode": {
|
|
||||||
"label": "Add Promotion Code(s)",
|
|
||||||
"apply": "Apply",
|
|
||||||
"subtitle": "If you wish, you can add a promotion code",
|
|
||||||
"placeholder": "Enter promotion code"
|
|
||||||
},
|
|
||||||
"items": {
|
|
||||||
"analysisPackages": {
|
|
||||||
"productColumnLabel": "Package name"
|
|
||||||
},
|
|
||||||
"services": {
|
|
||||||
"productColumnLabel": "Service name"
|
|
||||||
},
|
|
||||||
"delete": {
|
|
||||||
"success": "Item removed from cart",
|
|
||||||
"loading": "Removing item from cart",
|
|
||||||
"error": "Failed to remove item from cart"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"orderConfirmed": {
|
|
||||||
"title": "Order confirmed",
|
|
||||||
"summary": "Summary",
|
|
||||||
"subtotal": "Subtotal",
|
|
||||||
"taxes": "Taxes",
|
|
||||||
"giftCard": "Gift card",
|
|
||||||
"total": "Total",
|
|
||||||
"orderDate": "Order date",
|
|
||||||
"orderNumber": "Order number",
|
|
||||||
"orderStatus": "Order status",
|
|
||||||
"paymentStatus": "Payment status"
|
|
||||||
},
|
|
||||||
"montonioCallback": {
|
|
||||||
"title": "Montonio checkout",
|
|
||||||
"description": "Please wait while we process your payment."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,16 @@
|
|||||||
{
|
{
|
||||||
|
"standard": {
|
||||||
|
"label": "Standard",
|
||||||
|
"description": "Sobib, kui soovid lisaks peamistele tervisenäitajatele ülevaadet, kas organismis on olulisemaid vitamiine ja mineraalaineid piisavalt."
|
||||||
|
},
|
||||||
|
"standardPlus": {
|
||||||
|
"label": "Standard +",
|
||||||
|
"description": "Sobib, kui soovid lisaks peamistele tervisenäitajatele ülevaadet, kas organismis on olulisemaid vitamiine ja mineraalaineid piisavalt."
|
||||||
|
},
|
||||||
|
"premium": {
|
||||||
|
"label": "Premium",
|
||||||
|
"description": "Sobib, kui soovid lisaks peamistele tervisenäitajatele ülevaadet, kas organismis on olulisemaid vitamiine ja mineraalaineid piisavalt."
|
||||||
|
},
|
||||||
"nrOfAnalyses": "{{nr}} analyses",
|
"nrOfAnalyses": "{{nr}} analyses",
|
||||||
"clinicalBloodDraw": {
|
"clinicalBloodDraw": {
|
||||||
"label": "Kliiniline vereanalüüs",
|
"label": "Kliiniline vereanalüüs",
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "Ostukorv",
|
|
||||||
"description": "Vaata oma ostukorvi",
|
|
||||||
"emptyCartMessage": "Sinu ostukorv on tühi",
|
|
||||||
"emptyCartMessageDescription": "Lisa tooteid ostukorvi, et jätkata.",
|
|
||||||
"subtotal": "Vahesumma",
|
|
||||||
"total": "Summa",
|
|
||||||
"table": {
|
|
||||||
"item": "Toode",
|
|
||||||
"quantity": "Kogus",
|
|
||||||
"price": "Hind",
|
|
||||||
"total": "Summa"
|
|
||||||
},
|
|
||||||
"checkout": {
|
|
||||||
"goToCheckout": "Vormista ost",
|
|
||||||
"goToDashboard": "Jätkan",
|
|
||||||
"error": {
|
|
||||||
"title": "Midagi läks valesti",
|
|
||||||
"description": "Palun proovi hiljem uuesti."
|
|
||||||
},
|
|
||||||
"timeLeft": "Aega jäänud {{timeLeft}}",
|
|
||||||
"timeoutTitle": "Broneering aegus",
|
|
||||||
"timeoutDescription": "Toote {{productTitle}} broneering ostukorvis on aegunud.",
|
|
||||||
"timeoutAction": "Jätkan"
|
|
||||||
},
|
|
||||||
"discountCode": {
|
|
||||||
"title": "Kinkekaart või sooduskood",
|
|
||||||
"label": "Lisa promo kood",
|
|
||||||
"apply": "Rakenda",
|
|
||||||
"subtitle": "Kui soovid, võid lisada promo koodi",
|
|
||||||
"placeholder": "Sisesta promo kood"
|
|
||||||
},
|
|
||||||
"items": {
|
|
||||||
"analysisPackages": {
|
|
||||||
"productColumnLabel": "Paketi nimi"
|
|
||||||
},
|
|
||||||
"services": {
|
|
||||||
"productColumnLabel": "Teenuse nimi"
|
|
||||||
},
|
|
||||||
"delete": {
|
|
||||||
"success": "Toode eemaldatud ostukorvist",
|
|
||||||
"loading": "Toote eemaldamine ostukorvist",
|
|
||||||
"error": "Toote eemaldamine ostukorvist ebaõnnestus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"orderConfirmed": {
|
|
||||||
"title": "Tellimus on edukalt esitatud",
|
|
||||||
"summary": "Teenused",
|
|
||||||
"subtotal": "Vahesumma",
|
|
||||||
"taxes": "Maksud",
|
|
||||||
"giftCard": "Kinkekaart",
|
|
||||||
"total": "Summa",
|
|
||||||
"orderDate": "Tellimuse kuupäev",
|
|
||||||
"orderNumber": "Tellimuse number",
|
|
||||||
"orderStatus": "Tellimuse olek",
|
|
||||||
"paymentStatus": "Makse olek"
|
|
||||||
},
|
|
||||||
"montonioCallback": {
|
|
||||||
"title": "Montonio makseprotsess",
|
|
||||||
"description": "Palun oodake, kuni me töötleme sinu makseprotsessi lõpuni."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,16 @@
|
|||||||
{
|
{
|
||||||
|
"standard": {
|
||||||
|
"label": "Standard",
|
||||||
|
"description": "Sobib, kui soovid lisaks peamistele tervisenäitajatele ülevaadet, kas organismis on olulisemaid vitamiine ja mineraalaineid piisavalt."
|
||||||
|
},
|
||||||
|
"standardPlus": {
|
||||||
|
"label": "Standard +",
|
||||||
|
"description": "Sobib, kui soovid lisaks peamistele tervisenäitajatele ülevaadet, kas organismis on olulisemaid vitamiine ja mineraalaineid piisavalt."
|
||||||
|
},
|
||||||
|
"premium": {
|
||||||
|
"label": "Premium",
|
||||||
|
"description": "Sobib, kui soovid lisaks peamistele tervisenäitajatele ülevaadet, kas organismis on olulisemaid vitamiine ja mineraalaineid piisavalt."
|
||||||
|
},
|
||||||
"nrOfAnalyses": "{{nr}} analüüsi",
|
"nrOfAnalyses": "{{nr}} analüüsi",
|
||||||
"clinicalBloodDraw": {
|
"clinicalBloodDraw": {
|
||||||
"label": "Kliiniline vereanalüüs",
|
"label": "Kliiniline vereanalüüs",
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
{
|
{
|
||||||
|
"standard": {
|
||||||
|
"label": "Standard",
|
||||||
|
"description": "Sobib, kui soovid lisaks peamistele tervisenäitajatele ülevaadet, kas organismis on olulisemaid vitamiine ja mineraalaineid piisavalt."
|
||||||
|
},
|
||||||
|
"standardPlus": {
|
||||||
|
"label": "Standard +",
|
||||||
|
"description": "Sobib, kui soovid lisaks peamistele tervisenäitajatele ülevaadet, kas organismis on olulisemaid vitamiine ja mineraalaineid piisavalt."
|
||||||
|
},
|
||||||
|
"premium": {
|
||||||
|
"label": "Premium",
|
||||||
|
"description": "Sobib, kui soovid lisaks peamistele tervisenäitajatele ülevaadet, kas organismis on olulisemaid vitamiine ja mineraalaineid piisavalt."
|
||||||
|
},
|
||||||
"nrOfAnalyses": "{{nr}} analyses",
|
"nrOfAnalyses": "{{nr}} analyses",
|
||||||
"clinicalBloodDraw": {
|
"clinicalBloodDraw": {
|
||||||
"label": "Kliiniline vereanalüüs",
|
"label": "Kliiniline vereanalüüs",
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
alter type public.billing_provider add value 'montonio';
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
create table "audit"."cart_entries" (
|
|
||||||
"id" bigint generated by default as identity not null,
|
|
||||||
"account_id" text not null,
|
|
||||||
"cart_id" text not null,
|
|
||||||
"operation" text not null,
|
|
||||||
"variant_id" text,
|
|
||||||
"comment" text,
|
|
||||||
"created_at" timestamp with time zone not null default now(),
|
|
||||||
"changed_by" uuid not null
|
|
||||||
);
|
|
||||||
|
|
||||||
grant usage on schema audit to authenticated;
|
|
||||||
grant select, insert, update, delete on table audit.cart_entries to authenticated;
|
|
||||||
|
|
||||||
alter table "audit"."cart_entries" enable row level security;
|
|
||||||
|
|
||||||
create policy "insert_own"
|
|
||||||
on "audit"."cart_entries"
|
|
||||||
as permissive
|
|
||||||
for insert
|
|
||||||
to authenticated
|
|
||||||
with check (auth.uid() = changed_by);
|
|
||||||
|
|
||||||
create policy "service_role_select" on "audit"."cart_entries" for select to service_role using (true);
|
|
||||||
create policy "service_role_insert" on "audit"."cart_entries" for insert to service_role with check (true);
|
|
||||||
create policy "service_role_update" on "audit"."cart_entries" for update to service_role using (true);
|
|
||||||
create policy "service_role_delete" on "audit"."cart_entries" for delete to service_role using (true);
|
|
||||||
Reference in New Issue
Block a user