@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.
- package/dist/plugin/utils/ast-utils.d.ts +10 -0
- package/dist/plugin/utils/ast-utils.js +96 -0
- package/dist/plugin/utils/ast-utils.spec.d.ts +1 -0
- package/dist/plugin/utils/ast-utils.spec.js +120 -0
- package/dist/plugin/{config-loader.d.ts → utils/config-loader.d.ts} +22 -8
- package/dist/plugin/utils/config-loader.js +325 -0
- package/dist/plugin/{schema-generator.d.ts → utils/schema-generator.d.ts} +5 -0
- package/dist/plugin/{schema-generator.js → utils/schema-generator.js} +6 -0
- package/dist/plugin/{ui-config.js → utils/ui-config.js} +2 -2
- package/dist/plugin/vite-plugin-admin-api-schema.js +2 -2
- package/dist/plugin/vite-plugin-config-loader.d.ts +2 -3
- package/dist/plugin/vite-plugin-config-loader.js +18 -9
- package/dist/plugin/vite-plugin-dashboard-metadata.js +12 -14
- package/dist/plugin/vite-plugin-gql-tada.js +2 -2
- package/dist/plugin/vite-plugin-ui-config.js +3 -2
- package/package.json +8 -6
- package/src/app/app-providers.tsx +8 -8
- package/src/app/main.tsx +1 -1
- package/src/app/routes/_authenticated/_assets/assets.graphql.ts +26 -0
- package/src/app/routes/_authenticated/_assets/assets.tsx +2 -2
- package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +156 -0
- package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +104 -0
- package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +228 -0
- package/src/app/routes/_authenticated/_orders/components/money-gross-net.tsx +18 -0
- package/src/app/routes/_authenticated/_orders/components/order-address.tsx +2 -1
- package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +38 -0
- package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +53 -0
- package/src/app/routes/_authenticated/_orders/components/order-table.tsx +8 -49
- package/src/app/routes/_authenticated/_orders/components/shipping-method-selector.tsx +65 -0
- package/src/app/routes/_authenticated/_orders/orders.graphql.ts +187 -1
- package/src/app/routes/_authenticated/_orders/orders.tsx +39 -18
- package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +31 -9
- package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +418 -0
- package/src/app/routes/_authenticated/_products/products.tsx +1 -1
- package/src/app/routes/_authenticated.tsx +12 -1
- package/src/lib/components/data-table/add-filter-menu.tsx +61 -0
- package/src/lib/components/data-table/data-table-column-header.tsx +0 -13
- package/src/lib/components/data-table/data-table-filter-badge.tsx +75 -0
- package/src/lib/components/data-table/data-table-filter-dialog.tsx +27 -28
- package/src/lib/components/data-table/data-table-types.ts +1 -0
- package/src/lib/components/data-table/data-table-view-options.tsx +72 -23
- package/src/lib/components/data-table/data-table.tsx +23 -24
- package/src/lib/components/data-table/filters/data-table-boolean-filter.tsx +57 -0
- package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +93 -0
- package/src/lib/components/data-table/filters/data-table-id-filter.tsx +58 -0
- package/src/lib/components/data-table/filters/data-table-number-filter.tsx +119 -0
- package/src/lib/components/data-table/filters/data-table-string-filter.tsx +62 -0
- package/src/lib/components/data-table/human-readable-operator.tsx +65 -0
- package/src/lib/components/layout/nav-user.tsx +4 -4
- package/src/lib/components/shared/asset/asset-focal-point-editor.tsx +93 -0
- package/src/lib/components/shared/{asset-gallery.tsx → asset/asset-gallery.tsx} +51 -20
- package/src/lib/components/shared/{asset-picker-dialog.tsx → asset/asset-picker-dialog.tsx} +1 -1
- package/src/lib/components/shared/{asset-preview-dialog.tsx → asset/asset-preview-dialog.tsx} +1 -7
- package/src/lib/components/shared/asset/asset-preview-selector.tsx +34 -0
- package/src/lib/components/shared/asset/asset-preview.tsx +128 -0
- package/src/lib/components/shared/asset/asset-properties.tsx +46 -0
- package/src/lib/components/shared/{focal-point-control.tsx → asset/focal-point-control.tsx} +1 -1
- package/src/lib/components/shared/custom-fields-form.tsx +4 -3
- package/src/lib/components/shared/customer-selector.tsx +13 -14
- package/src/lib/components/shared/detail-page-button.tsx +2 -2
- package/src/lib/components/shared/entity-assets.tsx +3 -3
- package/src/lib/components/shared/navigation-confirmation.tsx +39 -0
- package/src/lib/components/shared/paginated-list-data-table.tsx +9 -1
- package/src/lib/components/shared/product-variant-selector.tsx +111 -0
- package/src/lib/components/shared/vendure-image.tsx +1 -1
- package/src/lib/components/ui/calendar.tsx +508 -63
- package/src/lib/framework/document-introspection/get-document-structure.spec.ts +113 -3
- package/src/lib/framework/document-introspection/get-document-structure.ts +70 -11
- package/src/lib/framework/form-engine/use-generated-form.tsx +8 -7
- package/src/lib/framework/layout-engine/page-layout.tsx +4 -0
- package/src/lib/framework/page/list-page.tsx +23 -4
- package/src/lib/framework/page/use-detail-page.ts +1 -0
- package/src/lib/graphql/fragments.tsx +8 -0
- package/src/lib/index.ts +5 -5
- package/src/lib/providers/auth.tsx +12 -9
- package/src/lib/providers/channel-provider.tsx +1 -0
- package/src/lib/providers/server-config.tsx +7 -1
- package/src/lib/providers/user-settings.tsx +24 -0
- package/vite/utils/ast-utils.spec.ts +128 -0
- package/vite/utils/ast-utils.ts +119 -0
- package/vite/utils/config-loader.ts +410 -0
- package/vite/{schema-generator.ts → utils/schema-generator.ts} +7 -1
- package/vite/{ui-config.ts → utils/ui-config.ts} +2 -2
- package/vite/vite-plugin-admin-api-schema.ts +2 -2
- package/vite/vite-plugin-config-loader.ts +25 -13
- package/vite/vite-plugin-dashboard-metadata.ts +19 -15
- package/vite/vite-plugin-gql-tada.ts +2 -2
- package/vite/vite-plugin-ui-config.ts +3 -2
- package/dist/plugin/config-loader.js +0 -141
- package/src/lib/components/shared/asset-preview.tsx +0 -345
- package/vite/config-loader.ts +0 -181
- /package/dist/plugin/{ui-config.d.ts → utils/ui-config.d.ts} +0 -0
|
@@ -5,6 +5,7 @@ import { api } from '@/graphql/api.js';
|
|
|
5
5
|
import { graphql } from '@/graphql/graphql.js';
|
|
6
6
|
import { Trans } from '@/lib/trans.js';
|
|
7
7
|
import { useQuery } from '@tanstack/react-query';
|
|
8
|
+
import { useDebounce } from '@uidotdev/usehooks';
|
|
8
9
|
import { Plus, Search } from 'lucide-react';
|
|
9
10
|
import { useState } from 'react';
|
|
10
11
|
|
|
@@ -38,19 +39,20 @@ export interface CustomerSelectorProps {
|
|
|
38
39
|
export function CustomerSelector(props: CustomerSelectorProps) {
|
|
39
40
|
const [open, setOpen] = useState(false);
|
|
40
41
|
const [searchTerm, setSearchTerm] = useState('');
|
|
42
|
+
const debouncedSearchTerm = useDebounce(searchTerm, 300);
|
|
41
43
|
|
|
42
44
|
const { data, isLoading } = useQuery({
|
|
43
|
-
queryKey: ['customers',
|
|
45
|
+
queryKey: ['customers', debouncedSearchTerm],
|
|
44
46
|
queryFn: () =>
|
|
45
47
|
api.query(customersDocument, {
|
|
46
48
|
options: {
|
|
47
49
|
sort: { lastName: 'ASC' },
|
|
48
|
-
filter:
|
|
49
|
-
firstName: { contains:
|
|
50
|
-
lastName: { contains:
|
|
51
|
-
emailAddress: { contains:
|
|
50
|
+
filter: debouncedSearchTerm ? {
|
|
51
|
+
firstName: { contains: debouncedSearchTerm },
|
|
52
|
+
lastName: { contains: debouncedSearchTerm },
|
|
53
|
+
emailAddress: { contains: debouncedSearchTerm },
|
|
52
54
|
} : undefined,
|
|
53
|
-
filterOperator:
|
|
55
|
+
filterOperator: debouncedSearchTerm ? 'OR' : undefined,
|
|
54
56
|
},
|
|
55
57
|
}),
|
|
56
58
|
staleTime: 1000 * 60, // 1 minute
|
|
@@ -70,14 +72,11 @@ export function CustomerSelector(props: CustomerSelectorProps) {
|
|
|
70
72
|
</PopoverTrigger>
|
|
71
73
|
<PopoverContent className="p-0 w-[350px]" align="start">
|
|
72
74
|
<Command shouldFilter={false}>
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
className="h-10 flex-1 bg-transparent outline-none placeholder:text-muted-foreground"
|
|
79
|
-
/>
|
|
80
|
-
</div>
|
|
75
|
+
<CommandInput
|
|
76
|
+
placeholder="Search customers..."
|
|
77
|
+
onValueChange={handleSearch}
|
|
78
|
+
className="h-10 flex-1 bg-transparent outline-none placeholder:text-muted-foreground"
|
|
79
|
+
/>
|
|
81
80
|
<CommandList>
|
|
82
81
|
<CommandEmpty>
|
|
83
82
|
{isLoading ? (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Link } from '@tanstack/react-router';
|
|
2
|
+
import { ChevronRight } from 'lucide-react';
|
|
2
3
|
import { Button } from '../ui/button.js';
|
|
3
|
-
import { SquareArrowOutUpRightIcon } from 'lucide-react';
|
|
4
4
|
|
|
5
5
|
export function DetailPageButton({
|
|
6
6
|
id,
|
|
@@ -15,7 +15,7 @@ export function DetailPageButton({
|
|
|
15
15
|
<Button asChild variant="ghost" disabled={disabled}>
|
|
16
16
|
<Link to={`./${id}`}>
|
|
17
17
|
{label}
|
|
18
|
-
{!disabled && <
|
|
18
|
+
{!disabled && <ChevronRight className="h-3 w-3 text-muted-foreground" />}
|
|
19
19
|
</Link>
|
|
20
20
|
</Button>
|
|
21
21
|
);
|
|
@@ -24,9 +24,9 @@ import {
|
|
|
24
24
|
} from '@dnd-kit/sortable';
|
|
25
25
|
import { CSS } from '@dnd-kit/utilities';
|
|
26
26
|
import { EllipsisIcon, ImageIcon, PaperclipIcon } from 'lucide-react';
|
|
27
|
-
import
|
|
28
|
-
import { AssetPickerDialog } from './asset-picker-dialog.js';
|
|
29
|
-
import { AssetPreviewDialog } from './asset-preview-dialog.js';
|
|
27
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
28
|
+
import { AssetPickerDialog } from './asset/asset-picker-dialog.js';
|
|
29
|
+
import { AssetPreviewDialog } from './asset/asset-preview-dialog.js';
|
|
30
30
|
import { VendureImage } from './vendure-image.js';
|
|
31
31
|
|
|
32
32
|
type Asset = AssetFragment;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Trans } from "@/lib/trans.js";
|
|
2
|
+
import { useBlocker } from "@tanstack/react-router";
|
|
3
|
+
import { UseFormReturn } from "react-hook-form";
|
|
4
|
+
|
|
5
|
+
import { Button } from "../ui/button.js";
|
|
6
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../ui/dialog.js";
|
|
7
|
+
|
|
8
|
+
export interface NavigationConfirmationProps {
|
|
9
|
+
form: UseFormReturn<any>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Navigation confirmation dialog that blocks navigation when the form is dirty.
|
|
14
|
+
*/
|
|
15
|
+
export function NavigationConfirmation(props: NavigationConfirmationProps) {
|
|
16
|
+
const { proceed, reset, status } = useBlocker({
|
|
17
|
+
shouldBlockFn: () => props.form.formState.isDirty,
|
|
18
|
+
withResolver: true,
|
|
19
|
+
enableBeforeUnload: true,
|
|
20
|
+
})
|
|
21
|
+
return (
|
|
22
|
+
<Dialog open={status === 'blocked'} onOpenChange={reset}>
|
|
23
|
+
<DialogContent className="sm:max-w-[425px]">
|
|
24
|
+
<DialogHeader>
|
|
25
|
+
<DialogTitle><Trans>Confirm navigation</Trans></DialogTitle>
|
|
26
|
+
<DialogDescription>
|
|
27
|
+
<Trans>Are you sure you want to navigate away from this page? Any unsaved changes will be lost.</Trans>
|
|
28
|
+
</DialogDescription>
|
|
29
|
+
</DialogHeader>
|
|
30
|
+
<DialogFooter>
|
|
31
|
+
<Button variant="outline" onClick={reset}><Trans>Cancel</Trans></Button>
|
|
32
|
+
<Button type="button" onClick={proceed}>
|
|
33
|
+
<Trans>Confirm</Trans>
|
|
34
|
+
</Button>
|
|
35
|
+
</DialogFooter>
|
|
36
|
+
</DialogContent>
|
|
37
|
+
</Dialog>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
SortingState,
|
|
29
29
|
Table,
|
|
30
30
|
} from '@tanstack/react-table';
|
|
31
|
-
import { AccessorKeyColumnDef, ColumnDef, Row, TableOptions } from '@tanstack/table-core';
|
|
31
|
+
import { AccessorKeyColumnDef, ColumnDef, Row, TableOptions, VisibilityState } from '@tanstack/table-core';
|
|
32
32
|
import { EllipsisIcon, TrashIcon } from 'lucide-react';
|
|
33
33
|
import React, { useMemo } from 'react';
|
|
34
34
|
import { toast } from 'sonner';
|
|
@@ -196,6 +196,7 @@ export interface PaginatedListDataTableProps<
|
|
|
196
196
|
onPageChange: (table: Table<any>, page: number, perPage: number) => void;
|
|
197
197
|
onSortChange: (table: Table<any>, sorting: SortingState) => void;
|
|
198
198
|
onFilterChange: (table: Table<any>, filters: ColumnFiltersState) => void;
|
|
199
|
+
onColumnVisibilityChange?: (table: Table<any>, columnVisibility: VisibilityState) => void;
|
|
199
200
|
facetedFilters?: FacetedFilterConfig<T>;
|
|
200
201
|
rowActions?: RowAction<PaginatedListItemFields<T>>[];
|
|
201
202
|
disableViewOptions?: boolean;
|
|
@@ -227,6 +228,7 @@ export function PaginatedListDataTable<
|
|
|
227
228
|
onPageChange,
|
|
228
229
|
onSortChange,
|
|
229
230
|
onFilterChange,
|
|
231
|
+
onColumnVisibilityChange,
|
|
230
232
|
facetedFilters,
|
|
231
233
|
rowActions,
|
|
232
234
|
disableViewOptions,
|
|
@@ -331,6 +333,10 @@ export function PaginatedListDataTable<
|
|
|
331
333
|
meta: { fieldInfo, isCustomField },
|
|
332
334
|
enableColumnFilter,
|
|
333
335
|
enableSorting: fieldInfo.isScalar,
|
|
336
|
+
// Filtering is done on the server side, but we set this to 'equalsString' because
|
|
337
|
+
// otherwise the TanStack Table with apply an "auto" function which somehow
|
|
338
|
+
// prevents certain filters from working.
|
|
339
|
+
filterFn: 'equalsString',
|
|
334
340
|
cell: ({ cell, row }) => {
|
|
335
341
|
const value = !isCustomField
|
|
336
342
|
? cell.getValue()
|
|
@@ -416,6 +422,7 @@ export function PaginatedListDataTable<
|
|
|
416
422
|
onPageChange={onPageChange}
|
|
417
423
|
onSortChange={onSortChange}
|
|
418
424
|
onFilterChange={onFilterChange}
|
|
425
|
+
onColumnVisibilityChange={onColumnVisibilityChange}
|
|
419
426
|
onSearchTermChange={onSearchTermChange ? term => setSearchTerm(term) : undefined}
|
|
420
427
|
defaultColumnVisibility={columnVisibility}
|
|
421
428
|
facetedFilters={facetedFilters}
|
|
@@ -434,6 +441,7 @@ function getRowActions(
|
|
|
434
441
|
id: 'actions',
|
|
435
442
|
accessorKey: 'actions',
|
|
436
443
|
header: 'Actions',
|
|
444
|
+
enableColumnFilter: false,
|
|
437
445
|
cell: ({ row }) => {
|
|
438
446
|
return (
|
|
439
447
|
<DropdownMenu>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command.js';
|
|
2
|
+
import {
|
|
3
|
+
Popover,
|
|
4
|
+
PopoverContent,
|
|
5
|
+
PopoverTrigger,
|
|
6
|
+
} from "@/components/ui/popover.js";
|
|
7
|
+
import { api } from '@/graphql/api.js';
|
|
8
|
+
import { graphql } from '@/graphql/graphql.js';
|
|
9
|
+
import { cn } from '@/lib/utils.js';
|
|
10
|
+
import { useQuery } from '@tanstack/react-query';
|
|
11
|
+
import { useDebounce } from '@uidotdev/usehooks';
|
|
12
|
+
import { ChevronsUpDown, Plus } from 'lucide-react';
|
|
13
|
+
import { useState } from 'react';
|
|
14
|
+
import { Button } from '../ui/button.js';
|
|
15
|
+
import { assetFragment } from '@/graphql/fragments.js';
|
|
16
|
+
import { VendureImage } from './vendure-image.js';
|
|
17
|
+
|
|
18
|
+
const productVariantListDocument = graphql(`
|
|
19
|
+
query ProductVariantList($options: ProductVariantListOptions) {
|
|
20
|
+
productVariants(options: $options) {
|
|
21
|
+
items {
|
|
22
|
+
id
|
|
23
|
+
name
|
|
24
|
+
sku
|
|
25
|
+
featuredAsset {
|
|
26
|
+
...Asset
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
totalItems
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`, [assetFragment]);
|
|
33
|
+
|
|
34
|
+
export interface ProductVariantSelectorProps {
|
|
35
|
+
onProductVariantIdChange: (productVariantId: string) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function ProductVariantSelector({ onProductVariantIdChange }: ProductVariantSelectorProps) {
|
|
39
|
+
const [search, setSearch] = useState('');
|
|
40
|
+
const [open, setOpen] = useState(false);
|
|
41
|
+
const debouncedSearch = useDebounce(search, 500);
|
|
42
|
+
|
|
43
|
+
const { data, isLoading } = useQuery({
|
|
44
|
+
queryKey: ['productVariants', debouncedSearch],
|
|
45
|
+
staleTime: 1000 * 60 * 5,
|
|
46
|
+
enabled: debouncedSearch.length > 0,
|
|
47
|
+
queryFn: () =>
|
|
48
|
+
api.query(productVariantListDocument, {
|
|
49
|
+
options: {
|
|
50
|
+
take: 10,
|
|
51
|
+
filter: {
|
|
52
|
+
name: { contains: debouncedSearch },
|
|
53
|
+
sku: { contains: debouncedSearch },
|
|
54
|
+
},
|
|
55
|
+
filterOperator: 'OR',
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
62
|
+
<PopoverTrigger asChild>
|
|
63
|
+
<Button
|
|
64
|
+
variant="outline"
|
|
65
|
+
role="combobox"
|
|
66
|
+
className="w-full"
|
|
67
|
+
>
|
|
68
|
+
Add item to order
|
|
69
|
+
<Plus className="opacity-50" />
|
|
70
|
+
</Button>
|
|
71
|
+
</PopoverTrigger>
|
|
72
|
+
<PopoverContent className="p-0">
|
|
73
|
+
<Command shouldFilter={false}>
|
|
74
|
+
<CommandInput
|
|
75
|
+
placeholder="Add item to order..."
|
|
76
|
+
className="h-9"
|
|
77
|
+
onValueChange={(value) => setSearch(value)}
|
|
78
|
+
/>
|
|
79
|
+
<CommandList>
|
|
80
|
+
<CommandEmpty>No products found.</CommandEmpty>
|
|
81
|
+
<CommandGroup>
|
|
82
|
+
{data?.productVariants.items.map((variant) => (
|
|
83
|
+
<CommandItem
|
|
84
|
+
key={variant.id}
|
|
85
|
+
value={variant.id}
|
|
86
|
+
onSelect={() => {
|
|
87
|
+
onProductVariantIdChange(variant.id);
|
|
88
|
+
setOpen(false);
|
|
89
|
+
}}
|
|
90
|
+
className="flex items-center gap-2 p-2"
|
|
91
|
+
>
|
|
92
|
+
{variant.featuredAsset && (
|
|
93
|
+
<VendureImage
|
|
94
|
+
asset={variant.featuredAsset}
|
|
95
|
+
preset="tiny"
|
|
96
|
+
className="size-8 rounded-md object-cover"
|
|
97
|
+
/>
|
|
98
|
+
)}
|
|
99
|
+
<div className="flex flex-col">
|
|
100
|
+
<span className="text-sm font-medium">{variant.name}</span>
|
|
101
|
+
<span className="text-xs text-muted-foreground">{variant.sku}</span>
|
|
102
|
+
</div>
|
|
103
|
+
</CommandItem>
|
|
104
|
+
))}
|
|
105
|
+
</CommandGroup>
|
|
106
|
+
</CommandList>
|
|
107
|
+
</Command>
|
|
108
|
+
</PopoverContent>
|
|
109
|
+
</Popover>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -70,7 +70,7 @@ export function VendureImage({
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// Apply focal point if available and requested
|
|
73
|
-
if (useFocalPoint && asset.focalPoint
|
|
73
|
+
if (useFocalPoint && asset.focalPoint) {
|
|
74
74
|
url.searchParams.set('fpx', asset.focalPoint.x.toString());
|
|
75
75
|
url.searchParams.set('fpy', asset.focalPoint.y.toString());
|
|
76
76
|
}
|