prettier fix
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { StoreCart, StoreCartLineItem } from '@medusajs/types';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { toast } from 'sonner';
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { StoreCart, StoreCartLineItem } from "@medusajs/types"
|
||||
import { Form } from "@kit/ui/form";
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Form } from '@kit/ui/form';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -17,29 +17,39 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@kit/ui/select';
|
||||
import { updateCartPartnerLocation } from '../../_lib/server/update-cart-partner-location';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { updateCartPartnerLocation } from '../../_lib/server/update-cart-partner-location';
|
||||
import partnerLocations from './partner-locations.json';
|
||||
|
||||
const AnalysisLocationSchema = z.object({
|
||||
locationId: z.string().min(1),
|
||||
});
|
||||
|
||||
export default function AnalysisLocation({ cart, synlabAnalyses }: { cart: StoreCart, synlabAnalyses: StoreCartLineItem[] }) {
|
||||
export default function AnalysisLocation({
|
||||
cart,
|
||||
synlabAnalyses,
|
||||
}: {
|
||||
cart: StoreCart;
|
||||
synlabAnalyses: StoreCartLineItem[];
|
||||
}) {
|
||||
const { t } = useTranslation('cart');
|
||||
|
||||
const form = useForm<z.infer<typeof AnalysisLocationSchema>>({
|
||||
defaultValues: {
|
||||
locationId: cart.metadata?.partner_location_id as string ?? '',
|
||||
locationId: (cart.metadata?.partner_location_id as string) ?? '',
|
||||
},
|
||||
resolver: zodResolver(AnalysisLocationSchema),
|
||||
});
|
||||
|
||||
const getLocation = (locationId: string) => partnerLocations.find(({ name }) => name === locationId);
|
||||
const getLocation = (locationId: string) =>
|
||||
partnerLocations.find(({ name }) => name === locationId);
|
||||
|
||||
const selectedLocation = getLocation(form.watch('locationId'));
|
||||
|
||||
const onSubmit = async ({ locationId }: z.infer<typeof AnalysisLocationSchema>) => {
|
||||
const onSubmit = async ({
|
||||
locationId,
|
||||
}: z.infer<typeof AnalysisLocationSchema>) => {
|
||||
const promise = updateCartPartnerLocation({
|
||||
cartId: cart.id,
|
||||
lineIds: synlabAnalyses.map(({ id }) => id),
|
||||
@@ -52,18 +62,18 @@ export default function AnalysisLocation({ cart, synlabAnalyses }: { cart: Store
|
||||
loading: t(`cart:items.analysisLocation.loading`),
|
||||
error: t(`cart:items.analysisLocation.error`),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full bg-white flex flex-col txt-medium gap-y-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<div className="txt-medium flex h-full w-full flex-col gap-y-4 bg-white">
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<Trans i18nKey={'cart:locations.description'} />
|
||||
</p>
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit((data) => onSubmit(data))}
|
||||
className="w-full mb-2 flex gap-x-2 flex-1"
|
||||
className="mb-2 flex w-full flex-1 gap-x-2"
|
||||
>
|
||||
<Select
|
||||
value={form.watch('locationId')}
|
||||
@@ -82,34 +92,38 @@ export default function AnalysisLocation({ cart, synlabAnalyses }: { cart: Store
|
||||
</SelectTrigger>
|
||||
|
||||
<SelectContent>
|
||||
{Object.entries(partnerLocations
|
||||
.reduce((acc, curr) => ({
|
||||
...acc,
|
||||
[curr.city]: [...((acc[curr.city] as typeof partnerLocations) ?? []), curr],
|
||||
}), {} as Record<string, typeof partnerLocations>))
|
||||
.map(([city, locations]) => (
|
||||
<SelectGroup key={city}>
|
||||
<SelectLabel>{city}</SelectLabel>
|
||||
{locations.map((location) => (
|
||||
<SelectItem key={location.name} value={location.name}>{location.name}</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
))}
|
||||
{Object.entries(
|
||||
partnerLocations.reduce(
|
||||
(acc, curr) => ({
|
||||
...acc,
|
||||
[curr.city]: [
|
||||
...((acc[curr.city] as typeof partnerLocations) ?? []),
|
||||
curr,
|
||||
],
|
||||
}),
|
||||
{} as Record<string, typeof partnerLocations>,
|
||||
),
|
||||
).map(([city, locations]) => (
|
||||
<SelectGroup key={city}>
|
||||
<SelectLabel>{city}</SelectLabel>
|
||||
{locations.map((location) => (
|
||||
<SelectItem key={location.name} value={location.name}>
|
||||
{location.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
{selectedLocation && (
|
||||
<div className="flex flex-col gap-y-2 mb-4">
|
||||
<p className="text-sm">
|
||||
{selectedLocation.address}
|
||||
</p>
|
||||
<p className="text-sm">
|
||||
{selectedLocation.hours}
|
||||
</p>
|
||||
<div className="mb-4 flex flex-col gap-y-2">
|
||||
<p className="text-sm">{selectedLocation.address}</p>
|
||||
<p className="text-sm">{selectedLocation.hours}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { Trash } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Spinner } from '@medusajs/icons';
|
||||
import { Trash } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Spinner } from "@medusajs/icons";
|
||||
import { handleDeleteCartItem } from "~/lib/services/medusaCart.service";
|
||||
import { handleDeleteCartItem } from '~/lib/services/medusaCart.service';
|
||||
|
||||
const CartItemDelete = ({
|
||||
id,
|
||||
@@ -33,9 +34,9 @@ const CartItemDelete = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between text-small-regular">
|
||||
<div className="text-small-regular flex items-center justify-between">
|
||||
<button
|
||||
className="flex gap-x-1 text-ui-fg-subtle hover:text-ui-fg-base cursor-pointer"
|
||||
className="text-ui-fg-subtle hover:text-ui-fg-base flex cursor-pointer gap-x-1"
|
||||
onClick={() => handleDelete()}
|
||||
>
|
||||
{isDeleting ? <Spinner className="animate-spin" /> : <Trash />}
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
"use client"
|
||||
'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";
|
||||
import { formatCurrency } from '@/packages/shared/src/utils';
|
||||
import { HttpTypes } from '@medusajs/types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function CartItem({ item, currencyCode }: {
|
||||
item: HttpTypes.StoreCartLineItem
|
||||
currencyCode: string
|
||||
import { TableCell, TableRow } from '@kit/ui/table';
|
||||
|
||||
import CartItemDelete from './cart-item-delete';
|
||||
|
||||
export default function CartItem({
|
||||
item,
|
||||
currencyCode,
|
||||
}: {
|
||||
item: HttpTypes.StoreCartLineItem;
|
||||
currencyCode: string;
|
||||
}) {
|
||||
const { i18n: { language } } = useTranslation();
|
||||
const {
|
||||
i18n: { language },
|
||||
} = useTranslation();
|
||||
|
||||
return (
|
||||
<TableRow className="w-full" data-testid="product-row">
|
||||
<TableCell className="text-left w-[100%] px-4 sm:px-6">
|
||||
<TableCell className="w-[100%] px-4 text-left sm:px-6">
|
||||
<p
|
||||
className="txt-medium-plus text-ui-fg-base"
|
||||
data-testid="product-title"
|
||||
@@ -26,9 +30,7 @@ export default function CartItem({ item, currencyCode }: {
|
||||
</p>
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="px-4 sm:px-6">
|
||||
{item.quantity}
|
||||
</TableCell>
|
||||
<TableCell className="px-4 sm:px-6">{item.quantity}</TableCell>
|
||||
|
||||
<TableCell className="min-w-[80px] px-4 sm:px-6">
|
||||
{formatCurrency({
|
||||
@@ -38,7 +40,7 @@ export default function CartItem({ item, currencyCode }: {
|
||||
})}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="min-w-[80px] px-4 sm:px-6 text-right">
|
||||
<TableCell className="min-w-[80px] px-4 text-right sm:px-6">
|
||||
{formatCurrency({
|
||||
value: item.total,
|
||||
currencyCode,
|
||||
@@ -46,11 +48,11 @@ export default function CartItem({ item, currencyCode }: {
|
||||
})}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="text-right px-4 sm:px-6">
|
||||
<span className="flex gap-x-1 justify-end w-[60px]">
|
||||
<TableCell className="px-4 text-right sm:px-6">
|
||||
<span className="flex w-[60px] justify-end gap-x-1">
|
||||
<CartItemDelete id={item.id} />
|
||||
</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import { StoreCart, StoreCartLineItem } from "@medusajs/types"
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import CartItem from "./cart-item";
|
||||
import { StoreCart, StoreCartLineItem } from '@medusajs/types';
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@kit/ui/table';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export default function CartItems({ cart, items, productColumnLabelKey }: {
|
||||
import CartItem from './cart-item';
|
||||
|
||||
export default function CartItems({
|
||||
cart,
|
||||
items,
|
||||
productColumnLabelKey,
|
||||
}: {
|
||||
cart: StoreCart;
|
||||
items: StoreCartLineItem[];
|
||||
productColumnLabelKey: string;
|
||||
@@ -19,7 +25,7 @@ export default function CartItems({ cart, items, productColumnLabelKey }: {
|
||||
}
|
||||
|
||||
return (
|
||||
<Table className="rounded-lg border border-separate">
|
||||
<Table className="border-separate rounded-lg border">
|
||||
<TableHeader className="text-ui-fg-subtle txt-medium-plus">
|
||||
<TableRow>
|
||||
<TableHead className="px-4 sm:px-6">
|
||||
@@ -28,19 +34,20 @@ export default function CartItems({ cart, items, productColumnLabelKey }: {
|
||||
<TableHead className="px-4 sm:px-6">
|
||||
<Trans i18nKey="cart:table.quantity" />
|
||||
</TableHead>
|
||||
<TableHead className="px-4 sm:px-6 min-w-[100px]">
|
||||
<TableHead className="min-w-[100px] px-4 sm:px-6">
|
||||
<Trans i18nKey="cart:table.price" />
|
||||
</TableHead>
|
||||
<TableHead className="px-4 sm:px-6 min-w-[100px] text-right">
|
||||
<TableHead className="min-w-[100px] px-4 text-right sm:px-6">
|
||||
<Trans i18nKey="cart:table.total" />
|
||||
</TableHead>
|
||||
<TableHead className="px-4 sm:px-6">
|
||||
</TableHead>
|
||||
<TableHead className="px-4 sm:px-6"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{items
|
||||
.sort((a, b) => (a.created_at ?? "") > (b.created_at ?? "") ? -1 : 1)
|
||||
.sort((a, b) =>
|
||||
(a.created_at ?? '') > (b.created_at ?? '') ? -1 : 1,
|
||||
)
|
||||
.map((item) => (
|
||||
<CartItem
|
||||
key={item.id}
|
||||
@@ -50,5 +57,5 @@ export default function CartItems({ cart, items, productColumnLabelKey }: {
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { handleLineItemTimeout } from '@/lib/services/medusaCart.service';
|
||||
import { StoreCartLineItem } from '@medusajs/types';
|
||||
import { Timer } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -8,18 +14,17 @@ import {
|
||||
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';
|
||||
AlertDialogTitle,
|
||||
} from '@kit/ui/alert-dialog';
|
||||
import { Button } from '@kit/ui/button';
|
||||
|
||||
const TIMEOUT_MINUTES = 15;
|
||||
|
||||
export default function CartTimer({ cartItem }: { cartItem: StoreCartLineItem }) {
|
||||
export default function CartTimer({
|
||||
cartItem,
|
||||
}: {
|
||||
cartItem: StoreCartLineItem;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [timeLeft, setTimeLeft] = useState<number | null>(null);
|
||||
const [isDialogOpen, setDialogOpen] = useState(false);
|
||||
@@ -39,7 +44,9 @@ export default function CartTimer({ cartItem }: { cartItem: StoreCartLineItem })
|
||||
return () => clearInterval(interval);
|
||||
}, [updatedAt]);
|
||||
|
||||
const minutes = timeLeft ? Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60)) : 0;
|
||||
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;
|
||||
@@ -53,13 +60,16 @@ export default function CartTimer({ cartItem }: { cartItem: StoreCartLineItem })
|
||||
}, [isTimeLeftPositive, cartItem.id]);
|
||||
|
||||
if (timeLeft === null) {
|
||||
return <div className='min-h-[40px]' />;
|
||||
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">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="bg-accent hover:bg-accent flex cursor-default items-center gap-x-2 px-4"
|
||||
>
|
||||
<Timer />
|
||||
<span className="text-sm">
|
||||
{t('cart:checkout.timeLeft', {
|
||||
@@ -76,7 +86,9 @@ export default function CartTimer({ cartItem }: { cartItem: StoreCartLineItem })
|
||||
{t('cart:checkout.timeoutTitle')}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{t('cart:checkout.timeoutDescription', { productTitle: cartItem.product?.title })}
|
||||
{t('cart:checkout.timeoutDescription', {
|
||||
productTitle: cartItem.product?.title,
|
||||
})}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
@@ -87,5 +99,5 @@ export default function CartTimer({ cartItem }: { cartItem: StoreCartLineItem })
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use server"
|
||||
'use server';
|
||||
|
||||
import { applyPromotions } from "@lib/data/cart"
|
||||
import { applyPromotions } from '@lib/data/cart';
|
||||
|
||||
export async function addPromotionCodeAction(code: string) {
|
||||
try {
|
||||
@@ -12,9 +12,14 @@ export async function addPromotionCodeAction(code: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function removePromotionCodeAction(codeToRemove: string, appliedCodes: string[]) {
|
||||
export async function removePromotionCodeAction(
|
||||
codeToRemove: string,
|
||||
appliedCodes: string[],
|
||||
) {
|
||||
try {
|
||||
const updatedCodes = appliedCodes.filter((appliedCode) => appliedCode !== codeToRemove);
|
||||
const updatedCodes = appliedCodes.filter(
|
||||
(appliedCode) => appliedCode !== codeToRemove,
|
||||
);
|
||||
await applyPromotions(updatedCodes);
|
||||
return { success: true, message: 'Discount code removed successfully' };
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import { Badge, Text } from "@medusajs/ui"
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { convertToLocale } from '@lib/util/money';
|
||||
import { StoreCart, StorePromotion } from '@medusajs/types';
|
||||
import { Badge, Text } from '@medusajs/ui';
|
||||
import Trash from '@modules/common/icons/trash';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
|
||||
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 { Form, FormControl, FormField, FormItem } from '@kit/ui/form';
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
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";
|
||||
import { addPromotionCodeAction, removePromotionCodeAction } from "./discount-code-actions";
|
||||
|
||||
import {
|
||||
addPromotionCodeAction,
|
||||
removePromotionCodeAction,
|
||||
} from './discount-code-actions';
|
||||
|
||||
const DiscountCodeSchema = z.object({
|
||||
code: z.string().min(1),
|
||||
})
|
||||
});
|
||||
|
||||
export default function DiscountCode({ cart }: {
|
||||
export default function DiscountCode({
|
||||
cart,
|
||||
}: {
|
||||
cart: StoreCart & {
|
||||
promotions: StorePromotion[]
|
||||
}
|
||||
promotions: StorePromotion[];
|
||||
};
|
||||
}) {
|
||||
const { t } = useTranslation('cart');
|
||||
|
||||
@@ -33,11 +40,11 @@ export default function DiscountCode({ cart }: {
|
||||
const removePromotionCode = async (code: string) => {
|
||||
const appliedCodes = promotions
|
||||
.filter((p) => p.code !== undefined)
|
||||
.map((p) => p.code!)
|
||||
.map((p) => p.code!);
|
||||
|
||||
const loading = toast.loading(t('cart:discountCode.removeLoading'));
|
||||
|
||||
const result = await removePromotionCodeAction(code, appliedCodes)
|
||||
const result = await removePromotionCodeAction(code, appliedCodes);
|
||||
|
||||
toast.dismiss(loading);
|
||||
if (result.success) {
|
||||
@@ -45,21 +52,20 @@ export default function DiscountCode({ cart }: {
|
||||
} else {
|
||||
toast.error(t('cart:discountCode.removeError'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addPromotionCode = async (code: string) => {
|
||||
const loading = toast.loading(t('cart:discountCode.addLoading'));
|
||||
const result = await addPromotionCodeAction(code)
|
||||
const result = await addPromotionCodeAction(code);
|
||||
|
||||
toast.dismiss(loading);
|
||||
if (result.success) {
|
||||
toast.success(t('cart:discountCode.addSuccess'));
|
||||
form.reset()
|
||||
form.reset();
|
||||
} else {
|
||||
toast.error(t('cart:discountCode.addError'));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const form = useForm<z.infer<typeof DiscountCodeSchema>>({
|
||||
defaultValues: {
|
||||
@@ -69,40 +75,41 @@ export default function DiscountCode({ cart }: {
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="w-full h-full bg-white flex flex-col txt-medium gap-y-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<div className="txt-medium flex h-full w-full flex-col gap-y-4 bg-white">
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<Trans i18nKey={'cart:discountCode.subtitle'} />
|
||||
</p>
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit((data) => addPromotionCode(data.code))}
|
||||
className="w-full mb-2 flex gap-x-2 sm:flex-row flex-col gap-y-2 flex-1"
|
||||
className="mb-2 flex w-full flex-1 flex-col gap-x-2 gap-y-2 sm:flex-row"
|
||||
>
|
||||
<FormField
|
||||
name={'code'}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<Input required type="text" {...field} placeholder={t('cart:discountCode.placeholder')} />
|
||||
<Input
|
||||
required
|
||||
type="text"
|
||||
{...field}
|
||||
placeholder={t('cart:discountCode.placeholder')}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="secondary"
|
||||
className="h-min"
|
||||
>
|
||||
<Button type="submit" variant="secondary" className="h-min">
|
||||
<Trans i18nKey={'cart:discountCode.apply'} />
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
{promotions.length > 0 && (
|
||||
<div className="w-full flex items-center mt-4">
|
||||
<div className="flex flex-col w-full gap-y-2">
|
||||
<div className="mt-4 flex w-full items-center">
|
||||
<div className="flex w-full flex-col gap-y-2">
|
||||
<p>
|
||||
<Trans i18nKey={'cart:discountCode.appliedCodes'} />
|
||||
</p>
|
||||
@@ -111,32 +118,32 @@ export default function DiscountCode({ cart }: {
|
||||
return (
|
||||
<div
|
||||
key={promotion.id}
|
||||
className="flex items-center justify-between w-full max-w-full mb-2"
|
||||
className="mb-2 flex w-full max-w-full items-center justify-between"
|
||||
data-testid="discount-row"
|
||||
>
|
||||
<Text className="flex gap-x-1 items-baseline text-sm w-4/5 pr-1">
|
||||
<Text className="flex w-4/5 items-baseline gap-x-1 pr-1 text-sm">
|
||||
<span className="truncate" data-testid="discount-code">
|
||||
<Badge
|
||||
color={promotion.is_automatic ? "green" : "grey"}
|
||||
color={promotion.is_automatic ? 'green' : 'grey'}
|
||||
size="small"
|
||||
className="px-4 text-sm"
|
||||
>
|
||||
{promotion.code}
|
||||
</Badge>{" "}
|
||||
</Badge>{' '}
|
||||
(
|
||||
{promotion.application_method?.value !== undefined &&
|
||||
promotion.application_method.currency_code !==
|
||||
undefined && (
|
||||
undefined && (
|
||||
<>
|
||||
{promotion.application_method.type ===
|
||||
"percentage"
|
||||
{promotion.application_method.type === 'percentage'
|
||||
? `${promotion.application_method.value}%`
|
||||
: convertToLocale({
|
||||
amount: Number(promotion.application_method.value),
|
||||
currency_code:
|
||||
promotion.application_method
|
||||
.currency_code,
|
||||
})}
|
||||
amount: Number(
|
||||
promotion.application_method.value,
|
||||
),
|
||||
currency_code:
|
||||
promotion.application_method.currency_code,
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
)
|
||||
@@ -152,10 +159,10 @@ export default function DiscountCode({ cart }: {
|
||||
className="flex items-center"
|
||||
onClick={() => {
|
||||
if (!promotion.code) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
removePromotionCode(promotion.code)
|
||||
removePromotionCode(promotion.code);
|
||||
}}
|
||||
data-testid="remove-discount-button"
|
||||
>
|
||||
@@ -166,11 +173,11 @@ export default function DiscountCode({ cart }: {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { handleNavigateToPayment } from '@/lib/services/medusaCart.service';
|
||||
import { formatCurrency } from '@/packages/shared/src/utils';
|
||||
import { initiatePaymentSession } from '@lib/data/cart';
|
||||
import { StoreCart, StoreCartLineItem } from '@medusajs/types';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useState } from "react";
|
||||
import { Loader2 } from "lucide-react";
|
||||
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 { initiatePaymentSession } from "@lib/data/cart";
|
||||
import { formatCurrency } from "@/packages/shared/src/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { handleNavigateToPayment } from "@/lib/services/medusaCart.service";
|
||||
import AnalysisLocation from "./analysis-location";
|
||||
import { Card, CardContent, CardHeader } from '@kit/ui/card';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import AnalysisLocation from './analysis-location';
|
||||
import CartItems from './cart-items';
|
||||
import DiscountCode from './discount-code';
|
||||
|
||||
const IS_DISCOUNT_SHOWN = true as boolean;
|
||||
|
||||
@@ -25,11 +24,13 @@ export default function Cart({
|
||||
synlabAnalyses,
|
||||
ttoServiceItems,
|
||||
}: {
|
||||
cart: StoreCart | null
|
||||
cart: StoreCart | null;
|
||||
synlabAnalyses: StoreCartLineItem[];
|
||||
ttoServiceItems: StoreCartLineItem[];
|
||||
}) {
|
||||
const { i18n: { language } } = useTranslation();
|
||||
const {
|
||||
i18n: { language },
|
||||
} = useTranslation();
|
||||
|
||||
const [isInitiatingSession, setIsInitiatingSession] = useState(false);
|
||||
|
||||
@@ -39,7 +40,10 @@ export default function Cart({
|
||||
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">
|
||||
<div
|
||||
className="flex flex-col items-center justify-center"
|
||||
data-testid="empty-cart-message"
|
||||
>
|
||||
<h4 className="text-center">
|
||||
<Trans i18nKey="cart:emptyCartMessage" />
|
||||
</h4>
|
||||
@@ -71,21 +75,29 @@ export default function Cart({
|
||||
const isLocationsShown = synlabAnalyses.length > 0;
|
||||
|
||||
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={synlabAnalyses} productColumnLabelKey="cart:items.synlabAnalyses.productColumnLabel" />
|
||||
<CartItems cart={cart} items={ttoServiceItems} productColumnLabelKey="cart:items.ttoServices.productColumnLabel" />
|
||||
<div className="small:grid-cols-[1fr_360px] grid grid-cols-1 gap-x-40 lg:px-4">
|
||||
<div className="flex flex-col gap-y-6 bg-white">
|
||||
<CartItems
|
||||
cart={cart}
|
||||
items={synlabAnalyses}
|
||||
productColumnLabelKey="cart:items.synlabAnalyses.productColumnLabel"
|
||||
/>
|
||||
<CartItems
|
||||
cart={cart}
|
||||
items={ttoServiceItems}
|
||||
productColumnLabelKey="cart:items.ttoServices.productColumnLabel"
|
||||
/>
|
||||
</div>
|
||||
{hasCartItems && (
|
||||
<>
|
||||
<div className="flex sm:justify-end gap-x-4 px-4 sm:px-6 pt-2 sm:pt-4">
|
||||
<div className="w-full sm:w-auto sm:mr-[42px]">
|
||||
<p className="ml-0 font-bold text-sm text-muted-foreground">
|
||||
<div className="flex gap-x-4 px-4 pt-2 sm:justify-end sm:px-6 sm:pt-4">
|
||||
<div className="w-full sm:mr-[42px] sm:w-auto">
|
||||
<p className="text-muted-foreground ml-0 text-sm font-bold">
|
||||
<Trans i18nKey="cart:order.subtotal" />
|
||||
</p>
|
||||
</div>
|
||||
<div className={`sm:mr-[112px] sm:w-[50px]`}>
|
||||
<p className="text-sm text-right">
|
||||
<p className="text-right text-sm">
|
||||
{formatCurrency({
|
||||
value: cart.subtotal,
|
||||
currencyCode: cart.currency_code,
|
||||
@@ -94,14 +106,14 @@ export default function Cart({
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex sm:justify-end gap-x-4 px-4 sm:px-6 py-2 sm:py-4">
|
||||
<div className="w-full sm:w-auto sm:mr-[42px]">
|
||||
<p className="ml-0 font-bold text-sm text-muted-foreground">
|
||||
<div className="flex gap-x-4 px-4 py-2 sm:justify-end sm:px-6 sm:py-4">
|
||||
<div className="w-full sm:mr-[42px] sm:w-auto">
|
||||
<p className="text-muted-foreground ml-0 text-sm font-bold">
|
||||
<Trans i18nKey="cart:order.promotionsTotal" />
|
||||
</p>
|
||||
</div>
|
||||
<div className={`sm:mr-[112px] sm:w-[50px]`}>
|
||||
<p className="text-sm text-right">
|
||||
<p className="text-right text-sm">
|
||||
{formatCurrency({
|
||||
value: cart.discount_total,
|
||||
currencyCode: cart.currency_code,
|
||||
@@ -110,14 +122,14 @@ export default function Cart({
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex sm:justify-end gap-x-4 px-4 sm:px-6">
|
||||
<div className="w-full sm:w-auto sm:mr-[42px]">
|
||||
<p className="ml-0 font-bold text-sm">
|
||||
<div className="flex gap-x-4 px-4 sm:justify-end sm:px-6">
|
||||
<div className="w-full sm:mr-[42px] sm:w-auto">
|
||||
<p className="ml-0 text-sm font-bold">
|
||||
<Trans i18nKey="cart:order.total" />
|
||||
</p>
|
||||
</div>
|
||||
<div className={`sm:mr-[112px] sm:w-[50px]`}>
|
||||
<p className="text-sm text-right">
|
||||
<p className="text-right text-sm">
|
||||
{formatCurrency({
|
||||
value: cart.total,
|
||||
currencyCode: cart.currency_code,
|
||||
@@ -129,11 +141,9 @@ export default function Cart({
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="flex sm:flex-row flex-col gap-y-6 py-4 sm:py-8 gap-x-4">
|
||||
<div className="flex flex-col gap-x-4 gap-y-6 py-4 sm:flex-row sm:py-8">
|
||||
{IS_DISCOUNT_SHOWN && (
|
||||
<Card
|
||||
className="flex flex-col justify-between w-full sm:w-1/2"
|
||||
>
|
||||
<Card className="flex w-full flex-col justify-between sm:w-1/2">
|
||||
<CardHeader className="pb-4">
|
||||
<h5>
|
||||
<Trans i18nKey="cart:discountCode.title" />
|
||||
@@ -146,24 +156,31 @@ export default function Cart({
|
||||
)}
|
||||
|
||||
{isLocationsShown && (
|
||||
<Card
|
||||
className="flex flex-col justify-between w-full sm:w-1/2"
|
||||
>
|
||||
<Card className="flex w-full flex-col justify-between sm:w-1/2">
|
||||
<CardHeader className="pb-4">
|
||||
<h5>
|
||||
<Trans i18nKey="cart:locations.title" />
|
||||
</h5>
|
||||
</CardHeader>
|
||||
<CardContent className="h-full">
|
||||
<AnalysisLocation cart={{ ...cart }} synlabAnalyses={synlabAnalyses} />
|
||||
<AnalysisLocation
|
||||
cart={{ ...cart }}
|
||||
synlabAnalyses={synlabAnalyses}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button className="h-10" onClick={initiatePayment} disabled={isInitiatingSession}>
|
||||
{isInitiatingSession && <Loader2 className="w-4 h-4 mr-2 animate-spin" />}
|
||||
<Button
|
||||
className="h-10"
|
||||
onClick={initiatePayment}
|
||||
disabled={isInitiatingSession}
|
||||
>
|
||||
{isInitiatingSession && (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
)}
|
||||
<Trans i18nKey="cart:checkout.goToCheckout" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -119,4 +119,4 @@
|
||||
"hours": "Verevõtt tööpäeviti 8.00-12.00",
|
||||
"city": "Otepää"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -4,12 +4,12 @@ export interface MontonioOrderToken {
|
||||
merchantReference: string;
|
||||
merchantReferenceDisplay: string;
|
||||
paymentStatus:
|
||||
| 'PAID'
|
||||
| 'FAILED'
|
||||
| 'CANCELLED'
|
||||
| 'PENDING'
|
||||
| 'EXPIRED'
|
||||
| 'REFUNDED';
|
||||
| 'PAID'
|
||||
| 'FAILED'
|
||||
| 'CANCELLED'
|
||||
| 'PENDING'
|
||||
| 'EXPIRED'
|
||||
| 'REFUNDED';
|
||||
paymentMethod: string;
|
||||
grandTotal: number;
|
||||
currency: string;
|
||||
@@ -19,4 +19,4 @@ export interface MontonioOrderToken {
|
||||
paymentLinkUuid: string;
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user