feat(MED-131): display analyses options and add to cart
This commit is contained in:
@@ -3,6 +3,8 @@ import { withI18n } from '~/lib/i18n/with-i18n';
|
|||||||
import { PageBody } from '@kit/ui/page';
|
import { PageBody } from '@kit/ui/page';
|
||||||
import { Trans } from '@kit/ui/trans';
|
import { Trans } from '@kit/ui/trans';
|
||||||
import { HomeLayoutPageHeader } from '../../_components/home-page-header';
|
import { HomeLayoutPageHeader } from '../../_components/home-page-header';
|
||||||
|
import { loadAnalyses } from '../../_lib/server/load-analyses';
|
||||||
|
import OrderAnalysesCards from '../../_components/order-analyses-cards';
|
||||||
|
|
||||||
export const generateMetadata = async () => {
|
export const generateMetadata = async () => {
|
||||||
const { t } = await createI18nServerInstance();
|
const { t } = await createI18nServerInstance();
|
||||||
@@ -13,6 +15,8 @@ export const generateMetadata = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function OrderAnalysisPage() {
|
async function OrderAnalysisPage() {
|
||||||
|
const { analyses, countryCode } = await loadAnalyses();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HomeLayoutPageHeader
|
<HomeLayoutPageHeader
|
||||||
@@ -20,6 +24,7 @@ async function OrderAnalysisPage() {
|
|||||||
description={<Trans i18nKey={'order-analysis:description'} />}
|
description={<Trans i18nKey={'order-analysis:description'} />}
|
||||||
/>
|
/>
|
||||||
<PageBody>
|
<PageBody>
|
||||||
|
<OrderAnalysesCards analyses={analyses} countryCode={countryCode} />
|
||||||
</PageBody>
|
</PageBody>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
76
app/home/(user)/_components/order-analyses-cards.tsx
Normal file
76
app/home/(user)/_components/order-analyses-cards.tsx
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { HeartPulse, ShoppingCart } from 'lucide-react';
|
||||||
|
|
||||||
|
import { Button } from '@kit/ui/button';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardFooter,
|
||||||
|
} from '@kit/ui/card';
|
||||||
|
import { StoreProduct, StoreProductVariant } from '@medusajs/types';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { handleAddToCart } from '~/lib/services/medusaCart.service';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
export default function OrderAnalysesCards({
|
||||||
|
analyses,
|
||||||
|
countryCode,
|
||||||
|
}: {
|
||||||
|
analyses: StoreProduct[];
|
||||||
|
countryCode: string;
|
||||||
|
}) {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-3 gap-6 mt-4">
|
||||||
|
{analyses.map(({
|
||||||
|
title,
|
||||||
|
variants
|
||||||
|
}) => (
|
||||||
|
<Card
|
||||||
|
key={title}
|
||||||
|
variant="gradient-success"
|
||||||
|
className="flex flex-col justify-between"
|
||||||
|
>
|
||||||
|
<CardHeader className="items-end-safe">
|
||||||
|
<div className='flex size-8 items-center-safe justify-center-safe rounded-full text-white bg-warning'>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="outline"
|
||||||
|
className="px-2 text-black"
|
||||||
|
onClick={() => handleSelect(variants![0]!)}
|
||||||
|
disabled={isAddingToCart}
|
||||||
|
>
|
||||||
|
<ShoppingCart className="size-4 stroke-2" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardFooter className="flex flex-col items-start gap-2">
|
||||||
|
<div
|
||||||
|
className={'flex size-8 items-center-safe justify-center-safe rounded-full text-white bg-primary\/10 mb-6'}
|
||||||
|
>
|
||||||
|
<HeartPulse className="size-4 fill-green-500" />
|
||||||
|
</div>
|
||||||
|
<h5>
|
||||||
|
{title}
|
||||||
|
</h5>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
app/home/(user)/_lib/server/load-analyses.ts
Normal file
41
app/home/(user)/_lib/server/load-analyses.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { cache } from 'react';
|
||||||
|
|
||||||
|
import { listProductTypes } from "@lib/data/products";
|
||||||
|
import { listRegions } from '@lib/data/regions';
|
||||||
|
import { getProductCategories } from '@lib/data/categories';
|
||||||
|
|
||||||
|
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 productCategoriesLoader() {
|
||||||
|
const productCategories = await getProductCategories({ fields: "*products, *products.variants" });
|
||||||
|
return productCategories.product_categories ?? [];
|
||||||
|
}
|
||||||
|
export const loadProductCategories = cache(productCategoriesLoader);
|
||||||
|
|
||||||
|
async function productTypesLoader() {
|
||||||
|
const { productTypes } = await listProductTypes();
|
||||||
|
return productTypes ?? [];
|
||||||
|
}
|
||||||
|
export const loadProductTypes = cache(productTypesLoader);
|
||||||
|
|
||||||
|
async function analysesLoader() {
|
||||||
|
const [countryCodes, productCategories] = await Promise.all([
|
||||||
|
loadCountryCodes(),
|
||||||
|
loadProductCategories(),
|
||||||
|
]);
|
||||||
|
const countryCode = countryCodes[0]!;
|
||||||
|
|
||||||
|
const category = productCategories.find(({ metadata }) => metadata?.page === 'order-analysis');
|
||||||
|
|
||||||
|
return {
|
||||||
|
analyses: category?.products ?? [],
|
||||||
|
countryCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const loadAnalyses = cache(analysesLoader);
|
||||||
@@ -27,8 +27,22 @@ export const listCategories = async (query?: Record<string, any>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getCategoryByHandle = async (categoryHandle: string[]) => {
|
export const getCategoryByHandle = async (categoryHandle: string[]) => {
|
||||||
const handle = `${categoryHandle.join("/")}`
|
const { product_categories } = await getProductCategories({
|
||||||
|
handle: `${categoryHandle.join("/")}`,
|
||||||
|
limit: 1,
|
||||||
|
});
|
||||||
|
return product_categories[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getProductCategories = async ({
|
||||||
|
handle,
|
||||||
|
limit,
|
||||||
|
fields = "*category_children, *products",
|
||||||
|
}: {
|
||||||
|
handle?: string;
|
||||||
|
limit?: number;
|
||||||
|
fields?: string;
|
||||||
|
} = {}) => {
|
||||||
const next = {
|
const next = {
|
||||||
...(await getCacheOptions("categories")),
|
...(await getCacheOptions("categories")),
|
||||||
}
|
}
|
||||||
@@ -38,12 +52,12 @@ export const getCategoryByHandle = async (categoryHandle: string[]) => {
|
|||||||
`/store/product-categories`,
|
`/store/product-categories`,
|
||||||
{
|
{
|
||||||
query: {
|
query: {
|
||||||
fields: "*category_children, *products",
|
fields,
|
||||||
handle,
|
handle,
|
||||||
|
limit,
|
||||||
},
|
},
|
||||||
next,
|
next,
|
||||||
cache: "force-cache",
|
//cache: "force-cache",
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
.then(({ product_categories }) => product_categories[0])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ export const listProductTypes = async (): Promise<{ productTypes: HttpTypes.Stor
|
|||||||
"/store/product-types",
|
"/store/product-types",
|
||||||
{
|
{
|
||||||
next,
|
next,
|
||||||
cache: "force-cache",
|
//cache: "force-cache",
|
||||||
query: {
|
query: {
|
||||||
fields: "id,value,metadata",
|
fields: "id,value,metadata",
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user