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 { Trans } from '@kit/ui/trans';
|
||||
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 () => {
|
||||
const { t } = await createI18nServerInstance();
|
||||
@@ -13,6 +15,8 @@ export const generateMetadata = async () => {
|
||||
};
|
||||
|
||||
async function OrderAnalysisPage() {
|
||||
const { analyses, countryCode } = await loadAnalyses();
|
||||
|
||||
return (
|
||||
<>
|
||||
<HomeLayoutPageHeader
|
||||
@@ -20,6 +24,7 @@ async function OrderAnalysisPage() {
|
||||
description={<Trans i18nKey={'order-analysis:description'} />}
|
||||
/>
|
||||
<PageBody>
|
||||
<OrderAnalysesCards analyses={analyses} countryCode={countryCode} />
|
||||
</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[]) => {
|
||||
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 = {
|
||||
...(await getCacheOptions("categories")),
|
||||
}
|
||||
@@ -38,12 +52,12 @@ export const getCategoryByHandle = async (categoryHandle: string[]) => {
|
||||
`/store/product-categories`,
|
||||
{
|
||||
query: {
|
||||
fields: "*category_children, *products",
|
||||
fields,
|
||||
handle,
|
||||
limit,
|
||||
},
|
||||
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",
|
||||
{
|
||||
next,
|
||||
cache: "force-cache",
|
||||
//cache: "force-cache",
|
||||
query: {
|
||||
fields: "id,value,metadata",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user