@vendure/dashboard 3.5.0-minor-202510031341 → 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 (220) hide show
  1. package/dist/plugin/dashboard.plugin.js +1 -1
  2. package/dist/plugin/default-page.html +1 -1
  3. package/dist/vite/utils/ast-utils.spec.js +3 -3
  4. package/dist/vite/utils/tsconfig-utils.js +2 -1
  5. package/dist/vite/vite-plugin-hmr.d.ts +8 -0
  6. package/dist/vite/vite-plugin-hmr.js +34 -0
  7. package/dist/vite/vite-plugin-theme.js +6 -6
  8. package/dist/vite/vite-plugin-transform-index.js +6 -1
  9. package/dist/vite/vite-plugin-vendure-dashboard.d.ts +31 -4
  10. package/dist/vite/vite-plugin-vendure-dashboard.js +89 -34
  11. package/package.json +18 -5
  12. package/src/app/app-providers.tsx +4 -1
  13. package/src/app/common/map-faceted-filter-fields.ts +21 -0
  14. package/src/app/main.tsx +3 -1
  15. package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +2 -2
  16. package/src/app/routes/_authenticated/_administrators/administrators.tsx +13 -3
  17. package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +6 -13
  18. package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +1 -1
  19. package/src/app/routes/_authenticated/_assets/assets.tsx +17 -1
  20. package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
  21. package/src/app/routes/_authenticated/_collections/collections.tsx +5 -0
  22. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +0 -1
  23. package/src/app/routes/_authenticated/_customers/customers.tsx +9 -5
  24. package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +0 -6
  25. package/src/app/routes/_authenticated/_facets/components/facet-value-bulk-actions.tsx +16 -0
  26. package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +43 -12
  27. package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +14 -5
  28. package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +4 -8
  29. package/src/app/routes/_authenticated/_global-settings/utils/global-languages.ts +268 -0
  30. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +117 -92
  31. package/src/app/routes/_authenticated/_orders/components/order-address.tsx +15 -15
  32. package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +5 -5
  33. package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +2 -1
  34. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +26 -27
  35. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +5 -3
  36. package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +6 -9
  37. package/src/app/routes/_authenticated/_orders/orders.graphql.ts +17 -1
  38. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +48 -281
  39. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +59 -40
  40. package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +73 -0
  41. package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +312 -0
  42. package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +2 -2
  43. package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +4 -0
  44. package/src/app/routes/_authenticated/_product-variants/components/add-currency-dropdown.tsx +49 -0
  45. package/src/app/routes/_authenticated/_product-variants/components/add-stock-location-dropdown.tsx +56 -0
  46. package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +12 -0
  47. package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +178 -50
  48. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +0 -6
  49. package/src/app/routes/_authenticated/_products/components/product-variants-table.tsx +0 -11
  50. package/src/app/routes/_authenticated/_products/products.tsx +6 -2
  51. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +4 -8
  52. package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +0 -10
  53. package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +2 -2
  54. package/src/app/routes/_authenticated/_promotions/promotions.tsx +12 -0
  55. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +3 -10
  56. package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +2 -2
  57. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +2 -2
  58. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +4 -0
  59. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +4 -10
  60. package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +2 -2
  61. package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +2 -2
  62. package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +9 -0
  63. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +1 -0
  64. package/src/app/routes/_authenticated/_zones/zones.graphql.ts +2 -2
  65. package/src/app/routes/login.tsx +2 -2
  66. package/src/i18n/locales/ar.po +420 -289
  67. package/src/i18n/locales/cs.po +420 -289
  68. package/src/i18n/locales/de.po +420 -289
  69. package/src/i18n/locales/en.po +420 -289
  70. package/src/i18n/locales/es.po +420 -289
  71. package/src/i18n/locales/fa.po +420 -289
  72. package/src/i18n/locales/fr.po +468 -337
  73. package/src/i18n/locales/he.po +420 -289
  74. package/src/i18n/locales/hr.po +420 -289
  75. package/src/i18n/locales/it.po +420 -289
  76. package/src/i18n/locales/ja.po +420 -289
  77. package/src/i18n/locales/nb.po +420 -289
  78. package/src/i18n/locales/ne.po +420 -289
  79. package/src/i18n/locales/pl.po +420 -289
  80. package/src/i18n/locales/pt_BR.po +420 -289
  81. package/src/i18n/locales/pt_PT.po +420 -289
  82. package/src/i18n/locales/ru.po +420 -289
  83. package/src/i18n/locales/sv.po +420 -289
  84. package/src/i18n/locales/tr.po +420 -289
  85. package/src/i18n/locales/uk.po +420 -289
  86. package/src/i18n/locales/zh_Hans.po +420 -289
  87. package/src/i18n/locales/zh_Hant.po +420 -289
  88. package/src/lib/components/data-input/affixed-input.stories.tsx +93 -0
  89. package/src/lib/components/data-input/affixed-input.tsx +5 -2
  90. package/src/lib/components/data-input/boolean-input.stories.tsx +102 -0
  91. package/src/lib/components/data-input/checkbox-input.stories.tsx +61 -0
  92. package/src/lib/components/data-input/customer-group-input.tsx +0 -1
  93. package/src/lib/components/data-input/datetime-input.stories.tsx +62 -0
  94. package/src/lib/components/data-input/datetime-input.tsx +27 -13
  95. package/src/lib/components/data-input/default-relation-input.tsx +18 -12
  96. package/src/lib/components/data-input/money-input.stories.tsx +88 -0
  97. package/src/lib/components/data-input/money-input.tsx +7 -11
  98. package/src/lib/components/data-input/number-input.stories.tsx +103 -0
  99. package/src/lib/components/data-input/number-input.tsx +16 -5
  100. package/src/lib/components/data-input/password-input.stories.tsx +65 -0
  101. package/src/lib/components/data-input/rich-text-input.stories.tsx +92 -0
  102. package/src/lib/components/data-input/slug-input.stories.tsx +232 -0
  103. package/src/lib/components/data-input/slug-input.tsx +9 -10
  104. package/src/lib/components/data-input/text-input.stories.tsx +52 -0
  105. package/src/lib/components/data-input/textarea-input.stories.tsx +55 -0
  106. package/src/lib/components/data-table/add-filter-menu.tsx +6 -1
  107. package/src/lib/components/data-table/column-header-wrapper.tsx +106 -0
  108. package/src/lib/components/data-table/data-table-bulk-action-item.tsx +11 -9
  109. package/src/lib/components/data-table/data-table-bulk-actions.tsx +4 -4
  110. package/src/lib/components/data-table/data-table-column-header.tsx +17 -14
  111. package/src/lib/components/data-table/data-table-faceted-filter.tsx +33 -11
  112. package/src/lib/components/data-table/data-table-filter-badge-editable.tsx +35 -0
  113. package/src/lib/components/data-table/data-table-filter-badge.tsx +28 -14
  114. package/src/lib/components/data-table/data-table-filter-dialog.tsx +28 -8
  115. package/src/lib/components/data-table/data-table-pagination.tsx +23 -7
  116. package/src/lib/components/data-table/data-table.stories.tsx +249 -0
  117. package/src/lib/components/data-table/data-table.tsx +39 -11
  118. package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +79 -34
  119. package/src/lib/components/data-table/use-generated-columns.tsx +55 -27
  120. package/src/lib/components/layout/generated-breadcrumbs.tsx +4 -12
  121. package/src/lib/components/layout/nav-user.tsx +19 -13
  122. package/src/lib/components/login/login-form.tsx +39 -123
  123. package/src/lib/components/shared/alerts.tsx +29 -17
  124. package/src/lib/components/shared/asset/asset-bulk-actions.tsx +3 -3
  125. package/src/lib/components/shared/asset/asset-gallery.stories.tsx +76 -0
  126. package/src/lib/components/shared/asset/asset-gallery.tsx +147 -113
  127. package/src/lib/components/shared/asset/asset-picker-dialog.stories.tsx +58 -0
  128. package/src/lib/components/shared/configurable-operation-input.tsx +1 -1
  129. package/src/lib/components/shared/customer-group-selector.tsx +5 -2
  130. package/src/lib/components/shared/detail-page-button.stories.tsx +52 -0
  131. package/src/lib/components/shared/facet-value-selector.stories.tsx +48 -0
  132. package/src/lib/components/shared/facet-value-selector.tsx +130 -34
  133. package/src/lib/components/shared/paginated-list-data-table.stories.tsx +212 -0
  134. package/src/lib/components/shared/paginated-list-data-table.tsx +12 -12
  135. package/src/lib/components/shared/permission-guard.stories.tsx +46 -0
  136. package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +2 -0
  137. package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +8 -4
  138. package/src/lib/components/shared/rich-text-editor/rich-text-editor.tsx +1 -0
  139. package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +40 -0
  140. package/src/lib/components/shared/vendure-image.stories.tsx +167 -0
  141. package/src/lib/components/shared/vendure-image.tsx +6 -7
  142. package/src/lib/components/ui/accordion.stories.tsx +33 -0
  143. package/src/lib/components/ui/alert-dialog.stories.tsx +48 -0
  144. package/src/lib/components/ui/alert.stories.tsx +35 -0
  145. package/src/lib/components/ui/aspect-ratio.stories.tsx +28 -0
  146. package/src/lib/components/ui/badge.stories.tsx +28 -0
  147. package/src/lib/components/ui/breadcrumb.stories.tsx +41 -0
  148. package/src/lib/components/ui/button.stories.tsx +38 -0
  149. package/src/lib/components/ui/calendar.stories.tsx +22 -0
  150. package/src/lib/components/ui/card.stories.tsx +28 -0
  151. package/src/lib/components/ui/carousel.stories.tsx +34 -0
  152. package/src/lib/components/ui/checkbox.stories.tsx +31 -0
  153. package/src/lib/components/ui/collapsible.stories.tsx +39 -0
  154. package/src/lib/components/ui/command.stories.tsx +44 -0
  155. package/src/lib/components/ui/context-menu.stories.tsx +38 -0
  156. package/src/lib/components/ui/dialog.stories.tsx +52 -0
  157. package/src/lib/components/ui/drawer.stories.tsx +50 -0
  158. package/src/lib/components/ui/dropdown-menu.stories.tsx +41 -0
  159. package/src/lib/components/ui/hover-card.stories.tsx +38 -0
  160. package/src/lib/components/ui/input-group.tsx +148 -0
  161. package/src/lib/components/ui/input-otp.stories.tsx +30 -0
  162. package/src/lib/components/ui/input.stories.tsx +38 -0
  163. package/src/lib/components/ui/label.stories.tsx +24 -0
  164. package/src/lib/components/ui/menubar.stories.tsx +53 -0
  165. package/src/lib/components/ui/navigation-menu.stories.tsx +54 -0
  166. package/src/lib/components/ui/pagination.stories.tsx +51 -0
  167. package/src/lib/components/ui/password-input.stories.tsx +32 -0
  168. package/src/lib/components/ui/password-input.tsx +33 -0
  169. package/src/lib/components/ui/popover.stories.tsx +33 -0
  170. package/src/lib/components/ui/progress.stories.tsx +27 -0
  171. package/src/lib/components/ui/radio-group.stories.tsx +34 -0
  172. package/src/lib/components/ui/resizable.stories.tsx +32 -0
  173. package/src/lib/components/ui/scroll-area.stories.tsx +31 -0
  174. package/src/lib/components/ui/select.stories.tsx +36 -0
  175. package/src/lib/components/ui/separator.stories.tsx +35 -0
  176. package/src/lib/components/ui/sheet.stories.tsx +50 -0
  177. package/src/lib/components/ui/sidebar-context.ts +16 -0
  178. package/src/lib/components/ui/sidebar.tsx +2 -13
  179. package/src/lib/components/ui/skeleton.stories.tsx +26 -0
  180. package/src/lib/components/ui/slider.stories.tsx +37 -0
  181. package/src/lib/components/ui/switch.stories.tsx +31 -0
  182. package/src/lib/components/ui/table.stories.tsx +52 -0
  183. package/src/lib/components/ui/tabs.stories.tsx +29 -0
  184. package/src/lib/components/ui/textarea.stories.tsx +32 -0
  185. package/src/lib/components/ui/toggle-group.stories.tsx +31 -0
  186. package/src/lib/components/ui/toggle.stories.tsx +39 -0
  187. package/src/lib/components/ui/tooltip.stories.tsx +30 -0
  188. package/src/lib/components/ui/tooltip.tsx +2 -2
  189. package/src/lib/framework/alert/alert-extensions.tsx +0 -11
  190. package/src/lib/framework/alert/alert-item.tsx +14 -19
  191. package/src/lib/framework/alert/alerts-indicator.tsx +14 -15
  192. package/src/lib/framework/alert/search-index-buffer-alert/search-index-buffer-alert.ts +41 -0
  193. package/src/lib/framework/component-registry/component-registry.tsx +3 -14
  194. package/src/lib/framework/dashboard-widget/base-widget.tsx +18 -9
  195. package/src/lib/framework/dashboard-widget/latest-orders-widget/index.tsx +0 -2
  196. package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +12 -11
  197. package/src/lib/framework/defaults.ts +9 -13
  198. package/src/lib/framework/extension-api/input-component-extensions.tsx +6 -1
  199. package/src/lib/framework/extension-api/logic/alerts.ts +3 -2
  200. package/src/lib/framework/extension-api/types/alerts.ts +12 -6
  201. package/src/lib/framework/extension-api/types/data-table.ts +5 -2
  202. package/src/lib/framework/extension-api/types/layout.ts +41 -1
  203. package/src/lib/framework/extension-api/types/login.ts +0 -21
  204. package/src/lib/framework/form-engine/value-transformers.ts +8 -1
  205. package/src/lib/framework/layout-engine/custom-form-page.stories.tsx +344 -0
  206. package/src/lib/framework/layout-engine/page-layout.tsx +69 -57
  207. package/src/lib/framework/layout-engine/page.stories.tsx +275 -0
  208. package/src/lib/framework/nav-menu/nav-menu-extensions.ts +32 -19
  209. package/src/lib/framework/page/detail-page.stories.tsx +151 -0
  210. package/src/lib/framework/page/detail-page.tsx +12 -15
  211. package/src/lib/framework/page/list-page.stories.tsx +217 -0
  212. package/src/lib/framework/page/list-page.tsx +8 -1
  213. package/src/lib/graphql/api.ts +18 -1
  214. package/src/lib/graphql/graphql-env.d.ts +1 -1
  215. package/src/lib/hooks/use-alerts.ts +84 -0
  216. package/src/lib/hooks/use-floating-bulk-actions.ts +2 -3
  217. package/src/lib/index.ts +12 -5
  218. package/src/lib/providers/alerts-provider.tsx +60 -0
  219. package/src/lib/providers/channel-provider.tsx +1 -0
  220. 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
  {
@@ -0,0 +1,49 @@
1
+ import { useLingui } from '@lingui/react/macro';
2
+ import { PlusIcon } from 'lucide-react';
3
+
4
+ import { Button } from '@/vdb/components/ui/button';
5
+ import {
6
+ DropdownMenu,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ DropdownMenuTrigger,
10
+ } from '@/vdb/components/ui/dropdown-menu';
11
+ import { useLocalFormat } from '@/vdb/hooks/use-local-format';
12
+
13
+ interface AddCurrencyDropdownProps {
14
+ unusedCurrencies: string[];
15
+ onCurrencySelect: (currencyCode: string) => void;
16
+ placeholder?: string;
17
+ }
18
+
19
+ export function AddCurrencyDropdown({
20
+ unusedCurrencies,
21
+ onCurrencySelect,
22
+ placeholder,
23
+ }: AddCurrencyDropdownProps) {
24
+ const { formatCurrencyName } = useLocalFormat();
25
+ const { t } = useLingui();
26
+
27
+ if (unusedCurrencies.length === 0) {
28
+ return null;
29
+ }
30
+
31
+ return (
32
+ <DropdownMenu>
33
+ <DropdownMenuTrigger asChild>
34
+ <Button variant="outline" className="gap-2">
35
+ <PlusIcon className="size-4" />
36
+ {placeholder || t`Add a price in another currency`}
37
+ </Button>
38
+ </DropdownMenuTrigger>
39
+ <DropdownMenuContent>
40
+ {unusedCurrencies.map(currencyCode => (
41
+ <DropdownMenuItem key={currencyCode} onSelect={() => onCurrencySelect(currencyCode)}>
42
+ <span className="uppercase text-muted-foreground">{currencyCode}</span>
43
+ {formatCurrencyName(currencyCode)}
44
+ </DropdownMenuItem>
45
+ ))}
46
+ </DropdownMenuContent>
47
+ </DropdownMenu>
48
+ );
49
+ }
@@ -0,0 +1,56 @@
1
+ import { useLingui } from '@lingui/react/macro';
2
+ import { PlusIcon } from 'lucide-react';
3
+
4
+ import { Button } from '@/vdb/components/ui/button';
5
+ import {
6
+ DropdownMenu,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ DropdownMenuTrigger,
10
+ } from '@/vdb/components/ui/dropdown-menu';
11
+ import { ResultOf } from 'gql.tada';
12
+
13
+ import { stockLocationsQueryDocument } from '../product-variants.graphql.js';
14
+
15
+ interface AddStockLocationDropdownProps {
16
+ availableStockLocations: ResultOf<typeof stockLocationsQueryDocument>['stockLocations']['items'];
17
+ usedStockLocationIds: string[];
18
+ onStockLocationSelect: (stockLocationId: string, stockLocationName: string) => void;
19
+ placeholder?: string;
20
+ }
21
+
22
+ export function AddStockLocationDropdown({
23
+ availableStockLocations,
24
+ usedStockLocationIds,
25
+ onStockLocationSelect,
26
+ placeholder,
27
+ }: AddStockLocationDropdownProps) {
28
+ const { t } = useLingui();
29
+
30
+ const unusedStockLocations = availableStockLocations.filter(sl => !usedStockLocationIds.includes(sl.id));
31
+
32
+ if (unusedStockLocations.length === 0) {
33
+ return null;
34
+ }
35
+
36
+ return (
37
+ <DropdownMenu>
38
+ <DropdownMenuTrigger asChild>
39
+ <Button variant="outline" className="gap-2">
40
+ <PlusIcon className="size-4" />
41
+ {placeholder || t`Add stock level for another location`}
42
+ </Button>
43
+ </DropdownMenuTrigger>
44
+ <DropdownMenuContent>
45
+ {unusedStockLocations.map(location => (
46
+ <DropdownMenuItem
47
+ key={location.id}
48
+ onSelect={() => onStockLocationSelect(location.id, location.name)}
49
+ >
50
+ {location.name}
51
+ </DropdownMenuItem>
52
+ ))}
53
+ </DropdownMenuContent>
54
+ </DropdownMenu>
55
+ );
56
+ }
@@ -193,3 +193,15 @@ export const updateProductVariantsDocument = graphql(`
193
193
  }
194
194
  }
195
195
  `);
196
+
197
+ export const stockLocationsQueryDocument = graphql(`
198
+ query StockLocations {
199
+ stockLocations(options: { take: 100 }) {
200
+ items {
201
+ id
202
+ name
203
+ description
204
+ }
205
+ }
206
+ }
207
+ `);