@vendure/dashboard 3.5.0-minor-202510071456 → 3.5.0-minor-202510161257

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 (201) hide show
  1. package/dist/plugin/dashboard.plugin.js +1 -1
  2. package/dist/vite/utils/ast-utils.spec.js +3 -3
  3. package/dist/vite/vite-plugin-hmr.d.ts +8 -0
  4. package/dist/vite/vite-plugin-hmr.js +34 -0
  5. package/dist/vite/vite-plugin-theme.js +6 -6
  6. package/dist/vite/vite-plugin-transform-index.js +6 -1
  7. package/dist/vite/vite-plugin-vendure-dashboard.d.ts +31 -4
  8. package/dist/vite/vite-plugin-vendure-dashboard.js +89 -34
  9. package/package.json +17 -5
  10. package/src/app/app-providers.tsx +4 -1
  11. package/src/app/common/map-faceted-filter-fields.ts +21 -0
  12. package/src/app/main.tsx +3 -1
  13. package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +2 -2
  14. package/src/app/routes/_authenticated/_administrators/administrators.tsx +13 -3
  15. package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +6 -13
  16. package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +1 -1
  17. package/src/app/routes/_authenticated/_assets/assets.tsx +17 -1
  18. package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
  19. package/src/app/routes/_authenticated/_collections/collections.tsx +5 -0
  20. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +0 -1
  21. package/src/app/routes/_authenticated/_customers/customers.tsx +9 -5
  22. package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +0 -6
  23. package/src/app/routes/_authenticated/_facets/components/facet-value-bulk-actions.tsx +16 -0
  24. package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +43 -12
  25. package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +14 -5
  26. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +117 -92
  27. package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +1 -1
  28. package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +2 -1
  29. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +26 -27
  30. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +5 -3
  31. package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +6 -9
  32. package/src/app/routes/_authenticated/_orders/orders.graphql.ts +17 -1
  33. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +48 -281
  34. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +59 -40
  35. package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +73 -0
  36. package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +312 -0
  37. package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +2 -2
  38. package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +4 -0
  39. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +0 -6
  40. package/src/app/routes/_authenticated/_products/products.tsx +6 -2
  41. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +4 -8
  42. package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +0 -10
  43. package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +2 -2
  44. package/src/app/routes/_authenticated/_promotions/promotions.tsx +12 -0
  45. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +6 -2
  46. package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +2 -2
  47. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +2 -2
  48. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +4 -0
  49. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +4 -10
  50. package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +2 -2
  51. package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +2 -2
  52. package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +9 -0
  53. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +1 -0
  54. package/src/app/routes/_authenticated/_zones/zones.graphql.ts +2 -2
  55. package/src/app/routes/login.tsx +2 -2
  56. package/src/i18n/locales/ar.po +420 -289
  57. package/src/i18n/locales/cs.po +420 -289
  58. package/src/i18n/locales/de.po +420 -289
  59. package/src/i18n/locales/en.po +420 -289
  60. package/src/i18n/locales/es.po +420 -289
  61. package/src/i18n/locales/fa.po +420 -289
  62. package/src/i18n/locales/fr.po +468 -337
  63. package/src/i18n/locales/he.po +420 -289
  64. package/src/i18n/locales/hr.po +420 -289
  65. package/src/i18n/locales/it.po +420 -289
  66. package/src/i18n/locales/ja.po +420 -289
  67. package/src/i18n/locales/nb.po +420 -289
  68. package/src/i18n/locales/ne.po +420 -289
  69. package/src/i18n/locales/pl.po +420 -289
  70. package/src/i18n/locales/pt_BR.po +420 -289
  71. package/src/i18n/locales/pt_PT.po +420 -289
  72. package/src/i18n/locales/ru.po +420 -289
  73. package/src/i18n/locales/sv.po +420 -289
  74. package/src/i18n/locales/tr.po +420 -289
  75. package/src/i18n/locales/uk.po +420 -289
  76. package/src/i18n/locales/zh_Hans.po +420 -289
  77. package/src/i18n/locales/zh_Hant.po +420 -289
  78. package/src/lib/components/data-input/affixed-input.stories.tsx +93 -0
  79. package/src/lib/components/data-input/affixed-input.tsx +5 -2
  80. package/src/lib/components/data-input/boolean-input.stories.tsx +102 -0
  81. package/src/lib/components/data-input/checkbox-input.stories.tsx +61 -0
  82. package/src/lib/components/data-input/datetime-input.stories.tsx +62 -0
  83. package/src/lib/components/data-input/datetime-input.tsx +27 -13
  84. package/src/lib/components/data-input/default-relation-input.tsx +18 -12
  85. package/src/lib/components/data-input/money-input.stories.tsx +88 -0
  86. package/src/lib/components/data-input/number-input.stories.tsx +103 -0
  87. package/src/lib/components/data-input/number-input.tsx +10 -4
  88. package/src/lib/components/data-input/password-input.stories.tsx +65 -0
  89. package/src/lib/components/data-input/rich-text-input.stories.tsx +92 -0
  90. package/src/lib/components/data-input/slug-input.stories.tsx +232 -0
  91. package/src/lib/components/data-input/slug-input.tsx +9 -10
  92. package/src/lib/components/data-input/text-input.stories.tsx +52 -0
  93. package/src/lib/components/data-input/textarea-input.stories.tsx +55 -0
  94. package/src/lib/components/data-table/add-filter-menu.tsx +6 -1
  95. package/src/lib/components/data-table/column-header-wrapper.tsx +106 -0
  96. package/src/lib/components/data-table/data-table-bulk-action-item.tsx +11 -9
  97. package/src/lib/components/data-table/data-table-bulk-actions.tsx +4 -4
  98. package/src/lib/components/data-table/data-table-column-header.tsx +17 -14
  99. package/src/lib/components/data-table/data-table-faceted-filter.tsx +33 -11
  100. package/src/lib/components/data-table/data-table-filter-badge-editable.tsx +35 -0
  101. package/src/lib/components/data-table/data-table-filter-badge.tsx +23 -16
  102. package/src/lib/components/data-table/data-table-filter-dialog.tsx +28 -8
  103. package/src/lib/components/data-table/data-table-pagination.tsx +23 -7
  104. package/src/lib/components/data-table/data-table.stories.tsx +249 -0
  105. package/src/lib/components/data-table/data-table.tsx +37 -9
  106. package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +79 -34
  107. package/src/lib/components/data-table/use-generated-columns.tsx +55 -27
  108. package/src/lib/components/layout/nav-user.tsx +19 -13
  109. package/src/lib/components/login/login-form.tsx +39 -123
  110. package/src/lib/components/shared/alerts.tsx +29 -17
  111. package/src/lib/components/shared/asset/asset-bulk-actions.tsx +3 -3
  112. package/src/lib/components/shared/asset/asset-gallery.stories.tsx +76 -0
  113. package/src/lib/components/shared/asset/asset-gallery.tsx +147 -113
  114. package/src/lib/components/shared/asset/asset-picker-dialog.stories.tsx +58 -0
  115. package/src/lib/components/shared/customer-group-selector.tsx +5 -2
  116. package/src/lib/components/shared/detail-page-button.stories.tsx +52 -0
  117. package/src/lib/components/shared/facet-value-selector.stories.tsx +48 -0
  118. package/src/lib/components/shared/facet-value-selector.tsx +130 -34
  119. package/src/lib/components/shared/paginated-list-data-table.stories.tsx +212 -0
  120. package/src/lib/components/shared/paginated-list-data-table.tsx +12 -12
  121. package/src/lib/components/shared/permission-guard.stories.tsx +46 -0
  122. package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +2 -0
  123. package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +8 -4
  124. package/src/lib/components/shared/rich-text-editor/rich-text-editor.tsx +1 -0
  125. package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +40 -0
  126. package/src/lib/components/shared/vendure-image.stories.tsx +167 -0
  127. package/src/lib/components/shared/vendure-image.tsx +6 -7
  128. package/src/lib/components/ui/accordion.stories.tsx +33 -0
  129. package/src/lib/components/ui/alert-dialog.stories.tsx +48 -0
  130. package/src/lib/components/ui/alert.stories.tsx +35 -0
  131. package/src/lib/components/ui/aspect-ratio.stories.tsx +28 -0
  132. package/src/lib/components/ui/badge.stories.tsx +28 -0
  133. package/src/lib/components/ui/breadcrumb.stories.tsx +41 -0
  134. package/src/lib/components/ui/button.stories.tsx +38 -0
  135. package/src/lib/components/ui/calendar.stories.tsx +22 -0
  136. package/src/lib/components/ui/card.stories.tsx +28 -0
  137. package/src/lib/components/ui/carousel.stories.tsx +34 -0
  138. package/src/lib/components/ui/checkbox.stories.tsx +31 -0
  139. package/src/lib/components/ui/collapsible.stories.tsx +39 -0
  140. package/src/lib/components/ui/command.stories.tsx +44 -0
  141. package/src/lib/components/ui/context-menu.stories.tsx +38 -0
  142. package/src/lib/components/ui/dialog.stories.tsx +52 -0
  143. package/src/lib/components/ui/drawer.stories.tsx +50 -0
  144. package/src/lib/components/ui/dropdown-menu.stories.tsx +41 -0
  145. package/src/lib/components/ui/hover-card.stories.tsx +38 -0
  146. package/src/lib/components/ui/input-group.tsx +148 -0
  147. package/src/lib/components/ui/input-otp.stories.tsx +30 -0
  148. package/src/lib/components/ui/input.stories.tsx +38 -0
  149. package/src/lib/components/ui/label.stories.tsx +24 -0
  150. package/src/lib/components/ui/menubar.stories.tsx +53 -0
  151. package/src/lib/components/ui/navigation-menu.stories.tsx +54 -0
  152. package/src/lib/components/ui/pagination.stories.tsx +51 -0
  153. package/src/lib/components/ui/password-input.stories.tsx +32 -0
  154. package/src/lib/components/ui/password-input.tsx +33 -0
  155. package/src/lib/components/ui/popover.stories.tsx +33 -0
  156. package/src/lib/components/ui/progress.stories.tsx +27 -0
  157. package/src/lib/components/ui/radio-group.stories.tsx +34 -0
  158. package/src/lib/components/ui/resizable.stories.tsx +32 -0
  159. package/src/lib/components/ui/scroll-area.stories.tsx +31 -0
  160. package/src/lib/components/ui/select.stories.tsx +36 -0
  161. package/src/lib/components/ui/separator.stories.tsx +35 -0
  162. package/src/lib/components/ui/sheet.stories.tsx +50 -0
  163. package/src/lib/components/ui/sidebar-context.ts +16 -0
  164. package/src/lib/components/ui/sidebar.tsx +2 -13
  165. package/src/lib/components/ui/skeleton.stories.tsx +26 -0
  166. package/src/lib/components/ui/slider.stories.tsx +37 -0
  167. package/src/lib/components/ui/switch.stories.tsx +31 -0
  168. package/src/lib/components/ui/table.stories.tsx +52 -0
  169. package/src/lib/components/ui/tabs.stories.tsx +29 -0
  170. package/src/lib/components/ui/textarea.stories.tsx +32 -0
  171. package/src/lib/components/ui/toggle-group.stories.tsx +31 -0
  172. package/src/lib/components/ui/toggle.stories.tsx +39 -0
  173. package/src/lib/components/ui/tooltip.stories.tsx +30 -0
  174. package/src/lib/components/ui/tooltip.tsx +2 -2
  175. package/src/lib/framework/alert/alert-extensions.tsx +0 -11
  176. package/src/lib/framework/alert/alert-item.tsx +14 -19
  177. package/src/lib/framework/alert/alerts-indicator.tsx +14 -15
  178. package/src/lib/framework/alert/search-index-buffer-alert/search-index-buffer-alert.ts +41 -0
  179. package/src/lib/framework/component-registry/component-registry.tsx +3 -14
  180. package/src/lib/framework/dashboard-widget/base-widget.tsx +18 -9
  181. package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +12 -11
  182. package/src/lib/framework/defaults.ts +9 -13
  183. package/src/lib/framework/extension-api/input-component-extensions.tsx +6 -1
  184. package/src/lib/framework/extension-api/logic/alerts.ts +3 -2
  185. package/src/lib/framework/extension-api/types/alerts.ts +12 -6
  186. package/src/lib/framework/extension-api/types/data-table.ts +5 -2
  187. package/src/lib/framework/extension-api/types/login.ts +0 -21
  188. package/src/lib/framework/layout-engine/custom-form-page.stories.tsx +344 -0
  189. package/src/lib/framework/layout-engine/page-layout.tsx +11 -9
  190. package/src/lib/framework/layout-engine/page.stories.tsx +275 -0
  191. package/src/lib/framework/nav-menu/nav-menu-extensions.ts +32 -19
  192. package/src/lib/framework/page/detail-page.stories.tsx +151 -0
  193. package/src/lib/framework/page/list-page.stories.tsx +217 -0
  194. package/src/lib/framework/page/list-page.tsx +8 -1
  195. package/src/lib/graphql/api.ts +18 -1
  196. package/src/lib/graphql/graphql-env.d.ts +1 -1
  197. package/src/lib/hooks/use-alerts.ts +84 -0
  198. package/src/lib/hooks/use-floating-bulk-actions.ts +2 -3
  199. package/src/lib/index.ts +12 -5
  200. package/src/lib/providers/alerts-provider.tsx +60 -0
  201. package/src/lib/providers/theme-provider.tsx +6 -3
