@vendure/dashboard 3.2.2 → 3.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/plugin/utils/ast-utils.d.ts +10 -0
  2. package/dist/plugin/utils/ast-utils.js +96 -0
  3. package/dist/plugin/utils/ast-utils.spec.d.ts +1 -0
  4. package/dist/plugin/utils/ast-utils.spec.js +120 -0
  5. package/dist/plugin/{config-loader.d.ts → utils/config-loader.d.ts} +22 -8
  6. package/dist/plugin/utils/config-loader.js +325 -0
  7. package/dist/plugin/{schema-generator.d.ts → utils/schema-generator.d.ts} +5 -0
  8. package/dist/plugin/{schema-generator.js → utils/schema-generator.js} +6 -0
  9. package/dist/plugin/{ui-config.js → utils/ui-config.js} +2 -2
  10. package/dist/plugin/vite-plugin-admin-api-schema.js +2 -2
  11. package/dist/plugin/vite-plugin-config-loader.d.ts +2 -3
  12. package/dist/plugin/vite-plugin-config-loader.js +18 -9
  13. package/dist/plugin/vite-plugin-dashboard-metadata.js +12 -14
  14. package/dist/plugin/vite-plugin-gql-tada.js +2 -2
  15. package/dist/plugin/vite-plugin-ui-config.js +3 -2
  16. package/package.json +8 -6
  17. package/src/app/app-providers.tsx +8 -8
  18. package/src/app/main.tsx +1 -1
  19. package/src/app/routes/_authenticated/_assets/assets.graphql.ts +26 -0
  20. package/src/app/routes/_authenticated/_assets/assets.tsx +2 -2
  21. package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +156 -0
  22. package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +104 -0
  23. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +228 -0
  24. package/src/app/routes/_authenticated/_orders/components/money-gross-net.tsx +18 -0
  25. package/src/app/routes/_authenticated/_orders/components/order-address.tsx +2 -1
  26. package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +38 -0
  27. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +53 -0
  28. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +8 -49
  29. package/src/app/routes/_authenticated/_orders/components/shipping-method-selector.tsx +65 -0
  30. package/src/app/routes/_authenticated/_orders/orders.graphql.ts +187 -1
  31. package/src/app/routes/_authenticated/_orders/orders.tsx +39 -18
  32. package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +31 -9
  33. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +418 -0
  34. package/src/app/routes/_authenticated/_products/products.tsx +1 -1
  35. package/src/app/routes/_authenticated.tsx +12 -1
  36. package/src/lib/components/data-table/add-filter-menu.tsx +61 -0
  37. package/src/lib/components/data-table/data-table-column-header.tsx +0 -13
  38. package/src/lib/components/data-table/data-table-filter-badge.tsx +75 -0
  39. package/src/lib/components/data-table/data-table-filter-dialog.tsx +27 -28
  40. package/src/lib/components/data-table/data-table-types.ts +1 -0
  41. package/src/lib/components/data-table/data-table-view-options.tsx +72 -23
  42. package/src/lib/components/data-table/data-table.tsx +23 -24
  43. package/src/lib/components/data-table/filters/data-table-boolean-filter.tsx +57 -0
  44. package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +93 -0
  45. package/src/lib/components/data-table/filters/data-table-id-filter.tsx +58 -0
  46. package/src/lib/components/data-table/filters/data-table-number-filter.tsx +119 -0
  47. package/src/lib/components/data-table/filters/data-table-string-filter.tsx +62 -0
  48. package/src/lib/components/data-table/human-readable-operator.tsx +65 -0
  49. package/src/lib/components/layout/nav-user.tsx +4 -4
  50. package/src/lib/components/shared/asset/asset-focal-point-editor.tsx +93 -0
  51. package/src/lib/components/shared/{asset-gallery.tsx → asset/asset-gallery.tsx} +51 -20
  52. package/src/lib/components/shared/{asset-picker-dialog.tsx → asset/asset-picker-dialog.tsx} +1 -1
  53. package/src/lib/components/shared/{asset-preview-dialog.tsx → asset/asset-preview-dialog.tsx} +1 -7
  54. package/src/lib/components/shared/asset/asset-preview-selector.tsx +34 -0
  55. package/src/lib/components/shared/asset/asset-preview.tsx +128 -0
  56. package/src/lib/components/shared/asset/asset-properties.tsx +46 -0
  57. package/src/lib/components/shared/{focal-point-control.tsx → asset/focal-point-control.tsx} +1 -1
  58. package/src/lib/components/shared/custom-fields-form.tsx +4 -3
  59. package/src/lib/components/shared/customer-selector.tsx +13 -14
  60. package/src/lib/components/shared/detail-page-button.tsx +2 -2
  61. package/src/lib/components/shared/entity-assets.tsx +3 -3
  62. package/src/lib/components/shared/navigation-confirmation.tsx +39 -0
  63. package/src/lib/components/shared/paginated-list-data-table.tsx +9 -1
  64. package/src/lib/components/shared/product-variant-selector.tsx +111 -0
  65. package/src/lib/components/shared/vendure-image.tsx +1 -1
  66. package/src/lib/components/ui/calendar.tsx +508 -63
  67. package/src/lib/framework/document-introspection/get-document-structure.spec.ts +113 -3
  68. package/src/lib/framework/document-introspection/get-document-structure.ts +70 -11
  69. package/src/lib/framework/form-engine/use-generated-form.tsx +8 -7
  70. package/src/lib/framework/layout-engine/page-layout.tsx +4 -0
  71. package/src/lib/framework/page/list-page.tsx +23 -4
  72. package/src/lib/framework/page/use-detail-page.ts +1 -0
  73. package/src/lib/graphql/fragments.tsx +8 -0
  74. package/src/lib/index.ts +5 -5
  75. package/src/lib/providers/auth.tsx +12 -9
  76. package/src/lib/providers/channel-provider.tsx +1 -0
  77. package/src/lib/providers/server-config.tsx +7 -1
  78. package/src/lib/providers/user-settings.tsx +24 -0
  79. package/vite/utils/ast-utils.spec.ts +128 -0
  80. package/vite/utils/ast-utils.ts +119 -0
  81. package/vite/utils/config-loader.ts +410 -0
  82. package/vite/{schema-generator.ts → utils/schema-generator.ts} +7 -1
  83. package/vite/{ui-config.ts → utils/ui-config.ts} +2 -2
  84. package/vite/vite-plugin-admin-api-schema.ts +2 -2
  85. package/vite/vite-plugin-config-loader.ts +25 -13
  86. package/vite/vite-plugin-dashboard-metadata.ts +19 -15
  87. package/vite/vite-plugin-gql-tada.ts +2 -2
  88. package/vite/vite-plugin-ui-config.ts +3 -2
  89. package/dist/plugin/config-loader.js +0 -141
  90. package/src/lib/components/shared/asset-preview.tsx +0 -345
  91. package/vite/config-loader.ts +0 -181
  92. /package/dist/plugin/{ui-config.d.ts → utils/ui-config.d.ts} +0 -0
