feat(MED-100): update cart checkout flow and views

This commit is contained in:
2025-07-17 10:16:52 +03:00
parent ea3fb22f1d
commit 6426e2a79b
33 changed files with 1505 additions and 138 deletions

View File

@@ -0,0 +1,82 @@
"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><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>
)
}

View File

@@ -0,0 +1,24 @@
import { Trans } from '@kit/ui/trans';
import { PageBody, PageHeader } from '@kit/ui/page';
import { StoreOrder } from "@medusajs/types"
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" />} />
<div className="grid grid-cols-1 small:grid-cols-[1fr_360px] gap-x-40 lg:px-4 gap-y-6">
<OrderDetails order={order} />
<OrderItems order={order} />
<CartTotals order={order} />
</div>
</PageBody>
)
}

View File

@@ -0,0 +1,47 @@
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>
)
}

View File

@@ -0,0 +1,52 @@
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="!pl-0 p-4 w-24">
<div className="flex w-16">
<Thumbnail thumbnail={item.thumbnail} size="square" />
</div>
</TableCell> */}
<TableCell className="text-left">
<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="!pr-0">
<span className="!pr-0 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>
)
}

View File

@@ -0,0 +1,41 @@
import repeat from "@lib/util/repeat"
import { StoreOrder } from "@medusajs/types"
import { Table, TableBody } from "@kit/ui/table"
import Divider from "@modules/common/components/divider"
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">
<Divider className="!mb-0" />
<Table>
<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>
)
}