feat(MED-121): use age+sex specific analysis package variants
This commit is contained in:
@@ -22,8 +22,14 @@ import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
|||||||
import { PackageHeader } from '@kit/shared/components/package-header';
|
import { PackageHeader } from '@kit/shared/components/package-header';
|
||||||
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
|
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
|
||||||
import { StoreProduct } from '@medusajs/types';
|
import { StoreProduct } from '@medusajs/types';
|
||||||
import { getAnalysisElementMedusaProductIds } from '@/utils/medusa-product';
|
import { AnalysisPackageWithVariant } from '@kit/shared/components/select-analysis-package';
|
||||||
import { withI18n } from '@/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
|
||||||
|
export type AnalysisPackageElement = Pick<StoreProduct, 'title' | 'id' | 'description'> & {
|
||||||
|
isIncludedInStandard: boolean;
|
||||||
|
isIncludedInStandardPlus: boolean;
|
||||||
|
isIncludedInPremium: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
const CheckWithBackground = () => {
|
const CheckWithBackground = () => {
|
||||||
return (
|
return (
|
||||||
@@ -33,15 +39,15 @@ const CheckWithBackground = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PackageTableHead = async ({ product, nrOfAnalyses }: { product: StoreProduct, nrOfAnalyses: number }) => {
|
const PackageTableHead = async ({ product }: { product: AnalysisPackageWithVariant }) => {
|
||||||
const { t, language } = await createI18nServerInstance();
|
const { t, language } = await createI18nServerInstance();
|
||||||
const variant = product.variants?.[0];
|
|
||||||
const titleKey = product.title;
|
const { title, price, nrOfAnalyses } = product;
|
||||||
const price = variant?.calculated_price?.calculated_amount ?? 0;
|
|
||||||
return (
|
return (
|
||||||
<TableHead className="py-2">
|
<TableHead className="py-2">
|
||||||
<PackageHeader
|
<PackageHeader
|
||||||
title={t(titleKey)}
|
title={t(title)}
|
||||||
tagColor='bg-cyan'
|
tagColor='bg-cyan'
|
||||||
analysesNr={t('product:nrOfAnalyses', { nr: nrOfAnalyses })}
|
analysesNr={t('product:nrOfAnalyses', { nr: nrOfAnalyses })}
|
||||||
language={language}
|
language={language}
|
||||||
@@ -56,24 +62,20 @@ const ComparePackagesModal = async ({
|
|||||||
analysisPackageElements,
|
analysisPackageElements,
|
||||||
triggerElement,
|
triggerElement,
|
||||||
}: {
|
}: {
|
||||||
analysisPackages: StoreProduct[];
|
analysisPackages: AnalysisPackageWithVariant[];
|
||||||
analysisPackageElements: StoreProduct[];
|
analysisPackageElements: AnalysisPackageElement[];
|
||||||
triggerElement: JSX.Element;
|
triggerElement: JSX.Element;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = await createI18nServerInstance();
|
const { t } = await createI18nServerInstance();
|
||||||
|
|
||||||
const standardPackage = analysisPackages.find(({ metadata }) => metadata?.analysisPackageTier === 'standard')!;
|
const standardPackage = analysisPackages.find(({ isStandard }) => isStandard);
|
||||||
const standardPlusPackage = analysisPackages.find(({ metadata }) => metadata?.analysisPackageTier === 'standard-plus')!;
|
const standardPlusPackage = analysisPackages.find(({ isStandardPlus }) => isStandardPlus);
|
||||||
const premiumPackage = analysisPackages.find(({ metadata }) => metadata?.analysisPackageTier === 'premium')!;
|
const premiumPackage = analysisPackages.find(({ isPremium }) => isPremium);
|
||||||
|
|
||||||
if (!standardPackage || !standardPlusPackage || !premiumPackage) {
|
if (!standardPackage || !standardPlusPackage || !premiumPackage) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const standardPackageAnalyses = getAnalysisElementMedusaProductIds([standardPackage]);
|
|
||||||
const standardPlusPackageAnalyses = getAnalysisElementMedusaProductIds([standardPlusPackage]);
|
|
||||||
const premiumPackageAnalyses = getAnalysisElementMedusaProductIds([premiumPackage]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>{triggerElement}</DialogTrigger>
|
<DialogTrigger asChild>{triggerElement}</DialogTrigger>
|
||||||
@@ -103,9 +105,9 @@ const ComparePackagesModal = async ({
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead></TableHead>
|
<TableHead></TableHead>
|
||||||
<PackageTableHead product={standardPackage} nrOfAnalyses={standardPackageAnalyses.length} />
|
<PackageTableHead product={standardPackage} />
|
||||||
<PackageTableHead product={standardPlusPackage} nrOfAnalyses={standardPlusPackageAnalyses.length} />
|
<PackageTableHead product={standardPlusPackage} />
|
||||||
<PackageTableHead product={premiumPackage} nrOfAnalyses={premiumPackageAnalyses.length} />
|
<PackageTableHead product={premiumPackage} />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@@ -115,29 +117,29 @@ const ComparePackagesModal = async ({
|
|||||||
title,
|
title,
|
||||||
id,
|
id,
|
||||||
description,
|
description,
|
||||||
|
isIncludedInStandard,
|
||||||
|
isIncludedInStandardPlus,
|
||||||
|
isIncludedInPremium,
|
||||||
},
|
},
|
||||||
index,
|
|
||||||
) => {
|
) => {
|
||||||
if (!title) {
|
if (!title) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const includedInStandard = standardPackageAnalyses.includes(id);
|
|
||||||
const includedInStandardPlus = standardPlusPackageAnalyses.includes(id);
|
|
||||||
const includedInPremium = premiumPackageAnalyses.includes(id);
|
|
||||||
return (
|
return (
|
||||||
<TableRow key={index}>
|
<TableRow key={id}>
|
||||||
<TableCell className="py-6">
|
<TableCell className="py-6">
|
||||||
{title}{' '}
|
{title}{' '}
|
||||||
{description && (<InfoTooltip content={description} icon={<QuestionMarkCircledIcon />} />)}
|
{description && (<InfoTooltip content={description} icon={<QuestionMarkCircledIcon />} />)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center" className="py-6">
|
<TableCell align="center" className="py-6">
|
||||||
{includedInStandard && <CheckWithBackground />}
|
{isIncludedInStandard && <CheckWithBackground />}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center" className="py-6">
|
<TableCell align="center" className="py-6">
|
||||||
{(includedInStandard || includedInStandardPlus) && <CheckWithBackground />}
|
{(isIncludedInStandard || isIncludedInStandardPlus) && <CheckWithBackground />}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center" className="py-6">
|
<TableCell align="center" className="py-6">
|
||||||
{(includedInStandard || includedInStandardPlus || includedInPremium) && <CheckWithBackground />}
|
{(isIncludedInStandard || isIncludedInStandardPlus || isIncludedInPremium) && <CheckWithBackground />}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,30 +9,39 @@ import {
|
|||||||
CardFooter,
|
CardFooter,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
} from '@kit/ui/card';
|
} from '@kit/ui/card';
|
||||||
import { StoreProduct, StoreProductVariant } from '@medusajs/types';
|
import { StoreProduct } from '@medusajs/types';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { handleAddToCart } from '~/lib/services/medusaCart.service';
|
import { handleAddToCart } from '~/lib/services/medusaCart.service';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
|
import { InfoTooltip } from '@kit/shared/components/ui/info-tooltip';
|
||||||
import { Trans } from '@kit/ui/trans';
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
|
||||||
|
export type OrderAnalysisCard = Pick<
|
||||||
|
StoreProduct, 'title' | 'description' | 'subtitle'
|
||||||
|
> & {
|
||||||
|
isAvailable: boolean;
|
||||||
|
variant: { id: string };
|
||||||
|
};
|
||||||
|
|
||||||
export default function OrderAnalysesCards({
|
export default function OrderAnalysesCards({
|
||||||
analyses,
|
analyses,
|
||||||
countryCode,
|
countryCode,
|
||||||
}: {
|
}: {
|
||||||
analyses: StoreProduct[];
|
analyses: OrderAnalysisCard[];
|
||||||
countryCode: string;
|
countryCode: string;
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const [isAddingToCart, setIsAddingToCart] = useState(false);
|
const [isAddingToCart, setIsAddingToCart] = useState(false);
|
||||||
const handleSelect = async (selectedVariant: StoreProductVariant) => {
|
const handleSelect = async (variantId: string) => {
|
||||||
if (!selectedVariant?.id || isAddingToCart) return null
|
if (isAddingToCart) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
setIsAddingToCart(true);
|
setIsAddingToCart(true);
|
||||||
try {
|
try {
|
||||||
await handleAddToCart({
|
await handleAddToCart({
|
||||||
selectedVariant,
|
selectedVariant: { id: variantId },
|
||||||
countryCode,
|
countryCode,
|
||||||
});
|
});
|
||||||
setIsAddingToCart(false);
|
setIsAddingToCart(false);
|
||||||
@@ -47,13 +56,11 @@ export default function OrderAnalysesCards({
|
|||||||
<div className="grid grid-cols-3 gap-6 mt-4">
|
<div className="grid grid-cols-3 gap-6 mt-4">
|
||||||
{analyses.map(({
|
{analyses.map(({
|
||||||
title,
|
title,
|
||||||
variants,
|
variant,
|
||||||
description,
|
description,
|
||||||
subtitle,
|
subtitle,
|
||||||
status,
|
isAvailable,
|
||||||
metadata,
|
|
||||||
}) => {
|
}) => {
|
||||||
const isAvailable = status === 'published' && !!metadata?.analysisIdOriginal;
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
key={title}
|
key={title}
|
||||||
@@ -72,7 +79,7 @@ export default function OrderAnalysesCards({
|
|||||||
size="icon"
|
size="icon"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="px-2 text-black"
|
className="px-2 text-black"
|
||||||
onClick={() => handleSelect(variants![0]!)}
|
onClick={() => handleSelect(variant.id)}
|
||||||
>
|
>
|
||||||
{isAddingToCart ? <Loader2 className="size-4 stroke-2 animate-spin" /> : <ShoppingCart className="size-4 stroke-2" />}
|
{isAddingToCart ? <Loader2 className="size-4 stroke-2 animate-spin" /> : <ShoppingCart className="size-4 stroke-2" />}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { cache } from 'react';
|
|||||||
import { listProductTypes } from "@lib/data/products";
|
import { listProductTypes } from "@lib/data/products";
|
||||||
import { listRegions } from '@lib/data/regions';
|
import { listRegions } from '@lib/data/regions';
|
||||||
import { getProductCategories } from '@lib/data/categories';
|
import { getProductCategories } from '@lib/data/categories';
|
||||||
|
import { OrderAnalysisCard } from '../../_components/order-analyses-cards';
|
||||||
|
|
||||||
async function countryCodesLoader() {
|
async function countryCodesLoader() {
|
||||||
const countryCodes = await listRegions().then((regions) =>
|
const countryCodes = await listRegions().then((regions) =>
|
||||||
@@ -34,7 +35,18 @@ async function analysesLoader() {
|
|||||||
const category = productCategories.find(({ metadata }) => metadata?.page === 'order-analysis');
|
const category = productCategories.find(({ metadata }) => metadata?.page === 'order-analysis');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
analyses: category?.products ?? [],
|
analyses: category?.products?.map<OrderAnalysisCard>(({ title, description, subtitle, variants, status, metadata }) => {
|
||||||
|
const variant = variants![0]!;
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
subtitle,
|
||||||
|
variant: {
|
||||||
|
id: variant.id,
|
||||||
|
},
|
||||||
|
isAvailable: status === 'published' && !!metadata?.analysisIdOriginal,
|
||||||
|
};
|
||||||
|
}) ?? [],
|
||||||
countryCode,
|
countryCode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import { cache } from 'react';
|
import { cache } from 'react';
|
||||||
|
import Isikukood, { Gender } from 'isikukood';
|
||||||
|
|
||||||
import { listProductTypes, listProducts } from "@lib/data/products";
|
import { listProductTypes, listProducts } from "@lib/data/products";
|
||||||
import { listRegions } from '@lib/data/regions';
|
import { listRegions } from '@lib/data/regions';
|
||||||
import { getAnalysisElementMedusaProductIds } from '@/utils/medusa-product';
|
import { getAnalysisElementMedusaProductIds } from '@/utils/medusa-product';
|
||||||
import type { StoreProduct } from '@medusajs/types';
|
import type { StoreProduct } from '@medusajs/types';
|
||||||
|
import { loadCurrentUserAccount } from './load-user-account';
|
||||||
|
import { AnalysisPackageWithVariant } from '~/components/select-analysis-package';
|
||||||
|
import { AccountWithParams } from '@/packages/features/accounts/src/server/api';
|
||||||
|
|
||||||
async function countryCodesLoader() {
|
async function countryCodesLoader() {
|
||||||
const countryCodes = await listRegions().then((regions) =>
|
const countryCodes = await listRegions().then((regions) =>
|
||||||
@@ -19,36 +23,153 @@ async function productTypesLoader() {
|
|||||||
}
|
}
|
||||||
export const loadProductTypes = cache(productTypesLoader);
|
export const loadProductTypes = cache(productTypesLoader);
|
||||||
|
|
||||||
async function analysisPackagesLoader() {
|
function userSpecificVariantLoader({
|
||||||
const [countryCodes, productTypes] = await Promise.all([loadCountryCodes(), loadProductTypes()]);
|
account,
|
||||||
const countryCode = countryCodes[0]!;
|
}: {
|
||||||
|
account: AccountWithParams;
|
||||||
|
}) {
|
||||||
|
const { personal_code: personalCode } = account;
|
||||||
|
if (!personalCode) {
|
||||||
|
throw new Error('Personal code not found');
|
||||||
|
}
|
||||||
|
const parsed = new Isikukood(personalCode);
|
||||||
|
const ageRange = (() => {
|
||||||
|
const age = parsed.getAge();
|
||||||
|
if (age >= 18 && age <= 29) {
|
||||||
|
return '18-29';
|
||||||
|
}
|
||||||
|
if (age >= 30 && age <= 49) {
|
||||||
|
return '30-49';
|
||||||
|
}
|
||||||
|
if (age >= 50 && age <= 59) {
|
||||||
|
return '50-59';
|
||||||
|
}
|
||||||
|
if (age >= 60) {
|
||||||
|
return '60';
|
||||||
|
}
|
||||||
|
throw new Error('Age range not supported');
|
||||||
|
})();
|
||||||
|
const gender = parsed.getGender() === Gender.MALE ? 'M' : 'F';
|
||||||
|
|
||||||
let analysisPackages: StoreProduct[] = [];
|
return ({
|
||||||
let analysisPackageElements: StoreProduct[] = [];
|
product,
|
||||||
|
}: {
|
||||||
|
product: StoreProduct;
|
||||||
|
}) => {
|
||||||
|
const variants = product.variants;
|
||||||
|
if (!variants) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const productType = productTypes.find(({ metadata }) => metadata?.handle === 'analysis-packages');
|
const variant = variants.find((v) => v.options?.every((o) => [ageRange, gender].includes(o.value)));
|
||||||
if (!productType) {
|
if (!variant) {
|
||||||
return { analysisPackageElements, analysisPackages, countryCode };
|
return null;
|
||||||
|
}
|
||||||
|
return variant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function analysisPackageElementsLoader({
|
||||||
|
analysisPackagesWithVariant,
|
||||||
|
countryCode,
|
||||||
|
}: {
|
||||||
|
analysisPackagesWithVariant: AnalysisPackageWithVariant[];
|
||||||
|
countryCode: string;
|
||||||
|
}) {
|
||||||
|
const analysisElementMedusaProductIds = getAnalysisElementMedusaProductIds(analysisPackagesWithVariant);
|
||||||
|
if (analysisElementMedusaProductIds.length === 0) {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { response: { products } } = await listProducts({
|
||||||
|
countryCode,
|
||||||
|
queryParams: {
|
||||||
|
id: analysisElementMedusaProductIds,
|
||||||
|
limit: 100,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const standardPackage = analysisPackagesWithVariant.find(({ isStandard }) => isStandard);
|
||||||
|
const standardPlusPackage = analysisPackagesWithVariant.find(({ isStandardPlus }) => isStandardPlus);
|
||||||
|
const premiumPackage = analysisPackagesWithVariant.find(({ isPremium }) => isPremium);
|
||||||
|
if (!standardPackage || !standardPlusPackage || !premiumPackage) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const standardPackageAnalyses = getAnalysisElementMedusaProductIds([standardPackage]);
|
||||||
|
const standardPlusPackageAnalyses = getAnalysisElementMedusaProductIds([standardPlusPackage]);
|
||||||
|
const premiumPackageAnalyses = getAnalysisElementMedusaProductIds([premiumPackage]);
|
||||||
|
|
||||||
|
return products.map(({ id, title, description }) => ({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
isIncludedInStandard: standardPackageAnalyses.includes(id),
|
||||||
|
isIncludedInStandardPlus: standardPlusPackageAnalyses.includes(id),
|
||||||
|
isIncludedInPremium: premiumPackageAnalyses.includes(id),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function analysisPackagesWithVariantLoader({
|
||||||
|
account,
|
||||||
|
countryCode,
|
||||||
|
}: {
|
||||||
|
account: AccountWithParams;
|
||||||
|
countryCode: string;
|
||||||
|
}) {
|
||||||
|
const productTypes = await loadProductTypes();
|
||||||
|
const productType = productTypes.find(({ metadata }) => metadata?.handle === 'analysis-packages');
|
||||||
|
if (!productType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const analysisPackagesResponse = await listProducts({
|
const analysisPackagesResponse = await listProducts({
|
||||||
countryCode,
|
countryCode,
|
||||||
queryParams: { limit: 100, "type_id[0]": productType.id },
|
queryParams: { limit: 100, "type_id[0]": productType.id },
|
||||||
});
|
});
|
||||||
analysisPackages = analysisPackagesResponse.response.products;
|
|
||||||
|
|
||||||
const analysisElementMedusaProductIds = getAnalysisElementMedusaProductIds(analysisPackages);
|
const getVariant = userSpecificVariantLoader({ account });
|
||||||
if (analysisElementMedusaProductIds.length > 0) {
|
const analysisPackagesWithVariant = analysisPackagesResponse.response.products
|
||||||
const { response: { products } } = await listProducts({
|
.reduce((acc, product) => {
|
||||||
countryCode,
|
const variant = getVariant({ product });
|
||||||
queryParams: {
|
if (!variant) {
|
||||||
id: analysisElementMedusaProductIds,
|
return acc;
|
||||||
limit: 100,
|
}
|
||||||
},
|
return [
|
||||||
});
|
...acc,
|
||||||
analysisPackageElements = products;
|
{
|
||||||
|
variantId: variant.id,
|
||||||
|
nrOfAnalyses: getAnalysisElementMedusaProductIds([product]).length,
|
||||||
|
price: variant.calculated_price?.calculated_amount ?? 0,
|
||||||
|
title: product.title,
|
||||||
|
subtitle: product.subtitle,
|
||||||
|
description: product.description,
|
||||||
|
metadata: product.metadata,
|
||||||
|
isStandard: product.metadata?.analysisPackageTier === 'standard',
|
||||||
|
isStandardPlus: product.metadata?.analysisPackageTier === 'standard-plus',
|
||||||
|
isPremium: product.metadata?.analysisPackageTier === 'premium',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, [] as AnalysisPackageWithVariant[]);
|
||||||
|
|
||||||
|
return analysisPackagesWithVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function analysisPackagesLoader() {
|
||||||
|
const account = await loadCurrentUserAccount();
|
||||||
|
if (!account) {
|
||||||
|
throw new Error('Account not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return { analysisPackageElements, analysisPackages, countryCode };
|
const countryCodes = await loadCountryCodes();
|
||||||
|
const countryCode = countryCodes[0]!;
|
||||||
|
|
||||||
|
const analysisPackagesWithVariant = await analysisPackagesWithVariantLoader({ account, countryCode });
|
||||||
|
if (!analysisPackagesWithVariant) {
|
||||||
|
return { analysisPackageElements: [], analysisPackages: [], countryCode };
|
||||||
|
}
|
||||||
|
|
||||||
|
const analysisPackageElements = await analysisPackageElementsLoader({ analysisPackagesWithVariant, countryCode });
|
||||||
|
|
||||||
|
return { analysisPackageElements, analysisPackages: analysisPackagesWithVariant, countryCode };
|
||||||
}
|
}
|
||||||
export const loadAnalysisPackages = cache(analysisPackagesLoader);
|
export const loadAnalysisPackages = cache(analysisPackagesLoader);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export async function handleAddToCart({
|
|||||||
selectedVariant,
|
selectedVariant,
|
||||||
countryCode,
|
countryCode,
|
||||||
}: {
|
}: {
|
||||||
selectedVariant: StoreProductVariant
|
selectedVariant: Pick<StoreProductVariant, 'id'>
|
||||||
countryCode: string
|
countryCode: string
|
||||||
}) {
|
}) {
|
||||||
const supabase = getSupabaseServerClient();
|
const supabase = getSupabaseServerClient();
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import { useState } from 'react';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
import { StoreProduct, StoreProductVariant } from '@medusajs/types';
|
import { StoreProduct } from '@medusajs/types';
|
||||||
import { Button } from '@medusajs/ui';
|
import { Button } from '@medusajs/ui';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { handleAddToCart } from '../../../../lib/services/medusaCart.service';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -17,25 +18,24 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
} from '@kit/ui/card';
|
} from '@kit/ui/card';
|
||||||
import { Trans } from '@kit/ui/trans';
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
|
||||||
import { handleAddToCart } from '../../../../lib/services/medusaCart.service';
|
|
||||||
import { getAnalysisElementMedusaProductIds } from '../../../../utils/medusa-product';
|
|
||||||
import { PackageHeader } from './package-header';
|
|
||||||
import { ButtonTooltip } from './ui/button-tooltip';
|
import { ButtonTooltip } from './ui/button-tooltip';
|
||||||
|
import { PackageHeader } from './package-header';
|
||||||
|
|
||||||
export interface IAnalysisPackage {
|
export type AnalysisPackageWithVariant = Pick<StoreProduct, 'title' | 'description' | 'subtitle' | 'metadata'> & {
|
||||||
titleKey: string;
|
variantId: string;
|
||||||
|
nrOfAnalyses: number;
|
||||||
price: number;
|
price: number;
|
||||||
tagColor: string;
|
isStandard: boolean;
|
||||||
descriptionKey: string;
|
isStandardPlus: boolean;
|
||||||
}
|
isPremium: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export default function SelectAnalysisPackage({
|
export default function SelectAnalysisPackage({
|
||||||
analysisPackage,
|
analysisPackage,
|
||||||
countryCode,
|
countryCode,
|
||||||
}: {
|
}: {
|
||||||
analysisPackage: StoreProduct;
|
analysisPackage: AnalysisPackageWithVariant;
|
||||||
countryCode: string;
|
countryCode: string,
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const {
|
const {
|
||||||
@@ -44,35 +44,21 @@ export default function SelectAnalysisPackage({
|
|||||||
} = useTranslation();
|
} = useTranslation();
|
||||||
|
|
||||||
const [isAddingToCart, setIsAddingToCart] = useState(false);
|
const [isAddingToCart, setIsAddingToCart] = useState(false);
|
||||||
const handleSelect = async (selectedVariant: StoreProductVariant) => {
|
|
||||||
if (!selectedVariant?.id) return null;
|
const { nrOfAnalyses, variantId, title, subtitle = '', description = '', price } = analysisPackage;
|
||||||
|
|
||||||
|
const handleSelect = async () => {
|
||||||
setIsAddingToCart(true);
|
setIsAddingToCart(true);
|
||||||
await handleAddToCart({
|
await handleAddToCart({
|
||||||
selectedVariant,
|
selectedVariant: { id: variantId },
|
||||||
countryCode,
|
countryCode,
|
||||||
});
|
});
|
||||||
setIsAddingToCart(false);
|
setIsAddingToCart(false);
|
||||||
router.push('/home/cart');
|
router.push('/home/cart');
|
||||||
};
|
};
|
||||||
|
|
||||||
const titleKey = analysisPackage.title;
|
|
||||||
const analysisElementMedusaProductIds = getAnalysisElementMedusaProductIds([
|
|
||||||
analysisPackage,
|
|
||||||
]);
|
|
||||||
const nrOfAnalyses = analysisElementMedusaProductIds.length;
|
|
||||||
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 (
|
return (
|
||||||
<Card key={titleKey}>
|
<Card key={title}>
|
||||||
<CardHeader className="relative">
|
<CardHeader className="relative">
|
||||||
{description && (
|
{description && (
|
||||||
<ButtonTooltip
|
<ButtonTooltip
|
||||||
@@ -90,8 +76,8 @@ export default function SelectAnalysisPackage({
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-1 text-center">
|
<CardContent className="space-y-1 text-center">
|
||||||
<PackageHeader
|
<PackageHeader
|
||||||
title={t(titleKey)}
|
title={title}
|
||||||
tagColor="bg-cyan"
|
tagColor='bg-cyan'
|
||||||
analysesNr={t('product:nrOfAnalyses', { nr: nrOfAnalyses })}
|
analysesNr={t('product:nrOfAnalyses', { nr: nrOfAnalyses })}
|
||||||
language={language}
|
language={language}
|
||||||
price={price}
|
price={price}
|
||||||
@@ -99,14 +85,8 @@ export default function SelectAnalysisPackage({
|
|||||||
<CardDescription>{subtitle}</CardDescription>
|
<CardDescription>{subtitle}</CardDescription>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter>
|
<CardFooter>
|
||||||
<Button
|
<Button className="w-full text-[10px] sm:text-sm" onClick={handleSelect} isLoading={isAddingToCart}>
|
||||||
className="w-full text-[10px] sm:text-sm"
|
{!isAddingToCart && <Trans i18nKey='order-analysis-package:selectThisPackage' />}
|
||||||
onClick={() => handleSelect(variant)}
|
|
||||||
isLoading={isAddingToCart}
|
|
||||||
>
|
|
||||||
{!isAddingToCart && (
|
|
||||||
<Trans i18nKey="order-analysis-package:selectThisPackage" />
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
import { Trans } from '@kit/ui/trans';
|
import { Trans } from '@kit/ui/trans';
|
||||||
import { StoreProduct } from '@medusajs/types';
|
|
||||||
|
|
||||||
import SelectAnalysisPackage from './select-analysis-package';
|
import SelectAnalysisPackage, { AnalysisPackageWithVariant } from './select-analysis-package';
|
||||||
|
|
||||||
export default function SelectAnalysisPackages({ analysisPackages, countryCode }: { analysisPackages: StoreProduct[], countryCode: string }) {
|
export default function SelectAnalysisPackages({
|
||||||
|
analysisPackages,
|
||||||
|
countryCode,
|
||||||
|
}: {
|
||||||
|
analysisPackages: AnalysisPackageWithVariant[];
|
||||||
|
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) => (
|
(analysisPackage) => (
|
||||||
<SelectAnalysisPackage key={product.title} analysisPackage={product} countryCode={countryCode} />
|
<SelectAnalysisPackage key={analysisPackage.title} analysisPackage={analysisPackage} countryCode={countryCode} />
|
||||||
)) : (
|
)) : (
|
||||||
<h4>
|
<h4>
|
||||||
<Trans i18nKey="order-analysis-package:noPackagesAvailable" />
|
<Trans i18nKey="order-analysis-package:noPackagesAvailable" />
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
export const getAnalysisElementMedusaProductIds = (products: ({ metadata?: { analysisElementMedusaProductIds?: string } | null } | null)[]) => {
|
export const getAnalysisElementMedusaProductIds = (products: ({
|
||||||
|
metadata?: {
|
||||||
|
analysisElementMedusaProductIds?: string;
|
||||||
|
} | null;
|
||||||
|
} | null)[]) => {
|
||||||
if (!products) {
|
if (!products) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user