@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
@@ -1,14 +1,17 @@
1
1
  import { DetailPageButton } from '@/vdb/components/shared/detail-page-button.js';
2
2
  import { PaginatedListDataTable } from '@/vdb/components/shared/paginated-list-data-table.js';
3
+ import { Button } from '@/vdb/components/ui/button.js';
3
4
  import { addCustomFields } from '@/vdb/framework/document-introspection/add-custom-fields.js';
4
5
  import { graphql } from '@/vdb/graphql/graphql.js';
6
+ import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
5
7
  import { Trans } from '@lingui/react/macro';
6
8
  import { Link } from '@tanstack/react-router';
7
- import { Button } from '@/vdb/components/ui/button.js';
8
- import { ColumnFiltersState, SortingState } from '@tanstack/react-table';
9
+ import { SortingState } from '@tanstack/react-table';
9
10
  import { PlusIcon } from 'lucide-react';
10
11
  import { useRef, useState } from 'react';
11
- import { deleteFacetValuesDocument } from '../facets.graphql.js';
12
+ import { DeleteFacetValuesBulkAction } from './facet-value-bulk-actions.js';
13
+
14
+ const pageId = 'facet-values-table';
12
15
 
13
16
  export const facetValueListDocument = graphql(`
14
17
  query FacetValueList($options: FacetValueListOptions) {
@@ -35,27 +38,49 @@ export function FacetValuesTable({ facetId, registerRefresher }: Readonly<FacetV
35
38
  const [sorting, setSorting] = useState<SortingState>([]);
36
39
  const [page, setPage] = useState(1);
37
40
  const [pageSize, setPageSize] = useState(10);
38
- const [filters, setFilters] = useState<ColumnFiltersState>([]);
41
+ const { setTableSettings, settings } = useUserSettings();
39
42
  const refreshRef = useRef<() => void>(() => {});
40
43
 
44
+ const tableSettings = pageId ? settings.tableSettings?.[pageId] : undefined;
45
+ const defaultVisibility = {
46
+ name: true,
47
+ code: true,
48
+ };
49
+
50
+ const columnVisibility = pageId
51
+ ? (tableSettings?.columnVisibility ?? defaultVisibility)
52
+ : defaultVisibility;
53
+ const columnOrder = pageId ? (tableSettings?.columnOrder ?? []) : ['name', 'code'];
54
+ const columnFilters = pageId ? tableSettings?.columnFilters : [];
55
+
41
56
  return (
42
57
  <>
43
58
  <PaginatedListDataTable
44
59
  listQuery={addCustomFields(facetValueListDocument)}
45
- deleteMutation={deleteFacetValuesDocument}
46
60
  page={page}
47
61
  itemsPerPage={pageSize}
48
62
  sorting={sorting}
49
- columnFilters={filters}
50
- onPageChange={(_, page, perPage) => {
51
- setPage(page);
52
- setPageSize(perPage);
63
+ columnFilters={columnFilters}
64
+ defaultColumnOrder={columnOrder}
65
+ defaultVisibility={columnVisibility}
66
+ onPageChange={(table, page, perPage) => {
67
+ if (pageId) {
68
+ setPageSize(perPage);
69
+ setPage(page);
70
+ }
53
71
  }}
54
- onSortChange={(_, sorting) => {
72
+ onSortChange={(table, sorting) => {
55
73
  setSorting(sorting);
56
74
  }}
57
- onFilterChange={(_, filters) => {
58
- setFilters(filters);
75
+ onFilterChange={(table, filters) => {
76
+ if (pageId) {
77
+ setTableSettings(pageId, 'columnFilters', filters);
78
+ }
79
+ }}
80
+ onColumnVisibilityChange={(table, columnVisibility) => {
81
+ if (pageId) {
82
+ setTableSettings(pageId, 'columnVisibility', columnVisibility);
83
+ }
59
84
  }}
60
85
  registerRefresher={refresher => {
61
86
  refreshRef.current = refresher;
@@ -94,6 +119,12 @@ export function FacetValuesTable({ facetId, registerRefresher }: Readonly<FacetV
94
119
  ),
95
120
  },
96
121
  }}
122
+ bulkActions={[
123
+ {
124
+ order: 400,
125
+ component: DeleteFacetValuesBulkAction,
126
+ },
127
+ ]}
97
128
  />
98
129
  <div className="mt-4">
99
130
  <Button asChild variant="outline">
@@ -1,3 +1,4 @@
1
+ import { SlugInput } from '@/vdb/components/data-input/index.js';
1
2
  import { PageBreadcrumb } from '@/vdb/components/layout/generated-breadcrumbs.js';
2
3
  import { ErrorPage } from '@/vdb/components/shared/error-page.js';
3
4
  import { FormFieldWrapper } from '@/vdb/components/shared/form-field-wrapper.js';
@@ -115,8 +116,8 @@ function FacetValueDetailPage() {
115
116
  </PageActionBarRight>
116
117
  </PageActionBar>
117
118
  <PageLayout>
118
- <PageBlock column="side" blockId="facet-info">
119
- {entity?.facet && (
119
+ {entity?.facet && (
120
+ <PageBlock column="side" blockId="facet-info">
120
121
  <div className="space-y-2">
121
122
  <div className="text-sm font-medium">
122
123
  <Trans>Facet</Trans>
@@ -124,8 +125,8 @@ function FacetValueDetailPage() {
124
125
  <div className="text-sm text-muted-foreground">{entity?.facet.name}</div>
125
126
  <div className="text-xs text-muted-foreground">{entity?.facet.code}</div>
126
127
  </div>
127
- )}
128
- </PageBlock>
128
+ </PageBlock>
129
+ )}
129
130
  <PageBlock column="main" blockId="main-form">
130
131
  <DetailFormGrid>
131
132
  <TranslatableFormFieldWrapper
@@ -138,7 +139,15 @@ function FacetValueDetailPage() {
138
139
  control={form.control}
139
140
  name="code"
140
141
  label={<Trans>Code</Trans>}
141
- render={({ field }) => <Input {...field} />}
142
+ render={({ field }) => (
143
+ <SlugInput
144
+ fieldName="code"
145
+ watchFieldName="name"
146
+ entityName="FacetValue"
147
+ entityId={entity?.id}
148
+ {...field}
149
+ />
150
+ )}
142
151
  />
143
152
  </DetailFormGrid>
144
153
  </PageBlock>
@@ -5,7 +5,7 @@ import { Button } from '@/vdb/components/ui/button.js';
5
5
  import { Input } from '@/vdb/components/ui/input.js';
6
6
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/vdb/components/ui/table.js';
7
7
  import { ResultOf } from '@/vdb/graphql/graphql.js';
8
- import { Trans } from '@lingui/react/macro';
8
+ import { Trans, useLingui } from '@lingui/react/macro';
9
9
  import {
10
10
  ColumnDef,
11
11
  flexRender,
@@ -14,7 +14,7 @@ import {
14
14
  VisibilityState,
15
15
  } from '@tanstack/react-table';
16
16
  import { Trash2 } from 'lucide-react';
17
- import { useState } from 'react';
17
+ import { useMemo, useState } from 'react';
18
18
  import {
19
19
  couponCodeSelectorPromotionListDocument,
20
20
  draftOrderEligibleShippingMethodsDocument,
@@ -44,7 +44,11 @@ export interface OrderTableProps {
44
44
  price?: any;
45
45
  priceWithTax?: any;
46
46
  }) => void;
47
- onAdjustLine: (event: { lineId: string; quantity: number; customFields: Record<string, any> }) => void;
47
+ onAdjustLine: (event: {
48
+ lineId: string;
49
+ quantity: number;
50
+ customFields: Record<string, any> | undefined;
51
+ }) => void;
48
52
  onRemoveLine: (event: { lineId: string }) => void;
49
53
  onSetShippingMethod: (event: { shippingMethodId: string }) => void;
50
54
  onApplyCouponCode: (event: { couponCode: string }) => void;
@@ -53,95 +57,116 @@ export interface OrderTableProps {
53
57
  }
54
58
 
55
59
  export function EditOrderTable({
56
- order,
57
- eligibleShippingMethods,
58
- onAddItem,
59
- onAdjustLine,
60
- onRemoveLine,
61
- onSetShippingMethod,
62
- onApplyCouponCode,
63
- onRemoveCouponCode,
64
- displayTotals = true,
65
- }: Readonly<OrderTableProps>) {
60
+ order,
61
+ eligibleShippingMethods,
62
+ onAddItem,
63
+ onAdjustLine,
64
+ onRemoveLine,
65
+ onSetShippingMethod,
66
+ onApplyCouponCode,
67
+ onRemoveCouponCode,
68
+ displayTotals = true,
69
+ }: Readonly<OrderTableProps>) {
66
70
  const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
71
+ const { t } = useLingui();
67
72
  const currencyCode = order.currencyCode;
68
- const columns: ColumnDef<OrderLineFragment & { customFields?: Record<string, any> }>[] = [
69
- {
70
- header: 'Image',
71
- accessorKey: 'featuredAsset',
72
- cell: ({ row }) => {
73
- const asset = row.original.featuredAsset;
74
- return <VendureImage asset={asset} preset="tiny" />;
73
+ const columns: ColumnDef<OrderLineFragment & { customFields?: Record<string, any> }>[] = useMemo(
74
+ () => [
75
+ {
76
+ header: '',
77
+ accessorKey: 'featuredAsset',
78
+ cell: ({ row }) => {
79
+ const asset = row.original.featuredAsset;
80
+ const removing = row.original.quantity === 0;
81
+ return (
82
+ <VendureImage className={removing ? 'opacity-50' : ''} asset={asset} preset="tiny" />
83
+ );
84
+ },
75
85
  },
76
- },
77
- {
78
- header: 'Product',
79
- accessorKey: 'productVariant.name',
80
- },
81
- {
82
- header: 'SKU',
83
- accessorKey: 'productVariant.sku',
84
- },
85
- {
86
- header: 'Unit price',
87
- accessorKey: 'unitPriceWithTax',
88
- cell: ({ row }) => {
89
- const value = row.original.unitPriceWithTax;
90
- const netValue = row.original.unitPrice;
91
- return <MoneyGrossNet priceWithTax={value} price={netValue} currencyCode={currencyCode} />;
86
+ {
87
+ header: t`Product`,
88
+ accessorKey: 'productVariant.name',
89
+ cell: ({ row }) => {
90
+ const value = row.original.productVariant.name;
91
+ const removing = row.original.quantity === 0;
92
+ return <div className={removing ? 'text-muted-foreground' : ''}>{value}</div>;
93
+ },
92
94
  },
93
- },
94
- {
95
- header: 'Quantity',
96
- accessorKey: 'quantity',
97
- cell: ({ row }) => {
98
- return (
99
- <div className="flex gap-2">
100
- <Input
101
- type="number"
102
- value={row.original.quantity}
103
- onChange={e =>
104
- onAdjustLine({
105
- lineId: row.original.id,
106
- quantity: e.target.valueAsNumber,
107
- customFields: row.original.customFields ?? {},
108
- })
109
- }
110
- />
111
- <Button
112
- variant="outline"
113
- type="button"
114
- size="icon"
115
- onClick={() => onRemoveLine({ lineId: row.original.id })}
116
- >
117
- <Trash2 />
118
- </Button>
119
- {row.original.customFields && (
120
- <OrderLineCustomFieldsForm
121
- onUpdate={customFields => {
95
+ {
96
+ header: t`SKU`,
97
+ accessorKey: 'productVariant.sku',
98
+ },
99
+ {
100
+ header: t`Unit price`,
101
+ accessorKey: 'unitPriceWithTax',
102
+ cell: ({ row }) => {
103
+ const value = row.original.unitPriceWithTax;
104
+ const netValue = row.original.unitPrice;
105
+ return (
106
+ <MoneyGrossNet priceWithTax={value} price={netValue} currencyCode={currencyCode} />
107
+ );
108
+ },
109
+ },
110
+ {
111
+ header: t`Quantity`,
112
+ accessorKey: 'quantity',
113
+ cell: ({ row }) => {
114
+ return (
115
+ <div className="flex gap-2">
116
+ <Input
117
+ type="number"
118
+ value={row.original.quantity}
119
+ min={0}
120
+ onChange={e => {
121
+ const value = Number.isNaN(e.target.valueAsNumber)
122
+ ? 0
123
+ : e.target.valueAsNumber;
122
124
  onAdjustLine({
123
125
  lineId: row.original.id,
124
- quantity: row.original.quantity,
125
- customFields: customFields,
126
+ quantity: value,
127
+ customFields: row.original.customFields,
126
128
  });
127
129
  }}
128
- value={row.original.customFields}
129
130
  />
130
- )}
131
- </div>
132
- );
131
+ <Button
132
+ variant="outline"
133
+ type="button"
134
+ size="icon"
135
+ disabled={row.original.quantity === 0}
136
+ onClick={() => onRemoveLine({ lineId: row.original.id })}
137
+ >
138
+ <Trash2 />
139
+ </Button>
140
+ {row.original.customFields && (
141
+ <OrderLineCustomFieldsForm
142
+ onUpdate={customFields => {
143
+ onAdjustLine({
144
+ lineId: row.original.id,
145
+ quantity: row.original.quantity,
146
+ customFields: customFields,
147
+ });
148
+ }}
149
+ value={row.original.customFields}
150
+ />
151
+ )}
152
+ </div>
153
+ );
154
+ },
133
155
  },
134
- },
135
- {
136
- header: 'Total',
137
- accessorKey: 'linePriceWithTax',
138
- cell: ({ row }) => {
139
- const value = row.original.linePriceWithTax;
140
- const netValue = row.original.linePrice;
141
- return <MoneyGrossNet priceWithTax={value} price={netValue} currencyCode={currencyCode} />;
156
+ {
157
+ header: t`Total`,
158
+ accessorKey: 'linePriceWithTax',
159
+ cell: ({ row }) => {
160
+ const value = row.original.linePriceWithTax;
161
+ const netValue = row.original.linePrice;
162
+ return (
163
+ <MoneyGrossNet priceWithTax={value} price={netValue} currencyCode={currencyCode} />
164
+ );
165
+ },
142
166
  },
143
- },
144
- ];
167
+ ],
168
+ [],
169
+ );
145
170
 
146
171
  const data = order.lines;
147
172
 
@@ -169,9 +194,9 @@ export function EditOrderTable({
169
194
  {header.isPlaceholder
170
195
  ? null
171
196
  : flexRender(
172
- header.column.columnDef.header,
173
- header.getContext(),
174
- )}
197
+ header.column.columnDef.header,
198
+ header.getContext(),
199
+ )}
175
200
  </TableHead>
176
201
  );
177
202
  })}
@@ -181,14 +206,14 @@ export function EditOrderTable({
181
206
  <TableBody>
182
207
  {table.getRowModel().rows?.length
183
208
  ? table.getRowModel().rows.map(row => (
184
- <TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
185
- {row.getVisibleCells().map(cell => (
186
- <TableCell key={cell.id}>
187
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
188
- </TableCell>
189
- ))}
190
- </TableRow>
191
- ))
209
+ <TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
210
+ {row.getVisibleCells().map(cell => (
211
+ <TableCell key={cell.id}>
212
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
213
+ </TableCell>
214
+ ))}
215
+ </TableRow>
216
+ ))
192
217
  : null}
193
218
  <TableRow>
194
219
  <TableCell colSpan={columns.length} className="h-12">
@@ -234,7 +234,7 @@ export function OrderDetailShared({
234
234
  {/* Side Column Blocks */}
235
235
  <PageBlock column="side" blockId="state">
236
236
  <StateTransitionControl
237
- currentState={getTranslatedOrderState(entity?.state)}
237
+ currentState={entity?.state}
238
238
  actions={stateTransitionActions}
239
239
  isLoading={transitionOrderToStateMutation.isPending}
240
240
  />
@@ -42,7 +42,8 @@ export function OrderModificationSummary({
42
42
  if (
43
43
  orig &&
44
44
  (adj.quantity !== orig.quantity ||
45
- JSON.stringify(adj.customFields) !== JSON.stringify((orig as any).customFields))
45
+ JSON.stringify(adj.customFields) !== JSON.stringify((orig as any).customFields)) &&
46
+ adj.quantity > 0
46
47
  ) {
47
48
  return {
48
49
  orderLineId: adj.orderLineId,
@@ -10,40 +10,39 @@ export interface OrderTableTotalsProps {
10
10
 
11
11
  export function OrderTableTotals({ order, columnCount }: Readonly<OrderTableTotalsProps>) {
12
12
  const currencyCode = order.currencyCode;
13
-
14
13
  return (
15
14
  <>
16
15
  {order.surcharges?.length > 0
17
16
  ? order.surcharges.map((surcharge, index) => (
18
- <TableRow key={`${surcharge.description}-${index}`}>
19
- <TableCell colSpan={columnCount - 1} className="h-12">
20
- <Trans>Surcharge</Trans>: {surcharge.description}
21
- </TableCell>
22
- <TableCell colSpan={1} className="h-12">
23
- <MoneyGrossNet
24
- priceWithTax={surcharge.priceWithTax}
25
- price={surcharge.price}
26
- currencyCode={currencyCode}
27
- />
28
- </TableCell>
29
- </TableRow>
30
- ))
17
+ <TableRow key={`${surcharge.description}-${index}`}>
18
+ <TableCell colSpan={columnCount - 1} className="h-12">
19
+ <Trans>Surcharge</Trans>: {surcharge.description}
20
+ </TableCell>
21
+ <TableCell colSpan={1} className="h-12">
22
+ <MoneyGrossNet
23
+ priceWithTax={surcharge.priceWithTax}
24
+ price={surcharge.price}
25
+ currencyCode={currencyCode}
26
+ />
27
+ </TableCell>
28
+ </TableRow>
29
+ ))
31
30
  : null}
32
31
  {order.discounts?.length > 0
33
32
  ? order.discounts.map((discount, index) => (
34
- <TableRow key={`${discount.description}-${index}`}>
35
- <TableCell colSpan={columnCount - 1} className="h-12">
36
- <Trans>Discount</Trans>: {discount.description}
37
- </TableCell>
38
- <TableCell colSpan={1} className="h-12">
39
- <MoneyGrossNet
40
- priceWithTax={discount.amountWithTax}
41
- price={discount.amount}
42
- currencyCode={currencyCode}
43
- />
44
- </TableCell>
45
- </TableRow>
46
- ))
33
+ <TableRow key={`${discount.description}-${index}`}>
34
+ <TableCell colSpan={columnCount - 1} className="h-12">
35
+ <Trans>Discount</Trans>: {discount.description}
36
+ </TableCell>
37
+ <TableCell colSpan={1} className="h-12">
38
+ <MoneyGrossNet
39
+ priceWithTax={discount.amountWithTax}
40
+ price={discount.amount}
41
+ currencyCode={currencyCode}
42
+ />
43
+ </TableCell>
44
+ </TableRow>
45
+ ))
47
46
  : null}
48
47
  <TableRow>
49
48
  <TableCell colSpan={columnCount - 1} className="h-12">
@@ -34,7 +34,10 @@ function createCustomizeColumns(currencyCode: string) {
34
34
  accessorKey: 'featuredAsset',
35
35
  cell: ({ row }: { row: any }) => {
36
36
  const asset = row.original.featuredAsset;
37
- return <VendureImage asset={asset} preset="tiny" />;
37
+ const fallbackAsset =
38
+ row.original.productVariant?.featuredAsset ??
39
+ row.original.productVariant?.product?.featuredAsset;
40
+ return <VendureImage asset={asset ?? fallbackAsset} preset="tiny" />;
38
41
  },
39
42
  },
40
43
  productVariant: {
@@ -154,14 +157,13 @@ export function OrderTable({ order, pageId }: Readonly<OrderTableProps>) {
154
157
  customizeColumns: customizeColumns as any,
155
158
  deleteMutation: undefined,
156
159
  defaultColumnOrder: columnOrder,
157
- additionalColumns: {},
158
160
  includeSelectionColumn: false,
159
161
  includeActionsColumn: false,
160
162
  enableSorting: false,
161
163
  });
162
164
 
163
165
  const columnVisibility = getColumnVisibility(columns, defaultColumnVisibility, customFieldColumnNames);
164
- const visibleColumnCount = Object.values(columnVisibility).filter(Boolean).length;
166
+ const visibleColumnCount = Object.values(columnVisibility).filter(Boolean).length - 2; // -2 for "actions" and "selection" cols
165
167
  const data = order.lines;
166
168
 
167
169
  return (
@@ -5,9 +5,10 @@ import {
5
5
  DropdownMenuItem,
6
6
  DropdownMenuTrigger,
7
7
  } from '@/vdb/components/ui/dropdown-menu.js';
8
- import { Trans } from '@lingui/react/macro';
8
+ import { useDynamicTranslations } from '@/vdb/hooks/use-dynamic-translations.js';
9
9
  import { cn } from '@/vdb/lib/utils.js';
10
- import { EllipsisVertical, CircleDashed, CircleCheck, CircleX } from 'lucide-react';
10
+ import { Trans } from '@lingui/react/macro';
11
+ import { CircleCheck, CircleDashed, CircleX, EllipsisVertical } from 'lucide-react';
11
12
 
12
13
  export type StateType = 'default' | 'destructive' | 'success';
13
14
 
@@ -44,6 +45,7 @@ export function StateTransitionControl({
44
45
  actions,
45
46
  isLoading,
46
47
  }: Readonly<StateTransitionControlProps>) {
48
+ const { getTranslatedOrderState } = useDynamicTranslations();
47
49
  const currentStateType = getTypeForState(currentState);
48
50
  const iconForType = {
49
51
  destructive: <CircleX className="h-4 w-4 text-destructive" />,
@@ -61,9 +63,7 @@ export function StateTransitionControl({
61
63
  title={currentState}
62
64
  >
63
65
  <div className="flex-shrink-0">{iconForType[currentStateType]}</div>
64
- <span className="truncate">
65
- {currentState}
66
- </span>
66
+ <span className="truncate">{getTranslatedOrderState(currentState)}</span>
67
67
  </div>
68
68
  {actions.length > 0 && (
69
69
  <DropdownMenu>
@@ -72,10 +72,7 @@ export function StateTransitionControl({
72
72
  variant="outline"
73
73
  size="sm"
74
74
  disabled={isLoading}
75
- className={cn(
76
- 'rounded-l-none border-l-0 shadow-none',
77
- 'bg-background',
78
- )}
75
+ className={cn('rounded-l-none border-l-0 shadow-none', 'bg-background')}
79
76
  >
80
77
  <EllipsisVertical className="h-4 w-4" />
81
78
  </Button>
@@ -638,8 +638,24 @@ export const modifyOrderDocument = graphql(
638
638
  mutation ModifyOrder($input: ModifyOrderInput!) {
639
639
  modifyOrder(input: $input) {
640
640
  __typename
641
- ...OrderDetail
642
641
  ...ErrorResult
642
+ ... on Order {
643
+ lines {
644
+ productVariant {
645
+ featuredAsset {
646
+ id
647
+ preview
648
+ }
649
+ product {
650
+ featuredAsset {
651
+ id
652
+ preview
653
+ }
654
+ }
655
+ }
656
+ }
657
+ }
658
+ ...OrderDetail
643
659
  }
644
660
  }
645
661
  `,