@vendure/dashboard 3.3.6-master-202507090236 → 3.3.6-master-202507110238
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.
- package/dist/plugin/tests/barrel-exports.spec.js +1 -1
- package/package.json +4 -4
- package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +30 -37
- package/src/app/routes/_authenticated/_orders/components/fulfillment-details.tsx +33 -53
- package/src/app/routes/_authenticated/_orders/components/order-address.tsx +14 -7
- package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +23 -12
- package/src/app/routes/_authenticated/_orders/components/order-modification-preview-dialog.tsx +364 -0
- package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +222 -0
- package/src/app/routes/_authenticated/_orders/components/order-table.tsx +146 -85
- package/src/app/routes/_authenticated/_orders/components/payment-details.tsx +268 -31
- package/src/app/routes/_authenticated/_orders/components/settle-refund-dialog.tsx +80 -0
- package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +102 -0
- package/src/app/routes/_authenticated/_orders/components/use-transition-order-to-state.tsx +144 -0
- package/src/app/routes/_authenticated/_orders/orders.graphql.ts +118 -2
- package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +144 -52
- package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +550 -0
- package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +0 -17
- package/src/app/routes/_authenticated/_orders/utils/order-types.ts +5 -2
- package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +4 -3
- package/src/app/routes/_authenticated/_products/products_.$id.tsx +0 -1
- package/src/lib/components/data-display/date-time.tsx +7 -1
- package/src/lib/components/data-input/relation-input.tsx +11 -0
- package/src/lib/components/data-input/relation-selector.tsx +9 -2
- package/src/lib/components/data-table/data-table-utils.ts +34 -0
- package/src/lib/components/data-table/data-table-view-options.tsx +2 -2
- package/src/lib/components/data-table/data-table.tsx +5 -2
- package/src/lib/components/data-table/use-generated-columns.tsx +307 -0
- package/src/lib/components/shared/paginated-list-data-table.tsx +15 -286
- package/src/lib/components/shared/product-variant-selector.tsx +28 -4
- package/src/lib/framework/component-registry/dynamic-component.tsx +3 -3
- package/src/lib/framework/document-introspection/get-document-structure.spec.ts +321 -2
- package/src/lib/framework/document-introspection/get-document-structure.ts +149 -31
- package/src/lib/framework/extension-api/types/layout.ts +21 -6
- package/src/lib/framework/layout-engine/layout-extensions.ts +1 -4
- package/src/lib/framework/layout-engine/page-layout.tsx +61 -10
- package/src/lib/framework/page/use-detail-page.ts +10 -7
- package/vite/tests/barrel-exports.spec.ts +1 -1
|
@@ -1,51 +1,26 @@
|
|
|
1
|
-
import { DataTableColumnHeader } from '@/vdb/components/data-table/data-table-column-header.js';
|
|
2
1
|
import { DataTable, FacetedFilter } from '@/vdb/components/data-table/data-table.js';
|
|
3
2
|
import {
|
|
4
|
-
|
|
5
|
-
getObjectPathToPaginatedList,
|
|
6
|
-
getTypeFieldInfo,
|
|
3
|
+
getObjectPathToPaginatedList
|
|
7
4
|
} from '@/vdb/framework/document-introspection/get-document-structure.js';
|
|
8
5
|
import { useListQueryFields } from '@/vdb/framework/document-introspection/hooks.js';
|
|
9
6
|
import { api } from '@/vdb/graphql/api.js';
|
|
10
|
-
import { keepPreviousData,
|
|
7
|
+
import { keepPreviousData, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
11
8
|
import { useDebounce } from '@uidotdev/usehooks';
|
|
12
9
|
|
|
13
|
-
import {
|
|
14
|
-
AlertDialog,
|
|
15
|
-
AlertDialogAction,
|
|
16
|
-
AlertDialogCancel,
|
|
17
|
-
AlertDialogContent,
|
|
18
|
-
AlertDialogDescription,
|
|
19
|
-
AlertDialogFooter,
|
|
20
|
-
AlertDialogHeader,
|
|
21
|
-
AlertDialogTitle,
|
|
22
|
-
AlertDialogTrigger,
|
|
23
|
-
} from '@/vdb/components/ui/alert-dialog.js';
|
|
24
|
-
import {
|
|
25
|
-
DropdownMenu,
|
|
26
|
-
DropdownMenuContent,
|
|
27
|
-
DropdownMenuItem,
|
|
28
|
-
DropdownMenuTrigger,
|
|
29
|
-
} from '@/vdb/components/ui/dropdown-menu.js';
|
|
30
|
-
import { DisplayComponent } from '@/vdb/framework/component-registry/dynamic-component.js';
|
|
31
10
|
import { BulkAction } from '@/vdb/framework/extension-api/types/index.js';
|
|
32
11
|
import { ResultOf } from '@/vdb/graphql/graphql.js';
|
|
33
12
|
import { useExtendedListQuery } from '@/vdb/hooks/use-extended-list-query.js';
|
|
34
|
-
import { Trans, useLingui } from '@/vdb/lib/trans.js';
|
|
35
13
|
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
36
14
|
import {
|
|
37
15
|
ColumnFiltersState,
|
|
38
16
|
ColumnSort,
|
|
39
|
-
createColumnHelper,
|
|
40
17
|
SortingState,
|
|
41
|
-
Table
|
|
18
|
+
Table
|
|
42
19
|
} from '@tanstack/react-table';
|
|
43
|
-
import {
|
|
44
|
-
import
|
|
45
|
-
import
|
|
46
|
-
import {
|
|
47
|
-
import { Button } from '../ui/button.js';
|
|
48
|
-
import { Checkbox } from '../ui/checkbox.js';
|
|
20
|
+
import { ColumnDef, Row, TableOptions, VisibilityState } from '@tanstack/table-core';
|
|
21
|
+
import React from 'react';
|
|
22
|
+
import { getColumnVisibility } from '../data-table/data-table-utils.js';
|
|
23
|
+
import { useGeneratedColumns } from '../data-table/use-generated-columns.js';
|
|
49
24
|
|
|
50
25
|
// Type that identifies a paginated list structure (has items array and totalItems)
|
|
51
26
|
type IsPaginatedList<T> = T extends { items: any[]; totalItems: number } ? true : false;
|
|
@@ -345,139 +320,14 @@ export function PaginatedListDataTable<
|
|
|
345
320
|
listData = listData?.[path];
|
|
346
321
|
}
|
|
347
322
|
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
.filter(field => field.name !== 'customFields' && !field.type.endsWith('CustomFields'))
|
|
357
|
-
.map(field => ({ fieldInfo: field, isCustomField: false })),
|
|
358
|
-
);
|
|
359
|
-
|
|
360
|
-
const customFieldColumn = fields.find(field => field.name === 'customFields');
|
|
361
|
-
if (customFieldColumn && customFieldColumn.type !== 'JSON') {
|
|
362
|
-
const customFieldFields = getTypeFieldInfo(customFieldColumn.type);
|
|
363
|
-
columnConfigs.push(
|
|
364
|
-
...customFieldFields.map(field => ({ fieldInfo: field, isCustomField: true })),
|
|
365
|
-
);
|
|
366
|
-
customFieldColumnNames.push(...customFieldFields.map(field => field.name));
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const queryBasedColumns = columnConfigs.map(({ fieldInfo, isCustomField }) => {
|
|
370
|
-
const customConfig = customizeColumns?.[fieldInfo.name as unknown as AllItemFieldKeys<T>] ?? {};
|
|
371
|
-
const { header, ...customConfigRest } = customConfig;
|
|
372
|
-
const enableColumnFilter = fieldInfo.isScalar && !facetedFilters?.[fieldInfo.name];
|
|
373
|
-
|
|
374
|
-
return columnHelper.accessor(fieldInfo.name as any, {
|
|
375
|
-
id: fieldInfo.name,
|
|
376
|
-
meta: { fieldInfo, isCustomField },
|
|
377
|
-
enableColumnFilter,
|
|
378
|
-
enableSorting: fieldInfo.isScalar,
|
|
379
|
-
// Filtering is done on the server side, but we set this to 'equalsString' because
|
|
380
|
-
// otherwise the TanStack Table with apply an "auto" function which somehow
|
|
381
|
-
// prevents certain filters from working.
|
|
382
|
-
filterFn: 'equalsString',
|
|
383
|
-
cell: ({ cell, row }) => {
|
|
384
|
-
const cellValue = cell.getValue();
|
|
385
|
-
const value =
|
|
386
|
-
cellValue ??
|
|
387
|
-
(isCustomField ? row.original?.customFields?.[fieldInfo.name] : undefined);
|
|
388
|
-
|
|
389
|
-
if (fieldInfo.list && Array.isArray(value)) {
|
|
390
|
-
return value.join(', ');
|
|
391
|
-
}
|
|
392
|
-
if (
|
|
393
|
-
(fieldInfo.type === 'DateTime' && typeof value === 'string') ||
|
|
394
|
-
value instanceof Date
|
|
395
|
-
) {
|
|
396
|
-
return <DisplayComponent id="vendure:dateTime" value={value} />;
|
|
397
|
-
}
|
|
398
|
-
if (fieldInfo.type === 'Boolean') {
|
|
399
|
-
if (cell.column.id === 'enabled') {
|
|
400
|
-
return <DisplayComponent id="vendure:booleanBadge" value={value} />;
|
|
401
|
-
} else {
|
|
402
|
-
return <DisplayComponent id="vendure:booleanCheckbox" value={value} />;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
if (fieldInfo.type === 'Asset') {
|
|
406
|
-
return <DisplayComponent id="vendure:asset" value={value} />;
|
|
407
|
-
}
|
|
408
|
-
if (value !== null && typeof value === 'object') {
|
|
409
|
-
return JSON.stringify(value);
|
|
410
|
-
}
|
|
411
|
-
return value;
|
|
412
|
-
},
|
|
413
|
-
header: headerContext => {
|
|
414
|
-
return (
|
|
415
|
-
<DataTableColumnHeader headerContext={headerContext} customConfig={customConfig} />
|
|
416
|
-
);
|
|
417
|
-
},
|
|
418
|
-
...customConfigRest,
|
|
419
|
-
});
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
let finalColumns = [...queryBasedColumns];
|
|
423
|
-
|
|
424
|
-
for (const [id, column] of Object.entries(additionalColumns ?? {})) {
|
|
425
|
-
if (!id) {
|
|
426
|
-
throw new Error('Column id is required');
|
|
427
|
-
}
|
|
428
|
-
finalColumns.push(columnHelper.accessor(id as any, { ...column, id }));
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (defaultColumnOrder) {
|
|
432
|
-
// ensure the columns with ids matching the items in defaultColumnOrder
|
|
433
|
-
// appear as the first columns in sequence, and leave the remainder in the
|
|
434
|
-
// existing order
|
|
435
|
-
const orderedColumns = finalColumns
|
|
436
|
-
.filter(column => column.id && defaultColumnOrder.includes(column.id as any))
|
|
437
|
-
.sort(
|
|
438
|
-
(a, b) =>
|
|
439
|
-
defaultColumnOrder.indexOf(a.id as any) - defaultColumnOrder.indexOf(b.id as any),
|
|
440
|
-
);
|
|
441
|
-
const remainingColumns = finalColumns.filter(
|
|
442
|
-
column => !column.id || !defaultColumnOrder.includes(column.id as any),
|
|
443
|
-
);
|
|
444
|
-
finalColumns = [...orderedColumns, ...remainingColumns];
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
if (rowActions || deleteMutation) {
|
|
448
|
-
const rowActionColumn = getRowActions(rowActions, deleteMutation);
|
|
449
|
-
if (rowActionColumn) {
|
|
450
|
-
finalColumns.push(rowActionColumn);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Add the row selection column
|
|
455
|
-
finalColumns.unshift({
|
|
456
|
-
id: 'selection',
|
|
457
|
-
accessorKey: 'selection',
|
|
458
|
-
header: ({ table }) => (
|
|
459
|
-
<Checkbox
|
|
460
|
-
className="mx-1"
|
|
461
|
-
checked={table.getIsAllRowsSelected()}
|
|
462
|
-
onCheckedChange={checked =>
|
|
463
|
-
table.toggleAllRowsSelected(checked === 'indeterminate' ? undefined : checked)
|
|
464
|
-
}
|
|
465
|
-
/>
|
|
466
|
-
),
|
|
467
|
-
enableColumnFilter: false,
|
|
468
|
-
cell: ({ row }) => {
|
|
469
|
-
return (
|
|
470
|
-
<Checkbox
|
|
471
|
-
className="mx-1"
|
|
472
|
-
checked={row.getIsSelected()}
|
|
473
|
-
onCheckedChange={row.getToggleSelectedHandler()}
|
|
474
|
-
/>
|
|
475
|
-
);
|
|
476
|
-
},
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
return { columns: finalColumns, customFieldColumnNames };
|
|
480
|
-
}, [fields, customizeColumns, rowActions]);
|
|
323
|
+
const { columns, customFieldColumnNames } = useGeneratedColumns({
|
|
324
|
+
fields,
|
|
325
|
+
customizeColumns,
|
|
326
|
+
rowActions,
|
|
327
|
+
deleteMutation,
|
|
328
|
+
additionalColumns,
|
|
329
|
+
defaultColumnOrder,
|
|
330
|
+
});
|
|
481
331
|
|
|
482
332
|
const columnVisibility = getColumnVisibility(fields, defaultVisibility, customFieldColumnNames);
|
|
483
333
|
const transformedData =
|
|
@@ -509,124 +359,3 @@ export function PaginatedListDataTable<
|
|
|
509
359
|
);
|
|
510
360
|
}
|
|
511
361
|
|
|
512
|
-
function getRowActions(
|
|
513
|
-
rowActions?: RowAction<any>[],
|
|
514
|
-
deleteMutation?: TypedDocumentNode<any, any>,
|
|
515
|
-
): AccessorKeyColumnDef<any> | undefined {
|
|
516
|
-
return {
|
|
517
|
-
id: 'actions',
|
|
518
|
-
accessorKey: 'actions',
|
|
519
|
-
header: 'Actions',
|
|
520
|
-
enableColumnFilter: false,
|
|
521
|
-
cell: ({ row }) => {
|
|
522
|
-
return (
|
|
523
|
-
<DropdownMenu>
|
|
524
|
-
<DropdownMenuTrigger asChild>
|
|
525
|
-
<Button variant="ghost" size="icon">
|
|
526
|
-
<EllipsisIcon />
|
|
527
|
-
</Button>
|
|
528
|
-
</DropdownMenuTrigger>
|
|
529
|
-
<DropdownMenuContent>
|
|
530
|
-
{rowActions?.map((action, index) => (
|
|
531
|
-
<DropdownMenuItem onClick={() => action.onClick?.(row)} key={index}>
|
|
532
|
-
{action.label}
|
|
533
|
-
</DropdownMenuItem>
|
|
534
|
-
))}
|
|
535
|
-
{deleteMutation && (
|
|
536
|
-
<DeleteMutationRowAction deleteMutation={deleteMutation} row={row} />
|
|
537
|
-
)}
|
|
538
|
-
</DropdownMenuContent>
|
|
539
|
-
</DropdownMenu>
|
|
540
|
-
);
|
|
541
|
-
},
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
function DeleteMutationRowAction({
|
|
546
|
-
deleteMutation,
|
|
547
|
-
row,
|
|
548
|
-
}: {
|
|
549
|
-
deleteMutation: TypedDocumentNode<any, any>;
|
|
550
|
-
row: Row<{ id: string }>;
|
|
551
|
-
}) {
|
|
552
|
-
const { refetchPaginatedList } = usePaginatedList();
|
|
553
|
-
const { i18n } = useLingui();
|
|
554
|
-
const { mutate: deleteMutationFn } = useMutation({
|
|
555
|
-
mutationFn: api.mutate(deleteMutation),
|
|
556
|
-
onSuccess: (result: { [key: string]: { result: 'DELETED' | 'NOT_DELETED'; message: string } }) => {
|
|
557
|
-
const unwrappedResult = Object.values(result)[0];
|
|
558
|
-
if (unwrappedResult.result === 'DELETED') {
|
|
559
|
-
refetchPaginatedList();
|
|
560
|
-
toast.success(i18n.t('Deleted successfully'));
|
|
561
|
-
} else {
|
|
562
|
-
toast.error(i18n.t('Failed to delete'), {
|
|
563
|
-
description: unwrappedResult.message,
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
},
|
|
567
|
-
onError: (err: Error) => {
|
|
568
|
-
toast.error(i18n.t('Failed to delete'), {
|
|
569
|
-
description: err.message,
|
|
570
|
-
});
|
|
571
|
-
},
|
|
572
|
-
});
|
|
573
|
-
return (
|
|
574
|
-
<AlertDialog>
|
|
575
|
-
<AlertDialogTrigger asChild>
|
|
576
|
-
<DropdownMenuItem onSelect={e => e.preventDefault()}>
|
|
577
|
-
<div className="flex items-center gap-2 text-destructive">
|
|
578
|
-
<TrashIcon className="w-4 h-4 text-destructive" />
|
|
579
|
-
<Trans>Delete</Trans>
|
|
580
|
-
</div>
|
|
581
|
-
</DropdownMenuItem>
|
|
582
|
-
</AlertDialogTrigger>
|
|
583
|
-
<AlertDialogContent>
|
|
584
|
-
<AlertDialogHeader>
|
|
585
|
-
<AlertDialogTitle>
|
|
586
|
-
<Trans>Confirm deletion</Trans>
|
|
587
|
-
</AlertDialogTitle>
|
|
588
|
-
<AlertDialogDescription>
|
|
589
|
-
<Trans>
|
|
590
|
-
Are you sure you want to delete this item? This action cannot be undone.
|
|
591
|
-
</Trans>
|
|
592
|
-
</AlertDialogDescription>
|
|
593
|
-
</AlertDialogHeader>
|
|
594
|
-
<AlertDialogFooter>
|
|
595
|
-
<AlertDialogCancel>
|
|
596
|
-
<Trans>Cancel</Trans>
|
|
597
|
-
</AlertDialogCancel>
|
|
598
|
-
<AlertDialogAction
|
|
599
|
-
onClick={() => deleteMutationFn({ id: row.original.id })}
|
|
600
|
-
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
601
|
-
>
|
|
602
|
-
<Trans>Delete</Trans>
|
|
603
|
-
</AlertDialogAction>
|
|
604
|
-
</AlertDialogFooter>
|
|
605
|
-
</AlertDialogContent>
|
|
606
|
-
</AlertDialog>
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
/**
|
|
611
|
-
* Returns the default column visibility configuration.
|
|
612
|
-
*/
|
|
613
|
-
function getColumnVisibility(
|
|
614
|
-
fields: FieldInfo[],
|
|
615
|
-
defaultVisibility?: Record<string, boolean | undefined>,
|
|
616
|
-
customFieldColumnNames?: string[],
|
|
617
|
-
): Record<string, boolean> {
|
|
618
|
-
const allDefaultsTrue = defaultVisibility && Object.values(defaultVisibility).every(v => v === true);
|
|
619
|
-
const allDefaultsFalse = defaultVisibility && Object.values(defaultVisibility).every(v => v === false);
|
|
620
|
-
return {
|
|
621
|
-
id: false,
|
|
622
|
-
createdAt: false,
|
|
623
|
-
updatedAt: false,
|
|
624
|
-
...(allDefaultsTrue ? { ...Object.fromEntries(fields.map(f => [f.name, false])) } : {}),
|
|
625
|
-
...(allDefaultsFalse ? { ...Object.fromEntries(fields.map(f => [f.name, true])) } : {}),
|
|
626
|
-
// Make custom fields hidden by default unless overridden
|
|
627
|
-
...(customFieldColumnNames
|
|
628
|
-
? { ...Object.fromEntries(customFieldColumnNames.map(f => [f, false])) }
|
|
629
|
-
: {}),
|
|
630
|
-
...defaultVisibility,
|
|
631
|
-
};
|
|
632
|
-
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from '@/vdb/components/ui/command.js';
|
|
9
9
|
import { Popover, PopoverContent, PopoverTrigger } from '@/vdb/components/ui/popover.js';
|
|
10
10
|
import { api } from '@/vdb/graphql/api.js';
|
|
11
|
-
import { assetFragment } from '@/vdb/graphql/fragments.js';
|
|
11
|
+
import { AssetFragment, assetFragment } from '@/vdb/graphql/fragments.js';
|
|
12
12
|
import { graphql } from '@/vdb/graphql/graphql.js';
|
|
13
13
|
import { useQuery } from '@tanstack/react-query';
|
|
14
14
|
import { useDebounce } from '@uidotdev/usehooks';
|
|
@@ -28,6 +28,13 @@ const productVariantListDocument = graphql(
|
|
|
28
28
|
featuredAsset {
|
|
29
29
|
...Asset
|
|
30
30
|
}
|
|
31
|
+
price
|
|
32
|
+
priceWithTax
|
|
33
|
+
product {
|
|
34
|
+
featuredAsset {
|
|
35
|
+
...Asset
|
|
36
|
+
}
|
|
37
|
+
}
|
|
31
38
|
}
|
|
32
39
|
totalItems
|
|
33
40
|
}
|
|
@@ -37,10 +44,17 @@ const productVariantListDocument = graphql(
|
|
|
37
44
|
);
|
|
38
45
|
|
|
39
46
|
export interface ProductVariantSelectorProps {
|
|
40
|
-
|
|
47
|
+
onProductVariantSelect: (variant: {
|
|
48
|
+
productVariantId: string;
|
|
49
|
+
productVariantName: string;
|
|
50
|
+
sku: string;
|
|
51
|
+
productAsset: AssetFragment | null;
|
|
52
|
+
price?: number;
|
|
53
|
+
priceWithTax?: number;
|
|
54
|
+
}) => void;
|
|
41
55
|
}
|
|
42
56
|
|
|
43
|
-
export function ProductVariantSelector({
|
|
57
|
+
export function ProductVariantSelector({ onProductVariantSelect }: Readonly<ProductVariantSelectorProps>) {
|
|
44
58
|
const [search, setSearch] = useState('');
|
|
45
59
|
const [open, setOpen] = useState(false);
|
|
46
60
|
const debouncedSearch = useDebounce(search, 500);
|
|
@@ -85,7 +99,17 @@ export function ProductVariantSelector({ onProductVariantIdChange }: Readonly<Pr
|
|
|
85
99
|
key={variant.id}
|
|
86
100
|
value={variant.id}
|
|
87
101
|
onSelect={() => {
|
|
88
|
-
|
|
102
|
+
onProductVariantSelect({
|
|
103
|
+
productVariantId: variant.id,
|
|
104
|
+
productVariantName: variant.name,
|
|
105
|
+
sku: variant.sku,
|
|
106
|
+
productAsset:
|
|
107
|
+
variant.featuredAsset ??
|
|
108
|
+
variant.product.featuredAsset ??
|
|
109
|
+
null,
|
|
110
|
+
price: variant.price,
|
|
111
|
+
priceWithTax: variant.priceWithTax,
|
|
112
|
+
});
|
|
89
113
|
setOpen(false);
|
|
90
114
|
}}
|
|
91
115
|
className="flex items-center gap-2 p-2"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useComponentRegistry } from "./component-registry.js";
|
|
3
3
|
|
|
4
4
|
export type DisplayComponentProps<
|
|
5
5
|
T extends keyof (typeof COMPONENT_REGISTRY)['dataDisplay'] | string,
|
|
@@ -33,7 +33,7 @@ export type InputComponentProps<
|
|
|
33
33
|
*/
|
|
34
34
|
export function DisplayComponent<
|
|
35
35
|
T extends keyof (typeof COMPONENT_REGISTRY)['dataDisplay'] | string,
|
|
36
|
-
>(props: DisplayComponentProps<T
|
|
36
|
+
>(props: Readonly<DisplayComponentProps<T>>): React.ReactNode {
|
|
37
37
|
const { getDisplayComponent } = useComponentRegistry();
|
|
38
38
|
const Component = getDisplayComponent(props.id);
|
|
39
39
|
if (!Component) {
|
|
@@ -45,7 +45,7 @@ export function DisplayComponent<
|
|
|
45
45
|
|
|
46
46
|
export function InputComponent<
|
|
47
47
|
T extends keyof (typeof COMPONENT_REGISTRY)['dataInput'] | string,
|
|
48
|
-
>(props: InputComponentProps<T
|
|
48
|
+
>(props: Readonly<InputComponentProps<T>>): React.ReactNode {
|
|
49
49
|
const { getInputComponent } = useComponentRegistry();
|
|
50
50
|
const Component = getInputComponent(props.id);
|
|
51
51
|
if (!Component) {
|