@@ -0,0 +1,312 @@
1
+ import { VariablesOf } from 'gql.tada';
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
+
4
+ import { modifyOrderDocument } from '../orders.graphql.js';
5
+
6
+ import { AddressFragment, Order } from './order-types.js';
7
+
8
+ type ModifyOrderInput = VariablesOf<typeof modifyOrderDocument>['input'];
9
+
10
+ export type ProductVariantInfo = {
11
+ productVariantId: string;
12
+ productVariantName: string;
13
+ sku: string;
14
+ productAsset: {
15
+ preview: string;
16
+ };
17
+ price?: number;
18
+ priceWithTax?: number;
19
+ };
20
+
21
+ export interface UseModifyOrderReturn {
22
+ modifyOrderInput: ModifyOrderInput;
23
+ addedVariants: Map<string, ProductVariantInfo>;
24
+ addItem: (variant: ProductVariantInfo) => void;
25
+ adjustLine: (params: {
26
+ lineId: string;
27
+ quantity: number;
28
+ customFields: Record<string, any> | undefined;
29
+ }) => void;
30
+ removeLine: (params: { lineId: string }) => void;
31
+ setShippingMethod: (params: { shippingMethodId: string }) => void;
32
+ applyCouponCode: (params: { couponCode: string }) => void;
33
+ removeCouponCode: (params: { couponCode: string }) => void;
34
+ updateShippingAddress: (address: AddressFragment) => void;
35
+ updateBillingAddress: (address: AddressFragment) => void;
36
+ hasModifications: boolean;
37
+ }
38
+
39
+ /**
40
+ * Custom hook to manage order modification state and operations
41
+ */
42
+ export function useModifyOrder(order: Order | null | undefined): UseModifyOrderReturn {
43
+ const [modifyOrderInput, setModifyOrderInput] = useState<ModifyOrderInput>({
44
+ orderId: '',
45
+ addItems: [],
46
+ adjustOrderLines: [],
47
+ surcharges: [],
48
+ note: '',
49
+ couponCodes: [],
50
+ options: {
51
+ recalculateShipping: true,
52
+ },
53
+ dryRun: true,
54
+ });
55
+
56
+ const [addedVariants, setAddedVariants] = useState<Map<string, ProductVariantInfo>>(new Map());
57
+
58
+ // Sync orderId and couponCodes when order changes
59
+ useEffect(() => {
60
+ setModifyOrderInput(prev => ({
61
+ ...prev,
62
+ orderId: order?.id ?? '',
63
+ couponCodes: order?.couponCodes ?? [],
64
+ }));
65
+ }, [order?.id]);
66
+
67
+ // Add item or increment existing line
68
+ const addItem = useCallback(
69
+ (variant: ProductVariantInfo) => {
70
+ setModifyOrderInput(prev => {
71
+ const existingLine = order?.lines.find(
72
+ line => line.productVariant.id === variant.productVariantId,
73
+ );
74
+
75
+ if (existingLine) {
76
+ const existingAdjustment = prev.adjustOrderLines?.find(
77
+ adj => adj.orderLineId === existingLine.id,
78
+ );
79
+
80
+ if (existingAdjustment) {
81
+ const newQuantity = existingAdjustment.quantity + 1;
82
+
83
+ // If back to original quantity, remove from adjustments
84
+ if (newQuantity === existingLine.quantity) {
85
+ return {
86
+ ...prev,
87
+ adjustOrderLines: (prev.adjustOrderLines ?? []).filter(
88
+ adj => adj.orderLineId !== existingLine.id,
89
+ ),
90
+ };
91
+ }
92
+
93
+ return {
94
+ ...prev,
95
+ adjustOrderLines:
96
+ prev.adjustOrderLines?.map(adj =>
97
+ adj.orderLineId === existingLine.id
98
+ ? { ...adj, quantity: newQuantity }
99
+ : adj,
100
+ ) ?? [],
101
+ };
102
+ } else {
103
+ return {
104
+ ...prev,
105
+ adjustOrderLines: [
106
+ ...(prev.adjustOrderLines ?? []),
107
+ { orderLineId: existingLine.id, quantity: existingLine.quantity + 1 },
108
+ ],
109
+ };
110
+ }
111
+ } else {
112
+ return {
113
+ ...prev,
114
+ addItems: [
115
+ ...(prev.addItems ?? []),
116
+ { productVariantId: variant.productVariantId, quantity: 1 },
117
+ ],
118
+ };
119
+ }
120
+ });
121
+
122
+ setAddedVariants(prev => {
123
+ const newMap = new Map(prev);
124
+ newMap.set(variant.productVariantId, variant);
125
+ return newMap;
126
+ });
127
+ },
128
+ [order],
129
+ );
130
+
131
+ // Adjust line quantity or custom fields
132
+ const adjustLine = useCallback(
133
+ ({
134
+ lineId,
135
+ quantity,
136
+ customFields,
137
+ }: {
138
+ lineId: string;
139
+ quantity: number;
140
+ customFields: Record<string, any> | undefined;
141
+ }) => {
142
+ if (lineId.startsWith('added-')) {
143
+ const productVariantId = lineId.replace('added-', '');
144
+ setModifyOrderInput(prev => ({
145
+ ...prev,
146
+ addItems: (prev.addItems ?? []).map(item =>
147
+ item.productVariantId === productVariantId ? { ...item, quantity } : item,
148
+ ),
149
+ }));
150
+ } else {
151
+ let normalizedCustomFields: Record<string, any> | undefined = customFields;
152
+ if (normalizedCustomFields) {
153
+ delete normalizedCustomFields.__entityId__;
154
+ }
155
+ if (normalizedCustomFields && Object.keys(normalizedCustomFields).length === 0) {
156
+ normalizedCustomFields = undefined;
157
+ }
158
+
159
+ setModifyOrderInput(prev => {
160
+ const originalLine = order?.lines.find(l => l.id === lineId);
161
+ const isBackToOriginal = originalLine && originalLine.quantity === quantity;
162
+
163
+ const originalCustomFields = (originalLine as any)?.customFields;
164
+ const customFieldsChanged =
165
+ JSON.stringify(normalizedCustomFields) !== JSON.stringify(originalCustomFields);
166
+
167
+ const existing = (prev.adjustOrderLines ?? []).find(l => l.orderLineId === lineId);
168
+
169
+ // If back to original and custom fields unchanged, remove from adjustments
170
+ if (isBackToOriginal && !customFieldsChanged) {
171
+ return {
172
+ ...prev,
173
+ adjustOrderLines: (prev.adjustOrderLines ?? []).filter(
174
+ l => l.orderLineId !== lineId,
175
+ ),
176
+ };
177
+ }
178
+
179
+ const adjustOrderLines = existing
180
+ ? (prev.adjustOrderLines ?? []).map(l =>
181
+ l.orderLineId === lineId
182
+ ? { ...l, quantity, customFields: normalizedCustomFields }
183
+ : l,
184
+ )
185
+ : [
186
+ ...(prev.adjustOrderLines ?? []),
187
+ { orderLineId: lineId, quantity, customFields: normalizedCustomFields },
188
+ ];
189
+ return { ...prev, adjustOrderLines };
190
+ });
191
+ }
192
+ },
193
+ [order],
194
+ );
195
+
196
+ // Remove line
197
+ const removeLine = useCallback(({ lineId }: { lineId: string }) => {
198
+ if (lineId.startsWith('added-')) {
199
+ const productVariantId = lineId.replace('added-', '');
200
+ setModifyOrderInput(prev => ({
201
+ ...prev,
202
+ addItems: (prev.addItems ?? []).filter(item => item.productVariantId !== productVariantId),
203
+ }));
204
+ setAddedVariants(prev => {
205
+ const newMap = new Map(prev);
206
+ newMap.delete(productVariantId);
207
+ return newMap;
208
+ });
209
+ } else {
210
+ setModifyOrderInput(prev => {
211
+ const existingAdjustment = (prev.adjustOrderLines ?? []).find(l => l.orderLineId === lineId);
212
+ const adjustOrderLines = existingAdjustment
213
+ ? (prev.adjustOrderLines ?? []).map(l =>
214
+ l.orderLineId === lineId ? { ...l, quantity: 0 } : l,
215
+ )
216
+ : [...(prev.adjustOrderLines ?? []), { orderLineId: lineId, quantity: 0 }];
217
+ return {
218
+ ...prev,
219
+ adjustOrderLines,
220
+ };
221
+ });
222
+ }
223
+ }, []);
224
+
225
+ // Set shipping method
226
+ const setShippingMethod = useCallback(({ shippingMethodId }: { shippingMethodId: string }) => {
227
+ setModifyOrderInput(prev => ({
228
+ ...prev,
229
+ shippingMethodIds: [shippingMethodId],
230
+ }));
231
+ }, []);
232
+
233
+ // Apply coupon code
234
+ const applyCouponCode = useCallback(({ couponCode }: { couponCode: string }) => {
235
+ setModifyOrderInput(prev => ({
236
+ ...prev,
237
+ couponCodes: prev.couponCodes?.includes(couponCode)
238
+ ? prev.couponCodes
239
+ : [...(prev.couponCodes ?? []), couponCode],
240
+ }));
241
+ }, []);
242
+
243
+ // Remove coupon code
244
+ const removeCouponCode = useCallback(({ couponCode }: { couponCode: string }) => {
245
+ setModifyOrderInput(prev => ({
246
+ ...prev,
247
+ couponCodes: (prev.couponCodes ?? []).filter(code => code !== couponCode),
248
+ }));
249
+ }, []);
250
+
251
+ // Update shipping address
252
+ const updateShippingAddress = useCallback((address: AddressFragment) => {
253
+ setModifyOrderInput(prev => ({
254
+ ...prev,
255
+ updateShippingAddress: {
256
+ streetLine1: address.streetLine1,
257
+ streetLine2: address.streetLine2,
258
+ city: address.city,
259
+ countryCode: address.country.code,
260
+ fullName: address.fullName,
261
+ postalCode: address.postalCode,
262
+ province: address.province,
263
+ company: address.company,
264
+ phoneNumber: address.phoneNumber,
265
+ },
266
+ }));
267
+ }, []);
268
+
269
+ // Update billing address
270
+ const updateBillingAddress = useCallback((address: AddressFragment) => {
271
+ setModifyOrderInput(prev => ({
272
+ ...prev,
273
+ updateBillingAddress: {
274
+ streetLine1: address.streetLine1,
275
+ streetLine2: address.streetLine2,
276
+ city: address.city,
277
+ countryCode: address.country.code,
278
+ fullName: address.fullName,
279
+ postalCode: address.postalCode,
280
+ province: address.province,
281
+ company: address.company,
282
+ phoneNumber: address.phoneNumber,
283
+ },
284
+ }));
285
+ }, []);
286
+
287
+ // Check if there are modifications
288
+ const hasModifications = useMemo(() => {
289
+ return (
290
+ (modifyOrderInput.addItems?.length ?? 0) > 0 ||
291
+ (modifyOrderInput.adjustOrderLines?.length ?? 0) > 0 ||
292
+ (modifyOrderInput.couponCodes?.length ?? 0) > 0 ||
293
+ (modifyOrderInput.shippingMethodIds?.length ?? 0) > 0 ||
294
+ !!modifyOrderInput.updateShippingAddress ||
295
+ !!modifyOrderInput.updateBillingAddress
296
+ );
297
+ }, [modifyOrderInput]);
298
+
299
+ return {
300
+ modifyOrderInput,
301
+ addedVariants,
302
+ addItem,
303
+ adjustLine,
304
+ removeLine,
305
+ setShippingMethod,
306
+ applyCouponCode,
307
+ removeCouponCode,
308
+ updateShippingAddress,
309
+ updateBillingAddress,
310
+ hasModifications,
311
+ };
312
+ }
@@ -15,8 +15,8 @@ export const paymentMethodItemFragment = graphql(`
15
15
 
16
16
  export const paymentMethodListQuery = graphql(
17
17
  `
18
- query PaymentMethodList {
19
- paymentMethods {
18
+ query PaymentMethodList($options: PaymentMethodListOptions) {
19
+ paymentMethods(options: $options) {
20
20
  items {
21
21
  ...PaymentMethodItem
22
22
  }
@@ -1,6 +1,7 @@
1
1
  import { BooleanDisplayBadge } from '@/vdb/components/data-display/boolean.js';
2
2
  import { DetailPageButton } from '@/vdb/components/shared/detail-page-button.js';
3
3
  import { PermissionGuard } from '@/vdb/components/shared/permission-guard.js';
4
+ import { RichTextDescriptionCell } from '@/vdb/components/shared/table-cell/order-table-cell-components.js';
4
5
  import { Button } from '@/vdb/components/ui/button.js';
5
6
  import { PageActionBarRight } from '@/vdb/framework/layout-engine/page-layout.js';
6
7
  import { ListPage } from '@/vdb/framework/page/list-page.js';
@@ -53,6 +54,9 @@ function PaymentMethodListPage() {
53
54
  enabled: {
54
55
  cell: ({ row }) => <BooleanDisplayBadge value={row.original.enabled} />,
55
56
  },
57
+ description: {
58
+ cell: RichTextDescriptionCell,
59
+ },
56
60
  }}
57
61
  bulkActions={[
58
62
  {
@@ -108,12 +108,6 @@ export const DuplicateProductsBulkAction: BulkActionComponent<any> = ({ selectio
108
108
  <DuplicateBulkAction
109
109
  entityType="Product"
110
110
  duplicatorCode="product-duplicator"
111
- duplicatorArguments={[
112
- {
113
- name: 'includeVariants',
114
- value: 'true',
115
- },
116
- ]}
117
111
  requiredPermissions={['UpdateCatalog', 'UpdateProduct']}
118
112
  entityName="Product"
119
113
  selection={selection}
@@ -1,5 +1,6 @@
1
1
  import { DetailPageButton } from '@/vdb/components/shared/detail-page-button.js';
2
2
  import { PermissionGuard } from '@/vdb/components/shared/permission-guard.js';
3
+ import { RichTextDescriptionCell } from '@/vdb/components/shared/table-cell/order-table-cell-components.js';
3
4
  import { Button } from '@/vdb/components/ui/button.js';
4
5
  import { PageActionBarRight } from '@/vdb/framework/layout-engine/page-layout.js';
5
6
  import { ListPage } from '@/vdb/framework/page/list-page.js';
@@ -7,7 +8,7 @@ import { api } from '@/vdb/graphql/api.js';
7
8
  import { Trans, useLingui } from '@lingui/react/macro';
8
9
  import { useMutation } from '@tanstack/react-query';
9
10
  import { createFileRoute, Link } from '@tanstack/react-router';
10
- import { PlusIcon, RefreshCwIcon } from 'lucide-react';
11
+ import { ListRestart, PlusIcon } from 'lucide-react';
11
12
  import { toast } from 'sonner';
12
13
  import {
13
14
  AssignFacetValuesToProductsBulkAction,
@@ -48,6 +49,9 @@ function ProductListPage() {
48
49
  name: {
49
50
  cell: ({ row }) => <DetailPageButton id={row.original.id} label={row.original.name} />,
50
51
  },
52
+ description: {
53
+ cell: RichTextDescriptionCell,
54
+ },
51
55
  }}
52
56
  onSearchTermChange={searchTerm => {
53
57
  return {
@@ -87,7 +91,7 @@ function ProductListPage() {
87
91
  <PageActionBarRight>
88
92
  <PermissionGuard requires={['UpdateCatalog']}>
89
93
  <Button variant="outline" onClick={handleRebuildSearchIndex}>
90
- <RefreshCwIcon />
94
+ <ListRestart />
91
95
  <Trans>Rebuild search index</Trans>
92
96
  </Button>
93
97
  </PermissionGuard>
@@ -124,11 +124,9 @@ function ProductOptionDetailPage() {
124
124
  params: { id: params.id },
125
125
  onSuccess: async data => {
126
126
  toast(
127
- i18n.t(
128
- creatingNewEntity
129
- ? 'Successfully created product option'
130
- : 'Successfully updated product option',
131
- ),
127
+ creatingNewEntity
128
+ ? t`Successfully created product option`
129
+ : t`Successfully updated product option`,
132
130
  );
133
131
  resetForm();
134
132
  const created = Array.isArray(data) ? data[0] : data;
@@ -138,9 +136,7 @@ function ProductOptionDetailPage() {
138
136
  },
139
137
  onError: err => {
140
138
  toast(
141
- i18n.t(
142
- creatingNewEntity ? 'Failed to create product option' : 'Failed to update product option',
143
- ),
139
+ creatingNewEntity ? t`Failed to create product option` : t`Failed to update product option`,
144
140
  {
145
141
  description: err instanceof Error ? err.message : 'Unknown error',
146
142
  },
@@ -63,16 +63,6 @@ export const DuplicatePromotionsBulkAction: BulkActionComponent<any> = ({ select
63
63
  <DuplicateBulkAction
64
64
  entityType="Promotion"
65
65
  duplicatorCode="promotion-duplicator"
66
- duplicatorArguments={[
67
- {
68
- name: 'includeConditions',
69
- value: 'true',
70
- },
71
- {
72
- name: 'includeActions',
73
- value: 'true',
74
- },
75
- ]}
76
66
  requiredPermissions={['CreatePromotion']}
77
67
  entityName="Promotion"
78
68
  selection={selection}
@@ -2,8 +2,8 @@ import { configurableOperationFragment } from '@/vdb/graphql/fragments.js';
2
2
  import { graphql } from '@/vdb/graphql/graphql.js';
3
3
 
4
4
  export const promotionListDocument = graphql(`
5
- query PromotionList {
6
- promotions {
5
+ query PromotionList($options: PromotionListOptions) {
6
+ promotions(options: $options) {
7
7
  items {
8
8
  id
9
9
  createdAt
@@ -1,6 +1,7 @@
1
1
  import { BooleanDisplayBadge } from '@/vdb/components/data-display/boolean.js';
2
2
  import { DetailPageButton } from '@/vdb/components/shared/detail-page-button.js';
3
3
  import { PermissionGuard } from '@/vdb/components/shared/permission-guard.js';
4
+ import { RichTextDescriptionCell } from '@/vdb/components/shared/table-cell/order-table-cell-components.js';
4
5
  import { Button } from '@/vdb/components/ui/button.js';
5
6
  import { PageActionBarRight } from '@/vdb/framework/layout-engine/page-layout.js';
6
7
  import { ListPage } from '@/vdb/framework/page/list-page.js';
@@ -40,6 +41,14 @@ function PromotionListPage() {
40
41
  couponCode: { contains: searchTerm },
41
42
  };
42
43
  }}
44
+ transformVariables={variables => {
45
+ return {
46
+ options: {
47
+ ...variables.options,
48
+ filterOperator: 'OR' as const,
49
+ },
50
+ };
51
+ }}
43
52
  customizeColumns={{
44
53
  name: {
45
54
  cell: ({ row }) => <DetailPageButton id={row.original.id} label={row.original.name} />,
@@ -47,6 +56,9 @@ function PromotionListPage() {
47
56
  enabled: {
48
57
  cell: ({ row }) => <BooleanDisplayBadge value={row.original.enabled} />,
49
58
  },
59
+ description: {
60
+ cell: RichTextDescriptionCell,
61
+ },
50
62
  }}
51
63
  bulkActions={[
52
64
  {
@@ -203,13 +203,17 @@ function PromotionDetailPage() {
203
203
  control={form.control}
204
204
  name="perCustomerUsageLimit"
205
205
  label={<Trans>Per customer usage limit</Trans>}
206
- render={({ field }) => <NumberInput {...field} value={field.value ?? ''} />}
206
+ render={({ field }) => (
207
+ <NumberInput {...field} value={field.value ?? ''} min={0} max={1000} />
208
+ )}
207
209
  />
208
210
  <FormFieldWrapper
209
211
  control={form.control}
210
212
  name="usageLimit"
211
213
  label={<Trans>Usage limit</Trans>}
212
- render={({ field }) => <NumberInput {...field} value={field.value ?? ''} />}
214
+ render={({ field }) => (
215
+ <NumberInput {...field} value={field.value ?? ''} min={0} max={1000} />
216
+ )}
213
217
  />
214
218
  </DetailFormGrid>
215
219
  </PageBlock>
@@ -11,8 +11,8 @@ export const sellerItemFragment = graphql(`
11
11
 
12
12
  export const sellerListQuery = graphql(
13
13
  `
14
- query SellerList {
15
- sellers {
14
+ query SellerList($options: SellerListOptions) {
15
+ sellers(options: $options) {
16
16
  items {
17
17
  ...SellerItem
18
18
  }
@@ -15,8 +15,8 @@ export const shippingMethodItemFragment = graphql(`
15
15
 
16
16
  export const shippingMethodListQuery = graphql(
17
17
  `
18
- query ShippingMethodList {
19
- shippingMethods {
18
+ query ShippingMethodList($options: ShippingMethodListOptions) {
19
+ shippingMethods(options: $options) {
20
20
  items {
21
21
  ...ShippingMethodItem
22
22
  }
@@ -1,5 +1,6 @@
1
1
  import { DetailPageButton } from '@/vdb/components/shared/detail-page-button.js';
2
2
  import { PermissionGuard } from '@/vdb/components/shared/permission-guard.js';
3
+ import { RichTextDescriptionCell } from '@/vdb/components/shared/table-cell/order-table-cell-components.js';
3
4
  import { Button } from '@/vdb/components/ui/button.js';
4
5
  import { PageActionBarRight } from '@/vdb/framework/layout-engine/page-layout.js';
5
6
  import { ListPage } from '@/vdb/framework/page/list-page.js';
@@ -35,6 +36,9 @@ function ShippingMethodListPage() {
35
36
  name: {
36
37
  cell: ({ row }) => <DetailPageButton id={row.original.id} label={row.original.name} />,
37
38
  },
39
+ description: {
40
+ cell: RichTextDescriptionCell,
41
+ },
38
42
  }}
39
43
  onSearchTermChange={searchTerm => {
40
44
  return {
@@ -86,11 +86,9 @@ function ShippingMethodDetailPage() {
86
86
  params: { id: params.id },
87
87
  onSuccess: async data => {
88
88
  toast.success(
89
- i18n.t(
90
- creatingNewEntity
91
- ? 'Successfully created shipping method'
92
- : 'Successfully updated shipping method',
93
- ),
89
+ creatingNewEntity
90
+ ? t`Successfully created shipping method`
91
+ : t`Successfully updated shipping method`,
94
92
  );
95
93
  resetForm();
96
94
  if (creatingNewEntity) {
@@ -99,11 +97,7 @@ function ShippingMethodDetailPage() {
99
97
  },
100
98
  onError: err => {
101
99
  toast.error(
102
- i18n.t(
103
- creatingNewEntity
104
- ? 'Failed to create shipping method'
105
- : 'Failed to update shipping method',
106
- ),
100
+ creatingNewEntity ? t`Failed to create shipping method` : t`Failed to update shipping method`,
107
101
  {
108
102
  description: err instanceof Error ? err.message : 'Unknown error',
109
103
  },
@@ -12,8 +12,8 @@ export const stockLocationFragment = graphql(`
12
12
 
13
13
  export const stockLocationListQuery = graphql(
14
14
  `
15
- query StockLocationList {
16
- stockLocations {
15
+ query StockLocationList($options: StockLocationListOptions) {
16
+ stockLocations(options: $options) {
17
17
  items {
18
18
  ...StockLocationItem
19
19
  }
@@ -12,8 +12,8 @@ export const taxCategoryItemFragment = graphql(`
12
12
 
13
13
  export const taxCategoryListQuery = graphql(
14
14
  `
15
- query TaxCategoryList {
16
- taxCategories {
15
+ query TaxCategoryList($options: TaxCategoryListOptions) {
16
+ taxCategories(options: $options) {
17
17
  items {
18
18
  ...TaxCategoryItem
19
19
  }
@@ -8,6 +8,7 @@ import { api } from '@/vdb/graphql/api.js';
8
8
  import { Trans, useLingui } from '@lingui/react/macro';
9
9
  import { createFileRoute, Link } from '@tanstack/react-router';
10
10
  import { PlusIcon } from 'lucide-react';
11
+ import { mapFacetedFilterFields } from '../../../common/map-faceted-filter-fields.js';
11
12
  import { taxCategoryListQuery } from '../_tax-categories/tax-categories.graphql.js';
12
13
  import { zoneListQuery } from '../_zones/zones.graphql.js';
13
14
  import { DeleteTaxRatesBulkAction } from './components/tax-rate-bulk-actions.js';
@@ -42,6 +43,14 @@ function TaxRateListPage() {
42
43
  name: { contains: searchTerm },
43
44
  };
44
45
  }}
46
+ transformVariables={input => {
47
+ const facetedFilters = input.options?.filter?._and ?? [];
48
+ mapFacetedFilterFields(facetedFilters, {
49
+ category: 'categoryId',
50
+ zone: 'zoneId',
51
+ });
52
+ return input;
53
+ }}
45
54
  facetedFilters={{
46
55
  enabled: {
47
56
  title: t`Enabled`,
@@ -125,6 +125,7 @@ function TaxRateDetailPage() {
125
125
  {...field}
126
126
  type="number"
127
127
  suffix="%"
128
+ min={0}
128
129
  value={field.value}
129
130
  onChange={e => field.onChange(e.target.valueAsNumber)}
130
131
  />
@@ -11,8 +11,8 @@ export const zoneItemFragment = graphql(`
11
11
 
12
12
  export const zoneListQuery = graphql(
13
13
  `
14
- query ZoneList {
15
- zones {
14
+ query ZoneList($options: ZoneListOptions) {
15
+ zones(options: $options) {
16
16
  items {
17
17
  ...ZoneItem
18
18
  }
@@ -36,8 +36,8 @@ function LoginPage() {
36
36
  const isVerifying = isLoading || auth.status === 'verifying';
37
37
 
38
38
  return (
39
- <div className="flex min-h-svh flex-col items-center justify-center p-6 md:p-10">
40
- <div className="w-full max-w-sm md:max-w-4xl">
39
+ <div className="flex min-h-svh flex-col items-center justify-center p-6 md:p-10 bg-sidebar">
40
+ <div className="w-full max-w-sm md:max-w-md">
41
41
  <LoginForm
42
42
  onFormSubmit={onFormSubmit}
43
43
  isVerifying={isVerifying}