Skip to content

Commit a0df5be

Browse files
feat(dashboard): Implement testing of shipping methods (#3833)
1 parent 1db50a6 commit a0df5be

17 files changed

+1190
-42
lines changed

packages/dashboard/src/app/routes/_authenticated/_orders/utils/order-detail-loaders.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { redirect } from '@tanstack/react-router';
66
import { OrderDetail } from '../components/order-detail-shared.js';
77
import { orderDetailDocument } from '../orders.graphql.js';
88

9-
export async function commonRegularOrderLoader(context: any, params: { id: string }): Promise<OrderDetail> {
9+
async function ensureOrderWithIdExists(context: any, params: { id: string }): Promise<OrderDetail> {
1010
if (!params.id) {
1111
throw new Error('ID param is required');
1212
}
@@ -18,13 +18,18 @@ export async function commonRegularOrderLoader(context: any, params: { id: strin
1818
if (!result.order) {
1919
throw new Error(`Order with the ID ${params.id} was not found`);
2020
}
21+
return result.order;
22+
}
2123

22-
if (result.order.state === 'Draft') {
24+
export async function commonRegularOrderLoader(context: any, params: { id: string }): Promise<OrderDetail> {
25+
const order = await ensureOrderWithIdExists(context, params);
26+
27+
if (order.state === 'Draft') {
2328
throw redirect({
2429
to: `/orders/draft/${params.id}`,
2530
});
2631
}
27-
return result.order;
32+
return order;
2833
}
2934

3035
export async function loadRegularOrder(context: any, params: { id: string }) {
@@ -42,7 +47,7 @@ export async function loadRegularOrder(context: any, params: { id: string }) {
4247
}
4348

4449
export async function loadDraftOrder(context: any, params: { id: string }) {
45-
const order = await commonRegularOrderLoader(context, params);
50+
const order = await ensureOrderWithIdExists(context, params);
4651

4752
if (order.state !== 'Draft') {
4853
throw redirect({
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Badge } from '@/vdb/components/ui/badge.js';
2+
import React from 'react';
3+
4+
export function MetadataBadges({ metadata }: Readonly<{ metadata?: Record<string, any> }>) {
5+
if (!metadata || Object.keys(metadata).length === 0) return null;
6+
return (
7+
<div className="mt-2 flex flex-wrap gap-1">
8+
{Object.entries(metadata).map(([key, value]) => (
9+
<Badge key={key} variant="outline" className="text-xs">
10+
{key}: {String(value)}
11+
</Badge>
12+
))}
13+
</div>
14+
);
15+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Money } from '@/vdb/components/data-display/money.js';
2+
import { Trans } from '@/vdb/lib/trans.js';
3+
4+
export function PriceDisplay({
5+
price,
6+
priceWithTax,
7+
currencyCode,
8+
}: Readonly<{
9+
price: number;
10+
priceWithTax: number;
11+
currencyCode: string;
12+
}>) {
13+
return (
14+
<div className="text-right">
15+
<Money value={priceWithTax} currency={currencyCode} />
16+
<div className="text-xs text-muted-foreground">
17+
<Trans>ex. tax:</Trans> <Money value={price} currency={currencyCode} />
18+
</div>
19+
</div>
20+
);
21+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { Alert, AlertDescription } from '@/vdb/components/ui/alert.js';
2+
import { Button } from '@/vdb/components/ui/button.js';
3+
import { Card, CardContent, CardHeader, CardTitle } from '@/vdb/components/ui/card.js';
4+
import { Trans } from '@/vdb/lib/trans.js';
5+
import { PlayIcon } from 'lucide-react';
6+
import React from 'react';
7+
8+
interface ShippingMethodTestResultWrapperProps {
9+
okToRun: boolean;
10+
testDataUpdated: boolean;
11+
hasTestedOnce: boolean;
12+
onRunTest: () => void;
13+
loading?: boolean;
14+
children: React.ReactNode;
15+
emptyState?: React.ReactNode;
16+
showEmptyState?: boolean;
17+
runTestLabel?: React.ReactNode;
18+
loadingLabel?: React.ReactNode;
19+
}
20+
21+
export function ShippingMethodTestResultWrapper({
22+
okToRun,
23+
testDataUpdated,
24+
hasTestedOnce,
25+
onRunTest,
26+
loading = false,
27+
children,
28+
emptyState,
29+
showEmptyState = false,
30+
runTestLabel = <Trans>Run Test</Trans>,
31+
loadingLabel = <Trans>Testing shipping method...</Trans>,
32+
}: Readonly<ShippingMethodTestResultWrapperProps>) {
33+
const canRunTest = okToRun && testDataUpdated;
34+
return (
35+
<Card>
36+
<CardHeader>
37+
<CardTitle className="flex items-center justify-between">
38+
<span>
39+
<Trans>Test Results</Trans>
40+
</span>
41+
{okToRun && (
42+
<Button
43+
onClick={onRunTest}
44+
disabled={!canRunTest || loading}
45+
size="sm"
46+
className="ml-auto"
47+
>
48+
<PlayIcon className="mr-1 h-4 w-4" />
49+
{runTestLabel}
50+
</Button>
51+
)}
52+
</CardTitle>
53+
</CardHeader>
54+
<CardContent>
55+
{!okToRun && (
56+
<Alert>
57+
<AlertDescription>
58+
<Trans>
59+
Please add products and complete the shipping address to run the test.
60+
</Trans>
61+
</AlertDescription>
62+
</Alert>
63+
)}
64+
65+
{okToRun && testDataUpdated && hasTestedOnce && (
66+
<Alert variant="destructive">
67+
<AlertDescription>
68+
<Trans>
69+
Test data has been updated. Click "Run Test" to see updated results.
70+
</Trans>
71+
</AlertDescription>
72+
</Alert>
73+
)}
74+
75+
{loading && (
76+
<div className="text-center py-8">
77+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
78+
<p className="mt-2 text-sm text-muted-foreground">{loadingLabel}</p>
79+
</div>
80+
)}
81+
82+
{!loading && showEmptyState && emptyState}
83+
{!loading && !showEmptyState && children}
84+
</CardContent>
85+
</Card>
86+
);
87+
}

0 commit comments

Comments
 (0)