@vendure/dashboard 3.5.2-master-202512170238 → 3.5.2-master-202512190240
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/constants.js +21 -2
- package/dist/plugin/dashboard.plugin.js +1 -1
- package/package.json +3 -3
- package/src/app/routeTree.gen.ts +1135 -1072
- package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
- package/src/app/routes/_authenticated/_collections/collections.tsx +249 -167
- package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +8 -0
- package/src/app/routes/_authenticated/_collections/components/move-collections-dialog.tsx +4 -0
- package/src/app/routes/_authenticated/_facets/components/facet-values-sheet.tsx +4 -1
- package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +1 -1
- package/src/app/routes/_authenticated/_facets/facets.tsx +22 -38
- package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +16 -1
- package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +0 -1
- package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +2 -2
- package/src/app/routes/_authenticated/_products/products.graphql.ts +5 -0
- package/src/app/routes/_authenticated/_products/products_.$id.tsx +24 -1
- package/src/app/routes/_authenticated/_system/components/payload-dialog.tsx +9 -2
- package/src/app/routes/_authenticated/_system/job-queue.tsx +11 -2
- package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +2 -9
- package/src/app/routes/_authenticated/_zones/zones.tsx +1 -0
- package/src/i18n/locales/ar.po +177 -141
- package/src/i18n/locales/cs.po +177 -141
- package/src/i18n/locales/de.po +177 -141
- package/src/i18n/locales/en.po +177 -141
- package/src/i18n/locales/es.po +177 -141
- package/src/i18n/locales/fa.po +177 -141
- package/src/i18n/locales/fr.po +177 -141
- package/src/i18n/locales/he.po +177 -141
- package/src/i18n/locales/hr.po +177 -141
- package/src/i18n/locales/it.po +177 -141
- package/src/i18n/locales/ja.po +177 -141
- package/src/i18n/locales/nb.po +177 -141
- package/src/i18n/locales/ne.po +177 -141
- package/src/i18n/locales/pl.po +177 -141
- package/src/i18n/locales/pt_BR.po +177 -141
- package/src/i18n/locales/pt_PT.po +177 -141
- package/src/i18n/locales/ru.po +177 -141
- package/src/i18n/locales/sv.po +177 -141
- package/src/i18n/locales/tr.po +177 -141
- package/src/i18n/locales/uk.po +177 -141
- package/src/i18n/locales/zh_Hans.po +177 -141
- package/src/i18n/locales/zh_Hant.po +177 -141
- package/src/lib/components/data-input/number-input.tsx +24 -5
- package/src/lib/components/data-table/data-table-context.tsx +18 -3
- package/src/lib/components/data-table/data-table-utils.ts +241 -1
- package/src/lib/components/data-table/data-table.tsx +189 -60
- package/src/lib/components/data-table/global-views-bar.tsx +1 -1
- package/src/lib/components/data-table/save-view-dialog.tsx +21 -0
- package/src/lib/components/data-table/use-generated-columns.tsx +56 -24
- package/src/lib/components/data-table/views-sheet.tsx +1 -1
- package/src/lib/components/layout/channel-switcher.tsx +7 -5
- package/src/lib/components/layout/nav-main.tsx +2 -2
- package/src/lib/components/shared/alerts.tsx +3 -1
- package/src/lib/components/shared/assign-to-channel-bulk-action.tsx +10 -10
- package/src/lib/components/shared/assign-to-channel-dialog.tsx +1 -1
- package/src/lib/components/shared/assigned-channels.tsx +108 -0
- package/src/lib/components/shared/assigned-facet-values.tsx +5 -7
- package/src/lib/components/shared/channel-chip.tsx +43 -0
- package/src/lib/components/shared/paginated-list-data-table.tsx +19 -0
- package/src/lib/components/ui/alert.tsx +1 -1
- package/src/lib/components/ui/dropdown-menu.tsx +4 -1
- package/src/lib/components/ui/sidebar.tsx +2 -1
- package/src/lib/framework/page/list-page.tsx +62 -38
- package/src/lib/hooks/use-drag-and-drop.ts +86 -0
- package/src/lib/hooks/use-saved-views.ts +1 -0
- package/src/lib/providers/channel-provider.tsx +7 -1
- package/src/lib/types/saved-views.ts +3 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { Badge } from '@/vdb/components/ui/badge.js';
|
|
1
2
|
import { TableCell, TableRow } from '@/vdb/components/ui/table.js';
|
|
3
|
+
import { useLocalFormat } from '@/vdb/hooks/use-local-format.js';
|
|
2
4
|
import { Trans } from '@lingui/react/macro';
|
|
3
5
|
import { Order } from '../utils/order-types.js';
|
|
4
6
|
import { MoneyGrossNet } from './money-gross-net.js';
|
|
@@ -9,6 +11,7 @@ export interface OrderTableTotalsProps {
|
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export function OrderTableTotals({ order, columnCount }: Readonly<OrderTableTotalsProps>) {
|
|
14
|
+
const { formatCurrency } = useLocalFormat();
|
|
12
15
|
const currencyCode = order.currencyCode;
|
|
13
16
|
return (
|
|
14
17
|
<>
|
|
@@ -58,7 +61,19 @@ export function OrderTableTotals({ order, columnCount }: Readonly<OrderTableTota
|
|
|
58
61
|
</TableRow>
|
|
59
62
|
<TableRow>
|
|
60
63
|
<TableCell colSpan={columnCount - 1} className="h-12">
|
|
61
|
-
<
|
|
64
|
+
<div className="flex flex-wrap gap-1">
|
|
65
|
+
<div>
|
|
66
|
+
<Trans>Shipping</Trans>
|
|
67
|
+
</div>
|
|
68
|
+
{order.shippingLines.map(sl => (
|
|
69
|
+
<Badge variant="outline" key={sl.id}>
|
|
70
|
+
{sl.shippingMethod.name}
|
|
71
|
+
{order.shippingLines.length > 1
|
|
72
|
+
? ` (${formatCurrency(sl.discountedPriceWithTax, order.currencyCode)})`
|
|
73
|
+
: ''}
|
|
74
|
+
</Badge>
|
|
75
|
+
))}
|
|
76
|
+
</div>
|
|
62
77
|
</TableCell>
|
|
63
78
|
<TableCell colSpan={1} className="h-12">
|
|
64
79
|
<MoneyGrossNet
|
|
@@ -54,7 +54,7 @@ export const Route = createFileRoute('/_authenticated/_product-variants/product-
|
|
|
54
54
|
breadcrumb(_isNew, entity, location) {
|
|
55
55
|
if ((location.search as any).from === 'product') {
|
|
56
56
|
return [
|
|
57
|
-
{ path: '/
|
|
57
|
+
{ path: '/products', label: <Trans>Products</Trans> },
|
|
58
58
|
{ path: `/products/${entity?.product.id}`, label: entity?.product.name ?? '' },
|
|
59
59
|
entity?.name,
|
|
60
60
|
];
|
|
@@ -97,7 +97,7 @@ function ProductVariantDetailPage() {
|
|
|
97
97
|
prices: entity.prices,
|
|
98
98
|
trackInventory: entity.trackInventory,
|
|
99
99
|
outOfStockThreshold: entity.outOfStockThreshold,
|
|
100
|
-
useGlobalOutOfStockThreshold
|
|
100
|
+
useGlobalOutOfStockThreshold: entity.useGlobalOutOfStockThreshold,
|
|
101
101
|
stockLevels: entity.stockLevels.map(stockLevel => ({
|
|
102
102
|
stockOnHand: stockLevel.stockOnHand,
|
|
103
103
|
stockLocationId: stockLevel.stockLocation.id,
|
|
@@ -31,7 +31,16 @@ import { toast } from 'sonner';
|
|
|
31
31
|
import { CreateProductVariantsDialog } from './components/create-product-variants-dialog.js';
|
|
32
32
|
import { ProductOptionGroupBadge } from './components/product-option-group-badge.js';
|
|
33
33
|
import { ProductVariantsTable } from './components/product-variants-table.js';
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
assignProductsToChannelDocument,
|
|
36
|
+
createProductDocument,
|
|
37
|
+
productDetailDocument,
|
|
38
|
+
removeProductsFromChannelDocument,
|
|
39
|
+
updateProductDocument,
|
|
40
|
+
} from './products.graphql.js';
|
|
41
|
+
import { api } from '@/vdb/graphql/api.js';
|
|
42
|
+
import { AssignedChannels } from '@/vdb/components/shared/assigned-channels.js';
|
|
43
|
+
import { useChannel } from '@/vdb/hooks/use-channel.js';
|
|
35
44
|
|
|
36
45
|
const pageId = 'product-detail';
|
|
37
46
|
|
|
@@ -56,6 +65,7 @@ function ProductDetailPage() {
|
|
|
56
65
|
const creatingNewEntity = params.id === NEW_ENTITY_PATH;
|
|
57
66
|
const { t } = useLingui();
|
|
58
67
|
const refreshRef = useRef<() => void>(() => {});
|
|
68
|
+
const { channels } = useChannel();
|
|
59
69
|
|
|
60
70
|
const { form, submitHandler, entity, isPending, refreshEntity, resetForm } = useDetailPage({
|
|
61
71
|
pageId,
|
|
@@ -70,6 +80,7 @@ function ProductDetailPage() {
|
|
|
70
80
|
featuredAssetId: entity.featuredAsset?.id,
|
|
71
81
|
assetIds: entity.assets.map(asset => asset.id),
|
|
72
82
|
facetValueIds: entity.facetValues.map(facetValue => facetValue.id),
|
|
83
|
+
channelIds: entity.channels.map(c => c.id) ?? [],
|
|
73
84
|
translations: entity.translations.map(translation => ({
|
|
74
85
|
id: translation.id,
|
|
75
86
|
languageCode: translation.languageCode,
|
|
@@ -205,6 +216,18 @@ function ProductDetailPage() {
|
|
|
205
216
|
)}
|
|
206
217
|
/>
|
|
207
218
|
</PageBlock>
|
|
219
|
+
{channels.length > 1 && entity && (
|
|
220
|
+
<PageBlock column="side" blockId="channels" title={<Trans>Channels</Trans>}>
|
|
221
|
+
<AssignedChannels
|
|
222
|
+
channels={entity.channels}
|
|
223
|
+
entityId={entity.id}
|
|
224
|
+
canUpdate={!creatingNewEntity}
|
|
225
|
+
assignMutationFn={api.mutate(assignProductsToChannelDocument)}
|
|
226
|
+
removeMutationFn={api.mutate(removeProductsFromChannelDocument)}
|
|
227
|
+
/>
|
|
228
|
+
</PageBlock>
|
|
229
|
+
)}
|
|
230
|
+
|
|
208
231
|
<PageBlock column="side" blockId="assets" title={<Trans>Assets</Trans>}>
|
|
209
232
|
<FormItem>
|
|
210
233
|
<FormControl>
|
|
@@ -14,11 +14,18 @@ type PayloadDialogProps = {
|
|
|
14
14
|
trigger: React.ReactNode;
|
|
15
15
|
title?: string | React.ReactNode;
|
|
16
16
|
description?: string | React.ReactNode;
|
|
17
|
+
onOpenChange?: (open: boolean) => void;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
|
-
export function PayloadDialog({
|
|
20
|
+
export function PayloadDialog({
|
|
21
|
+
payload,
|
|
22
|
+
trigger,
|
|
23
|
+
title,
|
|
24
|
+
description,
|
|
25
|
+
onOpenChange,
|
|
26
|
+
}: Readonly<PayloadDialogProps>) {
|
|
20
27
|
return (
|
|
21
|
-
<Dialog>
|
|
28
|
+
<Dialog onOpenChange={open => onOpenChange?.(open)}>
|
|
22
29
|
<DialogTrigger asChild>{trigger}</DialogTrigger>
|
|
23
30
|
<DialogContent>
|
|
24
31
|
<DialogHeader>
|
|
@@ -78,12 +78,17 @@ function JobQueuePage() {
|
|
|
78
78
|
const refreshRef = useRef<() => void>(() => {});
|
|
79
79
|
const { t } = useLingui();
|
|
80
80
|
const [refreshInterval, setRefreshInterval] = useState(10000);
|
|
81
|
+
const isActionMenuOpenRef = useRef(false);
|
|
81
82
|
|
|
82
83
|
useEffect(() => {
|
|
83
84
|
if (refreshInterval === 0) return;
|
|
84
85
|
|
|
85
86
|
const interval = setInterval(() => {
|
|
86
|
-
|
|
87
|
+
// Pause auto-refresh while the row action dropdown is open
|
|
88
|
+
// to avoid closing it mid-interaction
|
|
89
|
+
if (!isActionMenuOpenRef.current) {
|
|
90
|
+
refreshRef.current();
|
|
91
|
+
}
|
|
87
92
|
}, refreshInterval);
|
|
88
93
|
|
|
89
94
|
return () => clearInterval(interval);
|
|
@@ -111,6 +116,7 @@ function JobQueuePage() {
|
|
|
111
116
|
<PayloadDialog
|
|
112
117
|
payload={row.original.data}
|
|
113
118
|
title={<Trans>View job data</Trans>}
|
|
119
|
+
onOpenChange={open => (isActionMenuOpenRef.current = open)}
|
|
114
120
|
description={<Trans>The data that has been passed to the job</Trans>}
|
|
115
121
|
trigger={
|
|
116
122
|
<Button size="sm" variant="secondary">
|
|
@@ -129,6 +135,7 @@ function JobQueuePage() {
|
|
|
129
135
|
<PayloadDialog
|
|
130
136
|
payload={row.original.result}
|
|
131
137
|
title={<Trans>View job result</Trans>}
|
|
138
|
+
onOpenChange={open => (isActionMenuOpenRef.current = open)}
|
|
132
139
|
description={<Trans>The result of the job</Trans>}
|
|
133
140
|
trigger={
|
|
134
141
|
<Button size="sm" variant="secondary">
|
|
@@ -168,7 +175,9 @@ function JobQueuePage() {
|
|
|
168
175
|
{row.original.state}
|
|
169
176
|
{row.original.state === 'RUNNING' ? (
|
|
170
177
|
<div className="flex items-center gap-2">
|
|
171
|
-
<DropdownMenu
|
|
178
|
+
<DropdownMenu
|
|
179
|
+
onOpenChange={open => (isActionMenuOpenRef.current = open)}
|
|
180
|
+
>
|
|
172
181
|
<DropdownMenuTrigger asChild>
|
|
173
182
|
<Button variant="ghost" size="sm" className="h-6 w-6 p-0">
|
|
174
183
|
<MoreVertical className="h-4 w-4" />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NumberInput } from '@/vdb/components/data-input/number-input.js';
|
|
2
2
|
import { ErrorPage } from '@/vdb/components/shared/error-page.js';
|
|
3
3
|
import { FormFieldWrapper } from '@/vdb/components/shared/form-field-wrapper.js';
|
|
4
4
|
import { PermissionGuard } from '@/vdb/components/shared/permission-guard.js';
|
|
@@ -121,14 +121,7 @@ function TaxRateDetailPage() {
|
|
|
121
121
|
name="value"
|
|
122
122
|
label={<Trans>Rate</Trans>}
|
|
123
123
|
render={({ field }) => (
|
|
124
|
-
<
|
|
125
|
-
{...field}
|
|
126
|
-
type="number"
|
|
127
|
-
suffix="%"
|
|
128
|
-
min={0}
|
|
129
|
-
value={field.value}
|
|
130
|
-
onChange={e => field.onChange(e.target.valueAsNumber)}
|
|
131
|
-
/>
|
|
124
|
+
<NumberInput {...field} value={field.value} min={0} step={0.01} suffix="%" />
|
|
132
125
|
)}
|
|
133
126
|
/>
|
|
134
127
|
<FormFieldWrapper
|