@@ -0,0 +1,228 @@
1
+ import { ProductVariantSelector } from '@/components/shared/product-variant-selector.js';
2
+ import { VendureImage } from '@/components/shared/vendure-image.js';
3
+ import { Button } from '@/components/ui/button.js';
4
+ import { Input } from '@/components/ui/input.js';
5
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table.js';
6
+ import { ResultOf } from '@/graphql/graphql.js';
7
+ import { Trans } from '@/lib/trans.js';
8
+ import {
9
+ ColumnDef,
10
+ flexRender,
11
+ getCoreRowModel,
12
+ useReactTable,
13
+ VisibilityState,
14
+ } from '@tanstack/react-table';
15
+ import { Trash2 } from 'lucide-react';
16
+ import { useState } from 'react';
17
+ import { draftOrderEligibleShippingMethodsDocument, orderDetailDocument, orderLineFragment } from '../orders.graphql.js';
18
+ import { MoneyGrossNet } from './money-gross-net.js';
19
+ import { OrderTableTotals } from './order-table-totals.js';
20
+ import { ShippingMethodSelector } from './shipping-method-selector.js';
21
+ import { OrderLineCustomFieldsForm } from './order-line-custom-fields-form.js';
22
+ import { UseFormReturn } from 'react-hook-form';
23
+
24
+ type OrderFragment = NonNullable<ResultOf<typeof orderDetailDocument>['order']>;
25
+ type OrderLineFragment = ResultOf<typeof orderLineFragment>;
26
+
27
+ type ShippingMethodQuote = ResultOf<typeof draftOrderEligibleShippingMethodsDocument>['eligibleShippingMethodsForDraftOrder'][number];
28
+
29
+ export interface OrderTableProps {
30
+ order: OrderFragment;
31
+ eligibleShippingMethods: ShippingMethodQuote[];
32
+ onAddItem: (event: { productVariantId: string; }) => void;
33
+ onAdjustLine: (event: { lineId: string; quantity: number; customFields: Record<string, any> }) => void;
34
+ onRemoveLine: (event: { lineId: string }) => void;
35
+ onSetShippingMethod: (event: { shippingMethodId: string }) => void;
36
+ onApplyCouponCode: (event: { couponCode: string }) => void;
37
+ onRemoveCouponCode: (event: { couponCode: string }) => void;
38
+ orderLineForm: UseFormReturn<any>;
39
+ }
40
+
41
+ export function EditOrderTable({ order, eligibleShippingMethods, onAddItem, onAdjustLine, onRemoveLine,
42
+ onSetShippingMethod, onApplyCouponCode, onRemoveCouponCode, orderLineForm }: OrderTableProps) {
43
+
44
+ const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
45
+ const [couponCode, setCouponCode] = useState('');
46
+
47
+ const currencyCode = order.currencyCode;
48
+
49
+ const columns: ColumnDef<OrderLineFragment>[] = [
50
+ {
51
+ header: 'Image',
52
+ accessorKey: 'featuredAsset',
53
+ cell: ({ row }) => {
54
+ const asset = row.original.featuredAsset;
55
+ return <VendureImage asset={asset} preset="tiny" />;
56
+ },
57
+ },
58
+ {
59
+ header: 'Product',
60
+ accessorKey: 'productVariant.name',
61
+ },
62
+ {
63
+ header: 'SKU',
64
+ accessorKey: 'productVariant.sku',
65
+ },
66
+ {
67
+ header: 'Unit price',
68
+ accessorKey: 'unitPriceWithTax',
69
+ cell: ({ row }) => {
70
+ const value = row.original.unitPriceWithTax
71
+ const netValue = row.original.unitPrice;
72
+ return <MoneyGrossNet priceWithTax={value} price={netValue} currencyCode={currencyCode} />
73
+ },
74
+ },
75
+ {
76
+ header: 'Quantity',
77
+ accessorKey: 'quantity',
78
+ cell: ({ row }) => {
79
+ return <div className="flex gap-2">
80
+ <Input type="number" value={row.original.quantity} onChange={e => onAdjustLine({ lineId: row.original.id, quantity: e.target.valueAsNumber, customFields: row.original.customFields })} />
81
+ <Button variant="outline" type="button" size="icon" onClick={() => onRemoveLine({ lineId: row.original.id })}>
82
+ <Trash2 />
83
+ </Button>
84
+ {row.original.customFields &&
85
+ <OrderLineCustomFieldsForm onUpdate={(customFields) => {
86
+
87
+ onAdjustLine({
88
+ lineId: row.original.id,
89
+ quantity: row.original.quantity,
90
+ customFields: customFields
91
+ });
92
+ }} form={orderLineForm} />}
93
+ </div>;
94
+ },
95
+ },
96
+ {
97
+ header: 'Total',
98
+ accessorKey: 'linePriceWithTax',
99
+ cell: ({ cell, row }) => {
100
+ const value = row.original.linePriceWithTax;
101
+ const netValue = row.original.linePrice;
102
+ return <MoneyGrossNet priceWithTax={value} price={netValue} currencyCode={currencyCode} />;
103
+ },
104
+ },
105
+ ];
106
+
107
+ const data = order.lines;
108
+
109
+ const table = useReactTable({
110
+ data,
111
+ columns,
112
+ getCoreRowModel: getCoreRowModel(),
113
+ rowCount: data.length,
114
+ onColumnVisibilityChange: setColumnVisibility,
115
+ state: {
116
+ columnVisibility,
117
+ },
118
+ });
119
+
120
+ return (
121
+ <div className="w-full">
122
+ <div className="">
123
+ <Table>
124
+ <TableHeader>
125
+ {table.getHeaderGroups().map(headerGroup => (
126
+ <TableRow key={headerGroup.id}>
127
+ {headerGroup.headers.map(header => {
128
+ return (
129
+ <TableHead key={header.id}>
130
+ {header.isPlaceholder
131
+ ? null
132
+ : flexRender(
133
+ header.column.columnDef.header,
134
+ header.getContext(),
135
+ )}
136
+ </TableHead>
137
+ );
138
+ })}
139
+ </TableRow>
140
+ ))}
141
+ </TableHeader>
142
+ <TableBody>
143
+ {table.getRowModel().rows?.length ? (
144
+ table.getRowModel().rows.map(row => (
145
+ <TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
146
+ {row.getVisibleCells().map(cell => (
147
+ <TableCell key={cell.id}>
148
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
149
+ </TableCell>
150
+ ))}
151
+ </TableRow>
152
+ ))
153
+ ) : null}
154
+ <TableRow>
155
+ <TableCell colSpan={columns.length} className="h-12">
156
+ <div className="my-4 flex justify-center">
157
+ <div className="max-w-lg">
158
+ <ProductVariantSelector onProductVariantIdChange={variantId => {
159
+ onAddItem({ productVariantId: variantId });
160
+ }} />
161
+ </div>
162
+ </div>
163
+ </TableCell>
164
+ </TableRow>
165
+ <TableRow>
166
+ <TableCell colSpan={columns.length} className="h-12">
167
+ <ShippingMethodSelector
168
+ eligibleShippingMethods={eligibleShippingMethods}
169
+ selectedShippingMethodId={order.shippingLines?.[0]?.shippingMethod?.id}
170
+ currencyCode={currencyCode}
171
+ onSelect={(shippingMethodId) => onSetShippingMethod({ shippingMethodId })}
172
+ />
173
+ </TableCell>
174
+ </TableRow>
175
+ <TableRow>
176
+ <TableCell colSpan={columns.length} className="h-12">
177
+ <div className="flex flex-col gap-4">
178
+ <div className="flex gap-2">
179
+ <Input
180
+ type="text"
181
+ placeholder="Coupon code"
182
+ value={couponCode}
183
+ onChange={(e) => setCouponCode(e.target.value)}
184
+ onKeyDown={(e) => {
185
+ if (e.key === 'Enter') {
186
+ onApplyCouponCode({ couponCode });
187
+ }
188
+ }}
189
+ />
190
+ <Button
191
+ type="button"
192
+ onClick={() => onApplyCouponCode({ couponCode })}
193
+ disabled={!couponCode}
194
+ >
195
+ <Trans>Apply</Trans>
196
+ </Button>
197
+ </div>
198
+ {order.couponCodes?.length > 0 && (
199
+ <div className="flex flex-wrap gap-2">
200
+ {order.couponCodes.map((code) => (
201
+ <div
202
+ key={code}
203
+ className="flex items-center gap-2 px-3 py-1 text-sm border rounded-md"
204
+ >
205
+ <span>{code}</span>
206
+ <Button
207
+ type="button"
208
+ variant="ghost"
209
+ size="sm"
210
+ className="h-6 w-6 p-0"
211
+ onClick={() => onRemoveCouponCode({ couponCode: code })}
212
+ >
213
+ <Trash2 className="h-4 w-4" />
214
+ </Button>
215
+ </div>
216
+ ))}
217
+ </div>
218
+ )}
219
+ </div>
220
+ </TableCell>
221
+ </TableRow>
222
+ <OrderTableTotals order={order} columnCount={columns.length} />
223
+ </TableBody>
224
+ </Table>
225
+ </div>
226
+ </div>
227
+ );
228
+ }
@@ -0,0 +1,18 @@
1
+ import { Money } from "@/components/data-display/money.js";
2
+
3
+ export interface MoneyGrossNetProps {
4
+ priceWithTax: number;
5
+ price: number;
6
+ currencyCode: string;
7
+ }
8
+
9
+ export function MoneyGrossNet({ priceWithTax, price, currencyCode }: MoneyGrossNetProps) {
10
+ return <div className="flex flex-col gap-1">
11
+ <div>
12
+ <Money value={priceWithTax} currencyCode={currencyCode} />
13
+ </div>
14
+ <div className="text-xs text-muted-foreground">
15
+ <Money value={price} currencyCode={currencyCode} />
16
+ </div>
17
+ </div>;
18
+ }
@@ -32,7 +32,8 @@ export function OrderAddress({ address }: { address?: OrderAddress }) {
32
32
  <div className="text-sm">
33
33
  {streetLine1 && <p>{streetLine1}</p>}
34
34
  {streetLine2 && <p>{streetLine2}</p>}
35
- <p>{[city, province, postalCode].filter(Boolean).join(', ')}</p>
35
+ <p>{[city, province].filter(Boolean).join(', ')}</p>
36
+ {postalCode && <p>{postalCode}</p>}
36
37
  {country && (
37
38
  <div className="flex items-center gap-1.5 mt-1">
38
39
  <Globe className="h-3 w-3 text-muted-foreground" />
@@ -0,0 +1,38 @@
1
+ import { CustomFieldsForm } from '@/components/shared/custom-fields-form.js';
2
+ import { Button } from '@/components/ui/button.js';
3
+ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover.js';
4
+ import { Settings2 } from 'lucide-react';
5
+ import { UseFormReturn } from 'react-hook-form';
6
+ import { Form } from '@/components/ui/form.js';
7
+
8
+ interface OrderLineCustomFieldsFormProps {
9
+ onUpdate: (customFieldValues: Record<string, any>) => void;
10
+ form: UseFormReturn<any>;
11
+ }
12
+
13
+ export function OrderLineCustomFieldsForm({ onUpdate, form }: OrderLineCustomFieldsFormProps) {
14
+ const onSubmit = (values: any) => {
15
+ onUpdate(values.input?.customFields);
16
+ };
17
+
18
+ return (
19
+ <Popover>
20
+ <PopoverTrigger asChild>
21
+ <Button variant="ghost" size="icon">
22
+ <Settings2 className="h-4 w-4" />
23
+ </Button>
24
+ </PopoverTrigger>
25
+ <PopoverContent className="w-80">
26
+ <Form {...form}>
27
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
28
+ <h4 className="font-medium leading-none">Custom Fields</h4>
29
+ <CustomFieldsForm entityType="OrderLine" control={form.control} formPathPrefix='input' />
30
+ <Button type="submit" className="w-full" disabled={!form.formState.isValid}>
31
+ Update
32
+ </Button>
33
+ </form>
34
+ </Form>
35
+ </PopoverContent>
36
+ </Popover>
37
+ );
38
+ }
@@ -0,0 +1,53 @@
1
+ import { ResultOf } from "@/graphql/graphql.js";
2
+ import { orderDetailDocument } from "../orders.graphql.js";
3
+ import { TableRow, TableCell } from "@/components/ui/table.js";
4
+ import { MoneyGrossNet } from "./money-gross-net.js";
5
+ import { Trans } from "@/lib/trans.js";
6
+
7
+ type OrderFragment = NonNullable<ResultOf<typeof orderDetailDocument>['order']>;
8
+
9
+ export interface OrderTableTotalsProps {
10
+ order: OrderFragment;
11
+ columnCount: number;
12
+ }
13
+
14
+ export function OrderTableTotals({ order, columnCount }: OrderTableTotalsProps) {
15
+ const currencyCode = order.currencyCode;
16
+
17
+ return (
18
+ <>
19
+ {order.discounts?.length > 0 ? order.discounts.map(discount => <TableRow>
20
+ <TableCell colSpan={columnCount - 1} className="h-12">
21
+ <Trans>Discount</Trans>: {discount.description}
22
+ </TableCell>
23
+ <TableCell colSpan={1} className="h-12">
24
+ <MoneyGrossNet priceWithTax={discount.amountWithTax} price={discount.amount} currencyCode={currencyCode} />
25
+ </TableCell>
26
+ </TableRow>) : null}
27
+ <TableRow>
28
+ <TableCell colSpan={columnCount - 1} className="h-12">
29
+ <Trans>Sub total</Trans>
30
+ </TableCell>
31
+ <TableCell colSpan={1} className="h-12">
32
+ <MoneyGrossNet priceWithTax={order.subTotalWithTax} price={order.subTotal} currencyCode={currencyCode} />
33
+ </TableCell>
34
+ </TableRow>
35
+ <TableRow>
36
+ <TableCell colSpan={columnCount - 1} className="h-12">
37
+ <Trans>Shipping</Trans>
38
+ </TableCell>
39
+ <TableCell colSpan={1} className="h-12">
40
+ <MoneyGrossNet priceWithTax={order.shippingWithTax} price={order.shipping} currencyCode={currencyCode} />
41
+ </TableCell>
42
+ </TableRow>
43
+ <TableRow>
44
+ <TableCell colSpan={columnCount - 1} className="h-12 font-bold">
45
+ <Trans>Total</Trans>
46
+ </TableCell>
47
+ <TableCell colSpan={1} className="h-12 font-bold">
48
+ <MoneyGrossNet priceWithTax={order.totalWithTax} price={order.total} currencyCode={currencyCode} />
49
+ </TableCell>
50
+ </TableRow>
51
+ </>
52
+ )
53
+ }
@@ -1,3 +1,4 @@
1
+ import { VendureImage } from '@/components/shared/vendure-image.js';
1
2
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table.js';
2
3
  import { ResultOf } from '@/graphql/graphql.js';
3
4
  import {
@@ -9,9 +10,8 @@ import {
9
10
  } from '@tanstack/react-table';
10
11
  import { useState } from 'react';
11
12
  import { orderDetailDocument, orderLineFragment } from '../orders.graphql.js';
12
- import { VendureImage } from '@/components/shared/vendure-image.js';
13
- import { Money } from '@/components/data-display/money.js';
14
- import { Trans } from '@/lib/trans.js';
13
+ import { MoneyGrossNet } from './money-gross-net.js';
14
+ import { OrderTableTotals } from './order-table-totals.js';
15
15
 
16
16
  type OrderFragment = NonNullable<ResultOf<typeof orderDetailDocument>['order']>;
17
17
  type OrderLineFragment = ResultOf<typeof orderLineFragment>;
@@ -46,18 +46,9 @@ export function OrderTable({ order }: OrderTableProps) {
46
46
  header: 'Unit price',
47
47
  accessorKey: 'unitPriceWithTax',
48
48
  cell: ({ cell, row }) => {
49
- const value = cell.getValue();
49
+ const value = row.original.unitPriceWithTax;
50
50
  const netValue = row.original.unitPrice;
51
- return (
52
- <div className="flex flex-col gap-1">
53
- <div>
54
- <Money value={value} currencyCode={currencyCode} />
55
- </div>
56
- <div className="text-xs text-muted-foreground">
57
- <Money value={netValue} currencyCode={currencyCode} />
58
- </div>
59
- </div>
60
- );
51
+ return <MoneyGrossNet priceWithTax={value} price={netValue} currencyCode={currencyCode} />;
61
52
  },
62
53
  },
63
54
  {
@@ -68,18 +59,9 @@ export function OrderTable({ order }: OrderTableProps) {
68
59
  header: 'Total',
69
60
  accessorKey: 'linePriceWithTax',
70
61
  cell: ({ cell, row }) => {
71
- const value = cell.getValue();
62
+ const value = row.original.linePriceWithTax;
72
63
  const netValue = row.original.linePrice;
73
- return (
74
- <div className="flex flex-col gap-1">
75
- <div>
76
- <Money value={value} currencyCode={currencyCode} />
77
- </div>
78
- <div className="text-xs text-muted-foreground">
79
- <Money value={netValue} currencyCode={currencyCode} />
80
- </div>
81
- </div>
82
- );
64
+ return <MoneyGrossNet priceWithTax={value} price={netValue} currencyCode={currencyCode} />;
83
65
  },
84
66
  },
85
67
  ];
@@ -137,30 +119,7 @@ export function OrderTable({ order }: OrderTableProps) {
137
119
  </TableCell>
138
120
  </TableRow>
139
121
  )}
140
- <TableRow>
141
- <TableCell colSpan={columns.length - 1} className="h-12">
142
- <Trans>Sub total</Trans>
143
- </TableCell>
144
- <TableCell colSpan={1} className="h-12">
145
- <Money value={order.subTotalWithTax} currencyCode={currencyCode} />
146
- </TableCell>
147
- </TableRow>
148
- <TableRow>
149
- <TableCell colSpan={columns.length - 1} className="h-12">
150
- <Trans>Shipping</Trans>
151
- </TableCell>
152
- <TableCell colSpan={1} className="h-12">
153
- <Money value={order.shippingWithTax} currencyCode={currencyCode} />
154
- </TableCell>
155
- </TableRow>
156
- <TableRow>
157
- <TableCell colSpan={columns.length - 1} className="h-12 font-bold">
158
- <Trans>Total</Trans>
159
- </TableCell>
160
- <TableCell colSpan={1} className="h-12 font-bold">
161
- <Money value={order.totalWithTax} currencyCode={currencyCode} />
162
- </TableCell>
163
- </TableRow>
122
+ <OrderTableTotals order={order} columnCount={columns.length} />
164
123
  </TableBody>
165
124
  </Table>
166
125
  </div>
@@ -0,0 +1,65 @@
1
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card.js';
2
+ import { Money } from '@/components/data-display/money.js';
3
+ import { Trans } from '@/lib/trans.js';
4
+ import { draftOrderEligibleShippingMethodsDocument } from '../orders.graphql.js';
5
+ import { ResultOf } from '@/graphql/graphql.js';
6
+
7
+ type ShippingMethodQuote = ResultOf<typeof draftOrderEligibleShippingMethodsDocument>['eligibleShippingMethodsForDraftOrder'][number];
8
+
9
+ interface ShippingMethodSelectorProps {
10
+ eligibleShippingMethods: ShippingMethodQuote[];
11
+ selectedShippingMethodId?: string;
12
+ currencyCode: string;
13
+ onSelect: (shippingMethodId: string) => void;
14
+ }
15
+
16
+ export function ShippingMethodSelector({
17
+ eligibleShippingMethods,
18
+ selectedShippingMethodId,
19
+ currencyCode,
20
+ onSelect
21
+ }: ShippingMethodSelectorProps) {
22
+ return (
23
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
24
+ {eligibleShippingMethods?.length ? eligibleShippingMethods.map(method => (
25
+ <Card
26
+ key={method.id}
27
+ className={`cursor-pointer hover:bg-muted/50 transition-colors border-2 border-transparent ${
28
+ selectedShippingMethodId === method.id
29
+ ? 'border-primary'
30
+ : ''
31
+ }`}
32
+ onClick={() => onSelect(method.id)}
33
+ >
34
+ <CardHeader className="pb-2">
35
+ <CardTitle className="">
36
+ <Trans>{method.name}</Trans>
37
+ </CardTitle>
38
+ </CardHeader>
39
+ <CardContent>
40
+ <div className="space-y-2">
41
+ {method.description && (
42
+ <p className="text-sm text-muted-foreground">
43
+ <Trans>{method.description}</Trans>
44
+ </p>
45
+ )}
46
+ <div className="flex items-center justify-between">
47
+ <span className="text-sm font-medium">
48
+ <Trans>Price</Trans>
49
+ </span>
50
+ <Money
51
+ value={method.priceWithTax}
52
+ currencyCode={currencyCode}
53
+ />
54
+ </div>
55
+ </div>
56
+ </CardContent>
57
+ </Card>
58
+ )) : (
59
+ <div className="col-span-full text-center text-muted-foreground">
60
+ <Trans>No shipping methods available</Trans>
61
+ </div>
62
+ )}
63
+ </div>
64
+ );
65
+ }