feat(MED-105): update order details redirect and shown page
This commit is contained in:
@@ -91,7 +91,7 @@ export async function processMontonioCallback(orderToken: string) {
|
||||
|
||||
const medusaOrder = await placeOrder(cartId, { revalidateCacheTags: false });
|
||||
const orderedAnalysisElements = await getOrderedAnalysisElementsIds({ medusaOrder });
|
||||
await createOrder({ medusaOrder, orderedAnalysisElements });
|
||||
const orderId = await createOrder({ medusaOrder, orderedAnalysisElements });
|
||||
|
||||
const { productTypes } = await listProductTypes();
|
||||
const analysisPackagesType = productTypes.find(({ metadata }) => metadata?.handle === ANALYSIS_PACKAGES_TYPE_HANDLE);
|
||||
@@ -122,7 +122,7 @@ export async function processMontonioCallback(orderToken: string) {
|
||||
// Send order to Medipost (no await to avoid blocking)
|
||||
sendOrderToMedipost({ medusaOrderId, orderedAnalysisElements });
|
||||
|
||||
return { success: true };
|
||||
return { success: true, orderId };
|
||||
} catch (error) {
|
||||
console.error("Failed to place order", error);
|
||||
throw new Error(`Failed to place order, message=${error}`);
|
||||
|
||||
@@ -29,8 +29,8 @@ export default function MontonioCallbackClient({ orderToken, error }: {
|
||||
setHasProcessed(true);
|
||||
|
||||
try {
|
||||
await processMontonioCallback(orderToken);
|
||||
router.push('/home/order');
|
||||
const { orderId } = await processMontonioCallback(orderToken);
|
||||
router.push(`/home/order/${orderId}/confirmed`);
|
||||
} catch (error) {
|
||||
console.error("Failed to place order", error);
|
||||
router.push('/home/cart/montonio-callback/error');
|
||||
|
||||
@@ -7,7 +7,6 @@ export default async function MontonioCallbackPage({ searchParams }: {
|
||||
}) {
|
||||
const orderToken = (await searchParams)['order-token'];
|
||||
|
||||
console.log('orderToken', orderToken);
|
||||
if (!orderToken) {
|
||||
return <MontonioCallbackClient error="Order token is missing" />;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { PageBody, PageHeader } from '@kit/ui/page';
|
||||
|
||||
import { retrieveOrder } from '~/medusa/lib/data/orders';
|
||||
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
|
||||
import OrderCompleted from '@/app/home/(user)/_components/order/order-completed';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ orderId: string }>;
|
||||
};
|
||||
import { getOrder } from '~/lib/services/order.service';
|
||||
import { retrieveOrder } from '@lib/data/orders';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
import Divider from "@modules/common/components/divider"
|
||||
import OrderDetails from '@/app/home/(user)/_components/order/order-details';
|
||||
import OrderItems from '@/app/home/(user)/_components/order/order-items';
|
||||
import CartTotals from '@/app/home/(user)/_components/order/cart-totals';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export async function generateMetadata() {
|
||||
const { t } = await createI18nServerInstance();
|
||||
@@ -17,15 +20,33 @@ export async function generateMetadata() {
|
||||
};
|
||||
}
|
||||
|
||||
async function OrderConfirmedPage(props: Props) {
|
||||
async function OrderConfirmedPage(props: {
|
||||
params: Promise<{ orderId: string }>;
|
||||
}) {
|
||||
const params = await props.params;
|
||||
const order = await retrieveOrder(params.orderId).catch(() => null);
|
||||
|
||||
const order = await getOrder({ orderId: Number(params.orderId) }).catch(() => null);
|
||||
if (!order) {
|
||||
return notFound();
|
||||
redirect(pathsConfig.app.myOrders);
|
||||
}
|
||||
|
||||
return <OrderCompleted order={order} />;
|
||||
const medusaOrder = await retrieveOrder(order.medusa_order_id).catch(() => null);
|
||||
if (!medusaOrder) {
|
||||
redirect(pathsConfig.app.myOrders);
|
||||
}
|
||||
|
||||
return (
|
||||
<PageBody>
|
||||
<PageHeader title={<Trans i18nKey="cart:orderConfirmed.title" />} />
|
||||
<Divider />
|
||||
<div className="grid grid-cols-1 small:grid-cols-[1fr_360px] gap-x-40 lg:px-4 gap-y-6">
|
||||
<OrderDetails order={order} />
|
||||
<Divider />
|
||||
<OrderItems medusaOrder={medusaOrder} />
|
||||
<CartTotals medusaOrder={medusaOrder} />
|
||||
</div>
|
||||
</PageBody>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n(OrderConfirmedPage);
|
||||
|
||||
52
app/home/(user)/(dashboard)/order/[orderId]/page.tsx
Normal file
52
app/home/(user)/(dashboard)/order/[orderId]/page.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { redirect } from 'next/navigation';
|
||||
import { PageBody, PageHeader } from '@kit/ui/page';
|
||||
|
||||
import { createI18nServerInstance } from '@/lib/i18n/i18n.server';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
import { getOrder } from '~/lib/services/order.service';
|
||||
import { retrieveOrder } from '@lib/data/orders';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
import Divider from "@modules/common/components/divider"
|
||||
import OrderDetails from '@/app/home/(user)/_components/order/order-details';
|
||||
import OrderItems from '@/app/home/(user)/_components/order/order-items';
|
||||
import CartTotals from '@/app/home/(user)/_components/order/cart-totals';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export async function generateMetadata() {
|
||||
const { t } = await createI18nServerInstance();
|
||||
|
||||
return {
|
||||
title: t('cart:order.title'),
|
||||
};
|
||||
}
|
||||
|
||||
async function OrderConfirmedPage(props: {
|
||||
params: Promise<{ orderId: string }>;
|
||||
}) {
|
||||
const params = await props.params;
|
||||
|
||||
const order = await getOrder({ orderId: Number(params.orderId) }).catch(() => null);
|
||||
if (!order) {
|
||||
redirect(pathsConfig.app.myOrders);
|
||||
}
|
||||
|
||||
const medusaOrder = await retrieveOrder(order.medusa_order_id).catch(() => null);
|
||||
if (!medusaOrder) {
|
||||
redirect(pathsConfig.app.myOrders);
|
||||
}
|
||||
|
||||
return (
|
||||
<PageBody>
|
||||
<PageHeader title={<Trans i18nKey="cart:order.title" />} />
|
||||
<Divider />
|
||||
<div className="grid grid-cols-1 small:grid-cols-[1fr_360px] gap-x-40 lg:px-4 gap-y-6">
|
||||
<OrderDetails order={order} />
|
||||
<Divider />
|
||||
<OrderItems medusaOrder={medusaOrder} />
|
||||
<CartTotals medusaOrder={medusaOrder} />
|
||||
</div>
|
||||
</PageBody>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n(OrderConfirmedPage);
|
||||
@@ -10,6 +10,7 @@ import { HomeLayoutPageHeader } from '../../_components/home-page-header';
|
||||
import OrdersTable from '../../_components/orders/orders-table';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
import type { IOrderLineItem } from '../../_components/orders/types';
|
||||
import { getOrders } from '~/lib/services/order.service';
|
||||
|
||||
export async function generateMetadata() {
|
||||
const { t } = await createI18nServerInstance();
|
||||
@@ -20,7 +21,8 @@ export async function generateMetadata() {
|
||||
}
|
||||
|
||||
async function OrdersPage() {
|
||||
const orders = await listOrders().catch(() => null);
|
||||
const orders = await listOrders();
|
||||
const localOrders = await getOrders();
|
||||
const { productTypes } = await listProductTypes();
|
||||
|
||||
if (!orders || !productTypes) {
|
||||
@@ -30,13 +32,38 @@ async function OrdersPage() {
|
||||
const analysisPackagesType = productTypes.find(({ metadata }) => metadata?.handle === 'analysis-packages');
|
||||
const analysisPackageOrders: IOrderLineItem[] = orders.flatMap(({ id, items, payment_status, fulfillment_status }) => items
|
||||
?.filter((item) => item.product_type_id === analysisPackagesType?.id)
|
||||
.map((item) => ({ item, orderId: id, orderStatus: `${payment_status}/${fulfillment_status}` }))
|
||||
.map((item) => {
|
||||
const localOrder = localOrders.find((order) => order.medusa_order_id === id);
|
||||
if (!localOrder) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
item,
|
||||
medusaOrderId: id,
|
||||
orderId: localOrder?.id,
|
||||
orderStatus: localOrder.status,
|
||||
analysis_element_ids: localOrder.analysis_element_ids,
|
||||
}
|
||||
})
|
||||
.filter((order) => order !== null)
|
||||
|| []);
|
||||
|
||||
const otherOrders: IOrderLineItem[] = orders
|
||||
.filter(({ items }) => items?.some((item) => item.product_type_id !== analysisPackagesType?.id))
|
||||
.flatMap(({ id, items, payment_status, fulfillment_status }) => items
|
||||
?.map((item) => ({ item, orderId: id, orderStatus: `${payment_status}/${fulfillment_status}` }))
|
||||
?.map((item) => {
|
||||
const localOrder = localOrders.find((order) => order.medusa_order_id === id);
|
||||
if (!localOrder) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
item,
|
||||
medusaOrderId: id,
|
||||
orderId: localOrder.id,
|
||||
orderStatus: localOrder.status,
|
||||
}
|
||||
})
|
||||
.filter((order) => order !== null)
|
||||
|| []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,8 +6,8 @@ import React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export default function CartTotals({ order }: {
|
||||
order: StoreOrder
|
||||
export default function CartTotals({ medusaOrder }: {
|
||||
medusaOrder: StoreOrder
|
||||
}) {
|
||||
const { i18n: { language } } = useTranslation()
|
||||
const {
|
||||
@@ -17,7 +17,7 @@ export default function CartTotals({ order }: {
|
||||
tax_total,
|
||||
discount_total,
|
||||
gift_card_total,
|
||||
} = order
|
||||
} = medusaOrder
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { PageBody, PageHeader } from '@kit/ui/page';
|
||||
import { StoreOrder } from "@medusajs/types"
|
||||
import Divider from "@modules/common/components/divider"
|
||||
|
||||
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" />} />
|
||||
<Divider />
|
||||
<div className="grid grid-cols-1 small:grid-cols-[1fr_360px] gap-x-40 lg:px-4 gap-y-6">
|
||||
<OrderDetails order={order} />
|
||||
<Divider />
|
||||
<OrderItems order={order} />
|
||||
<CartTotals order={order} />
|
||||
</div>
|
||||
</PageBody>
|
||||
)
|
||||
}
|
||||
@@ -1,47 +1,21 @@
|
||||
import { StoreOrder } from "@medusajs/types"
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { formatDate } from 'date-fns';
|
||||
import { AnalysisOrder } from "~/lib/services/order.service";
|
||||
|
||||
export default function OrderDetails({ order, showStatus }: {
|
||||
order: StoreOrder
|
||||
showStatus?: boolean
|
||||
export default function OrderDetails({ order }: {
|
||||
order: AnalysisOrder
|
||||
}) {
|
||||
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()}
|
||||
{formatDate(order.created_at, 'dd.MM.yyyy HH:mm')}
|
||||
</span>
|
||||
</span>
|
||||
<span className="text-ui-fg-interactive">
|
||||
<Trans i18nKey="cart:orderConfirmed.orderNumber" />: <span data-testid="order-id">{order.display_id}</span>
|
||||
<Trans i18nKey="cart:orderConfirmed.orderNumber" />: <span data-testid="order-id">{order.medusa_order_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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { StoreCartLineItem, StoreOrderLineItem } from "@medusajs/types"
|
||||
import { TableCell, TableRow } from "@kit/ui/table"
|
||||
|
||||
import LineItemOptions from "@modules/common/components/line-item-options"
|
||||
// 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"
|
||||
|
||||
@@ -9,6 +9,7 @@ export default function OrderItem({ item, currencyCode }: {
|
||||
item: StoreCartLineItem | StoreOrderLineItem
|
||||
currencyCode: string
|
||||
}) {
|
||||
const partnerLocationName = item.metadata?.partner_location_name;
|
||||
return (
|
||||
<TableRow className="w-full" data-testid="product-row">
|
||||
{/* <TableCell className="px-6 w-24">
|
||||
@@ -22,9 +23,9 @@ export default function OrderItem({ item, currencyCode }: {
|
||||
className="txt-medium-plus text-ui-fg-base"
|
||||
data-testid="product-name"
|
||||
>
|
||||
{item.product_title}{` (${item.metadata?.partner_location_name ?? "-"})`}
|
||||
{item.product_title}{` ${partnerLocationName ? `(${partnerLocationName})` : ''}`}
|
||||
</span>
|
||||
<LineItemOptions variant={item.variant} data-testid="product-variant" />
|
||||
{/* <LineItemOptions variant={item.variant} data-testid="product-variant" /> */}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="px-6">
|
||||
|
||||
@@ -7,10 +7,10 @@ import OrderItem from "./order-item"
|
||||
import { Heading } from "@kit/ui/heading"
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export default function OrderItems({ order }: {
|
||||
order: StoreOrder
|
||||
export default function OrderItems({ medusaOrder }: {
|
||||
medusaOrder: StoreOrder
|
||||
}) {
|
||||
const items = order.items
|
||||
const items = medusaOrder.items
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-4">
|
||||
@@ -27,7 +27,7 @@ export default function OrderItems({ order }: {
|
||||
<OrderItem
|
||||
key={item.id}
|
||||
item={item}
|
||||
currencyCode={order.currency_code}
|
||||
currencyCode={medusaOrder.currency_code}
|
||||
/>
|
||||
))
|
||||
: repeat(5).map((i) => <SkeletonLineItem key={i} />)}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Eye } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { formatDate } from "date-fns";
|
||||
import { IOrderLineItem } from "./types";
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export default function OrdersItem({ orderItem }: {
|
||||
orderItem: IOrderLineItem,
|
||||
@@ -22,15 +23,13 @@ export default function OrdersItem({ orderItem }: {
|
||||
{formatDate(orderItem.item.created_at, 'dd.MM.yyyy HH:mm')}
|
||||
</TableCell>
|
||||
|
||||
{orderItem.orderStatus && (
|
||||
<TableCell className="px-6">
|
||||
{orderItem.orderStatus}
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell className="px-6 whitespace-nowrap">
|
||||
<Trans i18nKey={`orders:status.${orderItem.orderStatus}`} />
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="text-right px-6">
|
||||
<span className="flex gap-x-1 justify-end w-[60px]">
|
||||
<Link href={`/home/analysis-results`} className="flex items-center justify-between text-small-regular">
|
||||
<Link href={`/home/order/${orderItem.orderId}`} className="flex items-center justify-between text-small-regular">
|
||||
<button
|
||||
className="flex gap-x-1 text-ui-fg-subtle hover:text-ui-fg-base cursor-pointer"
|
||||
>
|
||||
|
||||
@@ -9,8 +9,6 @@ import {
|
||||
import OrdersItem from "./orders-item";
|
||||
import { IOrderLineItem } from "./types";
|
||||
|
||||
const IS_SHOWN_ORDER_STATUS = true as boolean;
|
||||
|
||||
export default function OrdersTable({ orderItems, title }: {
|
||||
orderItems: IOrderLineItem[];
|
||||
title: string;
|
||||
@@ -29,10 +27,9 @@ export default function OrdersTable({ orderItems, title }: {
|
||||
<TableHead className="px-6">
|
||||
<Trans i18nKey="orders:table.createdAt" />
|
||||
</TableHead>
|
||||
{IS_SHOWN_ORDER_STATUS && (
|
||||
<TableHead className="px-6">
|
||||
</TableHead>
|
||||
)}
|
||||
<TableHead className="px-6">
|
||||
<Trans i18nKey="orders:table.status" />
|
||||
</TableHead>
|
||||
<TableHead className="px-6">
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { StoreOrderLineItem } from "@medusajs/types";
|
||||
|
||||
export interface IOrderLineItem {
|
||||
item: StoreOrderLineItem;
|
||||
orderId: string;
|
||||
medusaOrderId: string;
|
||||
orderId: number;
|
||||
orderStatus: string;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import type { Tables } from '@kit/supabase/database';
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
import type { StoreOrder } from '@medusajs/types';
|
||||
|
||||
export type AnalysisOrder = Tables<{ schema: 'medreport' }, 'analysis_orders'>;
|
||||
|
||||
export async function createOrder({
|
||||
medusaOrder,
|
||||
orderedAnalysisElements,
|
||||
@@ -32,6 +34,8 @@ export async function createOrder({
|
||||
if (orderResult.error || !orderResult.data?.id) {
|
||||
throw new Error(`Failed to create order, message=${orderResult.error}, data=${JSON.stringify(orderResult)}`);
|
||||
}
|
||||
|
||||
return orderResult.data.id;
|
||||
}
|
||||
|
||||
export async function updateOrder({
|
||||
@@ -56,14 +60,22 @@ export async function updateOrder({
|
||||
|
||||
export async function getOrder({
|
||||
medusaOrderId,
|
||||
orderId,
|
||||
}: {
|
||||
medusaOrderId: string;
|
||||
medusaOrderId?: string;
|
||||
orderId?: number;
|
||||
}) {
|
||||
const query = getSupabaseServerAdminClient()
|
||||
.schema('medreport')
|
||||
.from('analysis_orders')
|
||||
.select('*')
|
||||
.eq('medusa_order_id', medusaOrderId)
|
||||
if (medusaOrderId) {
|
||||
query.eq('medusa_order_id', medusaOrderId);
|
||||
} else if (orderId) {
|
||||
query.eq('id', orderId);
|
||||
} else {
|
||||
throw new Error('Either medusaOrderId or orderId must be provided');
|
||||
}
|
||||
|
||||
const { data: order } = await query.single().throwOnError();
|
||||
return order;
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
"error": "Failed to update location"
|
||||
}
|
||||
},
|
||||
"order": {
|
||||
"title": "Order"
|
||||
},
|
||||
"orderConfirmed": {
|
||||
"title": "Order confirmed",
|
||||
"summary": "Summary",
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
"table": {
|
||||
"analysisPackage": "Analysis package",
|
||||
"otherOrders": "Order",
|
||||
"createdAt": "Ordered at"
|
||||
"createdAt": "Ordered at",
|
||||
"status": "Status"
|
||||
},
|
||||
"status": {
|
||||
"QUEUED": "Waiting to send to lab",
|
||||
"ON_HOLD": "Waiting for analysis results",
|
||||
"PROCESSING": "In progress",
|
||||
"COMPLETED": "Completed",
|
||||
"REJECTED": "Rejected",
|
||||
"CANCELLED": "Cancelled"
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,9 @@
|
||||
"error": "Asukoha uuendamine ebaõnnestus"
|
||||
}
|
||||
},
|
||||
"order": {
|
||||
"title": "Tellimus"
|
||||
},
|
||||
"orderConfirmed": {
|
||||
"title": "Tellimus on edukalt esitatud",
|
||||
"summary": "Teenused",
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
"table": {
|
||||
"analysisPackage": "Analüüsi pakett",
|
||||
"otherOrders": "Tellimus",
|
||||
"createdAt": "Tellitud"
|
||||
"createdAt": "Tellitud",
|
||||
"status": "Olek"
|
||||
},
|
||||
"status": {
|
||||
"QUEUED": "Ootab saatekirja saatmist",
|
||||
"ON_HOLD": "Ootab analüüsi tulemusi",
|
||||
"PROCESSING": "Töötlemisel",
|
||||
"COMPLETED": "Valmis",
|
||||
"REJECTED": "Tühistatud",
|
||||
"CANCELLED": "Tühistatud"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user