@vendure/dashboard 3.5.0-minor-202510071456 → 3.5.0-minor-202510201346
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugin/dashboard.plugin.js +1 -1
- package/dist/vite/utils/ast-utils.spec.js +3 -3
- package/dist/vite/vite-plugin-hmr.d.ts +8 -0
- package/dist/vite/vite-plugin-hmr.js +34 -0
- package/dist/vite/vite-plugin-theme.js +6 -6
- package/dist/vite/vite-plugin-transform-index.js +6 -1
- package/dist/vite/vite-plugin-vendure-dashboard.d.ts +31 -4
- package/dist/vite/vite-plugin-vendure-dashboard.js +89 -34
- package/package.json +17 -5
- package/src/app/app-providers.tsx +4 -1
- package/src/app/common/map-faceted-filter-fields.ts +21 -0
- package/src/app/main.tsx +3 -1
- package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_administrators/administrators.tsx +13 -3
- package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +6 -13
- package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +1 -1
- package/src/app/routes/_authenticated/_assets/assets.tsx +17 -1
- package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
- package/src/app/routes/_authenticated/_collections/collections.tsx +5 -0
- package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +0 -1
- package/src/app/routes/_authenticated/_customers/customers.tsx +9 -5
- package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +0 -6
- package/src/app/routes/_authenticated/_facets/components/facet-value-bulk-actions.tsx +16 -0
- package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +43 -12
- package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +14 -5
- package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +117 -92
- package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +1 -1
- package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +2 -1
- package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +26 -27
- package/src/app/routes/_authenticated/_orders/components/order-table.tsx +5 -3
- package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +6 -9
- package/src/app/routes/_authenticated/_orders/orders.graphql.ts +17 -1
- package/src/app/routes/_authenticated/_orders/orders.tsx +2 -0
- package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +48 -281
- package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +59 -40
- package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +73 -0
- package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +312 -0
- package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +4 -0
- package/src/app/routes/_authenticated/_product-variants/product-variants.tsx +2 -0
- package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +0 -6
- package/src/app/routes/_authenticated/_products/products.tsx +6 -2
- package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +4 -8
- package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +0 -10
- package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_promotions/promotions.tsx +12 -0
- package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +6 -2
- package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +4 -0
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +4 -10
- package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +9 -0
- package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +1 -0
- package/src/app/routes/_authenticated/_zones/zones.graphql.ts +2 -2
- package/src/app/routes/login.tsx +2 -2
- package/src/i18n/locales/ar.po +420 -289
- package/src/i18n/locales/cs.po +420 -289
- package/src/i18n/locales/de.po +420 -289
- package/src/i18n/locales/en.po +420 -289
- package/src/i18n/locales/es.po +420 -289
- package/src/i18n/locales/fa.po +420 -289
- package/src/i18n/locales/fr.po +468 -337
- package/src/i18n/locales/he.po +420 -289
- package/src/i18n/locales/hr.po +420 -289
- package/src/i18n/locales/it.po +420 -289
- package/src/i18n/locales/ja.po +420 -289
- package/src/i18n/locales/nb.po +420 -289
- package/src/i18n/locales/ne.po +420 -289
- package/src/i18n/locales/pl.po +420 -289
- package/src/i18n/locales/pt_BR.po +420 -289
- package/src/i18n/locales/pt_PT.po +420 -289
- package/src/i18n/locales/ru.po +420 -289
- package/src/i18n/locales/sv.po +420 -289
- package/src/i18n/locales/tr.po +420 -289
- package/src/i18n/locales/uk.po +420 -289
- package/src/i18n/locales/zh_Hans.po +420 -289
- package/src/i18n/locales/zh_Hant.po +420 -289
- package/src/lib/components/data-input/affixed-input.stories.tsx +93 -0
- package/src/lib/components/data-input/affixed-input.tsx +5 -2
- package/src/lib/components/data-input/boolean-input.stories.tsx +102 -0
- package/src/lib/components/data-input/checkbox-input.stories.tsx +61 -0
- package/src/lib/components/data-input/datetime-input.stories.tsx +62 -0
- package/src/lib/components/data-input/datetime-input.tsx +27 -13
- package/src/lib/components/data-input/default-relation-input.tsx +18 -12
- package/src/lib/components/data-input/money-input.stories.tsx +88 -0
- package/src/lib/components/data-input/number-input.stories.tsx +103 -0
- package/src/lib/components/data-input/number-input.tsx +10 -4
- package/src/lib/components/data-input/password-form-input.stories.tsx +65 -0
- package/src/lib/components/data-input/{password-input.tsx → password-form-input.tsx} +1 -1
- package/src/lib/components/data-input/rich-text-input.stories.tsx +92 -0
- package/src/lib/components/data-input/slug-input.stories.tsx +232 -0
- package/src/lib/components/data-input/slug-input.tsx +9 -10
- package/src/lib/components/data-input/text-input.stories.tsx +52 -0
- package/src/lib/components/data-input/textarea-input.stories.tsx +55 -0
- package/src/lib/components/data-table/add-filter-menu.tsx +6 -1
- package/src/lib/components/data-table/column-header-wrapper.tsx +106 -0
- package/src/lib/components/data-table/data-table-bulk-action-item.tsx +11 -9
- package/src/lib/components/data-table/data-table-bulk-actions.tsx +4 -4
- package/src/lib/components/data-table/data-table-column-header.tsx +17 -14
- package/src/lib/components/data-table/data-table-faceted-filter.tsx +33 -11
- package/src/lib/components/data-table/data-table-filter-badge-editable.tsx +35 -0
- package/src/lib/components/data-table/data-table-filter-badge.tsx +23 -16
- package/src/lib/components/data-table/data-table-filter-dialog.tsx +28 -8
- package/src/lib/components/data-table/data-table-pagination.tsx +23 -7
- package/src/lib/components/data-table/data-table.stories.tsx +249 -0
- package/src/lib/components/data-table/data-table.tsx +37 -9
- package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +79 -34
- package/src/lib/components/data-table/use-generated-columns.tsx +55 -27
- package/src/lib/components/layout/nav-user.tsx +19 -13
- package/src/lib/components/login/login-form.tsx +39 -123
- package/src/lib/components/shared/alerts.tsx +29 -17
- package/src/lib/components/shared/asset/asset-bulk-actions.tsx +3 -3
- package/src/lib/components/shared/asset/asset-gallery.stories.tsx +76 -0
- package/src/lib/components/shared/asset/asset-gallery.tsx +147 -113
- package/src/lib/components/shared/asset/asset-picker-dialog.stories.tsx +58 -0
- package/src/lib/components/shared/customer-group-selector.tsx +5 -2
- package/src/lib/components/shared/detail-page-button.stories.tsx +52 -0
- package/src/lib/components/shared/facet-value-selector.stories.tsx +48 -0
- package/src/lib/components/shared/facet-value-selector.tsx +130 -34
- package/src/lib/components/shared/paginated-list-data-table.stories.tsx +212 -0
- package/src/lib/components/shared/paginated-list-data-table.tsx +12 -12
- package/src/lib/components/shared/permission-guard.stories.tsx +46 -0
- package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +2 -0
- package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +8 -4
- package/src/lib/components/shared/rich-text-editor/rich-text-editor.tsx +1 -0
- package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +40 -0
- package/src/lib/components/shared/vendure-image.stories.tsx +167 -0
- package/src/lib/components/shared/vendure-image.tsx +6 -7
- package/src/lib/components/ui/accordion.stories.tsx +33 -0
- package/src/lib/components/ui/alert-dialog.stories.tsx +48 -0
- package/src/lib/components/ui/alert.stories.tsx +35 -0
- package/src/lib/components/ui/aspect-ratio.stories.tsx +28 -0
- package/src/lib/components/ui/badge.stories.tsx +28 -0
- package/src/lib/components/ui/breadcrumb.stories.tsx +41 -0
- package/src/lib/components/ui/button.stories.tsx +38 -0
- package/src/lib/components/ui/calendar.stories.tsx +22 -0
- package/src/lib/components/ui/card.stories.tsx +28 -0
- package/src/lib/components/ui/carousel.stories.tsx +34 -0
- package/src/lib/components/ui/checkbox.stories.tsx +31 -0
- package/src/lib/components/ui/collapsible.stories.tsx +39 -0
- package/src/lib/components/ui/command.stories.tsx +44 -0
- package/src/lib/components/ui/context-menu.stories.tsx +38 -0
- package/src/lib/components/ui/dialog.stories.tsx +52 -0
- package/src/lib/components/ui/drawer.stories.tsx +50 -0
- package/src/lib/components/ui/dropdown-menu.stories.tsx +41 -0
- package/src/lib/components/ui/hover-card.stories.tsx +38 -0
- package/src/lib/components/ui/input-group.tsx +148 -0
- package/src/lib/components/ui/input-otp.stories.tsx +30 -0
- package/src/lib/components/ui/input.stories.tsx +38 -0
- package/src/lib/components/ui/label.stories.tsx +24 -0
- package/src/lib/components/ui/menubar.stories.tsx +53 -0
- package/src/lib/components/ui/navigation-menu.stories.tsx +54 -0
- package/src/lib/components/ui/pagination.stories.tsx +51 -0
- package/src/lib/components/ui/password-input.stories.tsx +32 -0
- package/src/lib/components/ui/password-input.tsx +29 -0
- package/src/lib/components/ui/popover.stories.tsx +33 -0
- package/src/lib/components/ui/progress.stories.tsx +27 -0
- package/src/lib/components/ui/radio-group.stories.tsx +34 -0
- package/src/lib/components/ui/resizable.stories.tsx +32 -0
- package/src/lib/components/ui/scroll-area.stories.tsx +31 -0
- package/src/lib/components/ui/select.stories.tsx +36 -0
- package/src/lib/components/ui/separator.stories.tsx +35 -0
- package/src/lib/components/ui/sheet.stories.tsx +50 -0
- package/src/lib/components/ui/sidebar-context.ts +16 -0
- package/src/lib/components/ui/sidebar.tsx +2 -13
- package/src/lib/components/ui/skeleton.stories.tsx +26 -0
- package/src/lib/components/ui/slider.stories.tsx +37 -0
- package/src/lib/components/ui/switch.stories.tsx +31 -0
- package/src/lib/components/ui/table.stories.tsx +52 -0
- package/src/lib/components/ui/tabs.stories.tsx +29 -0
- package/src/lib/components/ui/textarea.stories.tsx +32 -0
- package/src/lib/components/ui/toggle-group.stories.tsx +31 -0
- package/src/lib/components/ui/toggle.stories.tsx +39 -0
- package/src/lib/components/ui/tooltip.stories.tsx +30 -0
- package/src/lib/components/ui/tooltip.tsx +2 -2
- package/src/lib/framework/alert/alert-extensions.tsx +0 -11
- package/src/lib/framework/alert/alert-item.tsx +14 -19
- package/src/lib/framework/alert/alerts-indicator.tsx +14 -15
- package/src/lib/framework/alert/search-index-buffer-alert/search-index-buffer-alert.ts +41 -0
- package/src/lib/framework/component-registry/component-registry.tsx +3 -14
- package/src/lib/framework/dashboard-widget/base-widget.tsx +18 -9
- package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +12 -11
- package/src/lib/framework/defaults.ts +9 -13
- package/src/lib/framework/extension-api/input-component-extensions.tsx +8 -3
- package/src/lib/framework/extension-api/logic/alerts.ts +3 -2
- package/src/lib/framework/extension-api/types/alerts.ts +12 -6
- package/src/lib/framework/extension-api/types/data-table.ts +5 -2
- package/src/lib/framework/extension-api/types/login.ts +0 -21
- package/src/lib/framework/layout-engine/custom-form-page.stories.tsx +344 -0
- package/src/lib/framework/layout-engine/page-layout.tsx +11 -9
- package/src/lib/framework/layout-engine/page.stories.tsx +275 -0
- package/src/lib/framework/nav-menu/nav-menu-extensions.ts +32 -19
- package/src/lib/framework/page/detail-page.stories.tsx +151 -0
- package/src/lib/framework/page/list-page.stories.tsx +217 -0
- package/src/lib/framework/page/list-page.tsx +8 -1
- package/src/lib/graphql/api.ts +18 -1
- package/src/lib/graphql/graphql-env.d.ts +1 -1
- package/src/lib/hooks/use-alerts.ts +84 -0
- package/src/lib/hooks/use-floating-bulk-actions.ts +2 -3
- package/src/lib/index.ts +14 -1
- package/src/lib/providers/alerts-provider.tsx +60 -0
- package/src/lib/providers/theme-provider.tsx +6 -3
|
@@ -13,9 +13,8 @@ import { api } from '@/vdb/graphql/api.js';
|
|
|
13
13
|
import { Trans, useLingui } from '@lingui/react/macro';
|
|
14
14
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
15
15
|
import { createFileRoute, Link, useNavigate } from '@tanstack/react-router';
|
|
16
|
-
import { VariablesOf } from 'gql.tada';
|
|
17
16
|
import { User } from 'lucide-react';
|
|
18
|
-
import {
|
|
17
|
+
import { useState } from 'react';
|
|
19
18
|
import { toast } from 'sonner';
|
|
20
19
|
import { CustomerAddressSelector } from './components/customer-address-selector.js';
|
|
21
20
|
import { EditOrderTable } from './components/edit-order-table.js';
|
|
@@ -23,16 +22,13 @@ import { OrderAddress } from './components/order-address.js';
|
|
|
23
22
|
import { OrderModificationPreviewDialog } from './components/order-modification-preview-dialog.js';
|
|
24
23
|
import { OrderModificationSummary } from './components/order-modification-summary.js';
|
|
25
24
|
import { useTransitionOrderToState } from './components/use-transition-order-to-state.js';
|
|
26
|
-
import {
|
|
27
|
-
draftOrderEligibleShippingMethodsDocument,
|
|
28
|
-
modifyOrderDocument,
|
|
29
|
-
orderDetailDocument,
|
|
30
|
-
} from './orders.graphql.js';
|
|
25
|
+
import { draftOrderEligibleShippingMethodsDocument, orderDetailDocument } from './orders.graphql.js';
|
|
31
26
|
import { loadModifyingOrder } from './utils/order-detail-loaders.js';
|
|
32
|
-
import { AddressFragment
|
|
27
|
+
import { AddressFragment } from './utils/order-types.js';
|
|
28
|
+
import { computePendingOrder } from './utils/order-utils.js';
|
|
29
|
+
import { useModifyOrder } from './utils/use-modify-order.js';
|
|
33
30
|
|
|
34
31
|
const pageId = 'order-modify';
|
|
35
|
-
type ModifyOrderInput = VariablesOf<typeof modifyOrderDocument>['input'];
|
|
36
32
|
|
|
37
33
|
export const Route = createFileRoute('/_authenticated/_orders/orders_/$id_/modify')({
|
|
38
34
|
component: ModifyOrderPage,
|
|
@@ -40,34 +36,6 @@ export const Route = createFileRoute('/_authenticated/_orders/orders_/$id_/modif
|
|
|
40
36
|
errorComponent: ({ error }) => <ErrorPage message={error.message} />,
|
|
41
37
|
});
|
|
42
38
|
|
|
43
|
-
// --- AddedLine type for added items ---
|
|
44
|
-
interface AddedLine {
|
|
45
|
-
id: string;
|
|
46
|
-
featuredAsset?: any;
|
|
47
|
-
productVariant: {
|
|
48
|
-
id: string;
|
|
49
|
-
name: string;
|
|
50
|
-
sku: string;
|
|
51
|
-
};
|
|
52
|
-
unitPrice: number;
|
|
53
|
-
unitPriceWithTax: number;
|
|
54
|
-
quantity: number;
|
|
55
|
-
linePrice: number;
|
|
56
|
-
linePriceWithTax: number;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// --- ProductVariantInfo type ---
|
|
60
|
-
type ProductVariantInfo = {
|
|
61
|
-
productVariantId: string;
|
|
62
|
-
productVariantName: string;
|
|
63
|
-
sku: string;
|
|
64
|
-
productAsset: {
|
|
65
|
-
preview: string;
|
|
66
|
-
};
|
|
67
|
-
price?: number;
|
|
68
|
-
priceWithTax?: number;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
39
|
function ModifyOrderPage() {
|
|
72
40
|
const params = Route.useParams();
|
|
73
41
|
const navigate = useNavigate({ from: '/orders/$id/modify' });
|
|
@@ -103,254 +71,48 @@ function ModifyOrderPage() {
|
|
|
103
71
|
const { transitionToPreModifyingState, ManuallySelectNextState, selectNextState, transitionToState } =
|
|
104
72
|
useTransitionOrderToState(entity?.id ?? '');
|
|
105
73
|
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
useEffect(() => {
|
|
122
|
-
setModifyOrderInput(prev => ({
|
|
123
|
-
...prev,
|
|
124
|
-
orderId: entity?.id ?? '',
|
|
125
|
-
couponCodes: entity?.couponCodes ?? [],
|
|
126
|
-
}));
|
|
127
|
-
}, [entity?.id]);
|
|
128
|
-
|
|
129
|
-
// --- Added variants info state ---
|
|
130
|
-
const [addedVariants, setAddedVariants] = useState<Map<string, ProductVariantInfo>>(new Map());
|
|
131
|
-
|
|
132
|
-
// --- Handlers update modifyOrderInput ---
|
|
133
|
-
function handleAddItem(variant: ProductVariantInfo) {
|
|
134
|
-
setModifyOrderInput(prev => ({
|
|
135
|
-
...prev,
|
|
136
|
-
addItems: [...(prev.addItems ?? []), { productVariantId: variant.productVariantId, quantity: 1 }],
|
|
137
|
-
}));
|
|
138
|
-
setAddedVariants(prev => {
|
|
139
|
-
const newMap = new Map(prev);
|
|
140
|
-
newMap.set(variant.productVariantId, variant);
|
|
141
|
-
return newMap;
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function handleAdjustLine({
|
|
146
|
-
lineId,
|
|
147
|
-
quantity,
|
|
148
|
-
customFields,
|
|
149
|
-
}: {
|
|
150
|
-
lineId: string;
|
|
151
|
-
quantity: number;
|
|
152
|
-
customFields: Record<string, any>;
|
|
153
|
-
}) {
|
|
154
|
-
// Check if this is an added line
|
|
155
|
-
if (lineId.startsWith('added-')) {
|
|
156
|
-
const productVariantId = lineId.replace('added-', '');
|
|
157
|
-
setModifyOrderInput(prev => ({
|
|
158
|
-
...prev,
|
|
159
|
-
addItems: (prev.addItems ?? []).map(item =>
|
|
160
|
-
item.productVariantId === productVariantId ? { ...item, quantity } : item,
|
|
161
|
-
),
|
|
162
|
-
}));
|
|
163
|
-
} else {
|
|
164
|
-
let normalizedCustomFields: any = customFields;
|
|
165
|
-
delete normalizedCustomFields.__entityId__;
|
|
166
|
-
if (Object.keys(normalizedCustomFields).length === 0) {
|
|
167
|
-
normalizedCustomFields = undefined;
|
|
168
|
-
}
|
|
169
|
-
setModifyOrderInput(prev => {
|
|
170
|
-
const existing = (prev.adjustOrderLines ?? []).find(l => l.orderLineId === lineId);
|
|
171
|
-
const adjustOrderLines = existing
|
|
172
|
-
? (prev.adjustOrderLines ?? []).map(l =>
|
|
173
|
-
l.orderLineId === lineId
|
|
174
|
-
? { ...l, quantity, customFields: normalizedCustomFields }
|
|
175
|
-
: l,
|
|
176
|
-
)
|
|
177
|
-
: [
|
|
178
|
-
...(prev.adjustOrderLines ?? []),
|
|
179
|
-
{ orderLineId: lineId, quantity, customFields: normalizedCustomFields },
|
|
180
|
-
];
|
|
181
|
-
return { ...prev, adjustOrderLines };
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function handleRemoveLine({ lineId }: { lineId: string }) {
|
|
187
|
-
if (lineId.startsWith('added-')) {
|
|
188
|
-
const productVariantId = lineId.replace('added-', '');
|
|
189
|
-
setModifyOrderInput(prev => ({
|
|
190
|
-
...prev,
|
|
191
|
-
addItems: (prev.addItems ?? []).filter(item => item.productVariantId !== productVariantId),
|
|
192
|
-
}));
|
|
193
|
-
setAddedVariants(prev => {
|
|
194
|
-
const newMap = new Map(prev);
|
|
195
|
-
newMap.delete(productVariantId);
|
|
196
|
-
return newMap;
|
|
197
|
-
});
|
|
198
|
-
} else {
|
|
199
|
-
setModifyOrderInput(prev => {
|
|
200
|
-
const existingAdjustment = (prev.adjustOrderLines ?? []).find(l => l.orderLineId === lineId);
|
|
201
|
-
const adjustOrderLines = existingAdjustment
|
|
202
|
-
? (prev.adjustOrderLines ?? []).map(l =>
|
|
203
|
-
l.orderLineId === lineId ? { ...l, quantity: 0 } : l,
|
|
204
|
-
)
|
|
205
|
-
: [...(prev.adjustOrderLines ?? []), { orderLineId: lineId, quantity: 0 }];
|
|
206
|
-
return {
|
|
207
|
-
...prev,
|
|
208
|
-
adjustOrderLines,
|
|
209
|
-
};
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function handleSetShippingMethod({ shippingMethodId }: { shippingMethodId: string }) {
|
|
215
|
-
setModifyOrderInput(prev => ({
|
|
216
|
-
...prev,
|
|
217
|
-
shippingMethodIds: [shippingMethodId],
|
|
218
|
-
}));
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function handleApplyCouponCode({ couponCode }: { couponCode: string }) {
|
|
222
|
-
setModifyOrderInput(prev => ({
|
|
223
|
-
...prev,
|
|
224
|
-
couponCodes: prev.couponCodes?.includes(couponCode)
|
|
225
|
-
? prev.couponCodes
|
|
226
|
-
: [...(prev.couponCodes ?? []), couponCode],
|
|
227
|
-
}));
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function handleRemoveCouponCode({ couponCode }: { couponCode: string }) {
|
|
231
|
-
setModifyOrderInput(prev => ({
|
|
232
|
-
...prev,
|
|
233
|
-
couponCodes: (prev.couponCodes ?? []).filter(code => code !== couponCode),
|
|
234
|
-
}));
|
|
235
|
-
}
|
|
74
|
+
// Use the custom hook for order modification logic
|
|
75
|
+
const {
|
|
76
|
+
modifyOrderInput,
|
|
77
|
+
addedVariants,
|
|
78
|
+
addItem,
|
|
79
|
+
adjustLine,
|
|
80
|
+
removeLine,
|
|
81
|
+
setShippingMethod,
|
|
82
|
+
applyCouponCode,
|
|
83
|
+
removeCouponCode,
|
|
84
|
+
updateShippingAddress: updateShippingAddressInInput,
|
|
85
|
+
updateBillingAddress: updateBillingAddressInInput,
|
|
86
|
+
hasModifications,
|
|
87
|
+
} = useModifyOrder(entity);
|
|
236
88
|
|
|
237
89
|
// --- Address editing state ---
|
|
238
90
|
const [editingShippingAddress, setEditingShippingAddress] = useState(false);
|
|
239
91
|
const [editingBillingAddress, setEditingBillingAddress] = useState(false);
|
|
240
92
|
|
|
241
|
-
function orderAddressToModifyOrderInput(
|
|
242
|
-
address: AddressFragment,
|
|
243
|
-
): ModifyOrderInput['updateShippingAddress'] {
|
|
244
|
-
return {
|
|
245
|
-
streetLine1: address.streetLine1,
|
|
246
|
-
streetLine2: address.streetLine2,
|
|
247
|
-
city: address.city,
|
|
248
|
-
countryCode: address.country.code,
|
|
249
|
-
fullName: address.fullName,
|
|
250
|
-
postalCode: address.postalCode,
|
|
251
|
-
province: address.province,
|
|
252
|
-
company: address.company,
|
|
253
|
-
phoneNumber: address.phoneNumber,
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
93
|
// --- Address selection handlers ---
|
|
258
94
|
function handleSelectShippingAddress(address: AddressFragment) {
|
|
259
|
-
|
|
260
|
-
...prev,
|
|
261
|
-
updateShippingAddress: orderAddressToModifyOrderInput(address),
|
|
262
|
-
}));
|
|
95
|
+
updateShippingAddressInInput(address);
|
|
263
96
|
setEditingShippingAddress(false);
|
|
264
97
|
}
|
|
265
98
|
|
|
266
99
|
function handleSelectBillingAddress(address: AddressFragment) {
|
|
267
|
-
|
|
268
|
-
...prev,
|
|
269
|
-
updateBillingAddress: orderAddressToModifyOrderInput(address),
|
|
270
|
-
}));
|
|
100
|
+
updateBillingAddressInInput(address);
|
|
271
101
|
setEditingBillingAddress(false);
|
|
272
102
|
}
|
|
273
103
|
|
|
274
|
-
// --- Utility: compute pending order for display ---
|
|
275
|
-
function computePendingOrder(input: ModifyOrderInput): Order | null {
|
|
276
|
-
if (!entity) {
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
// Adjust lines
|
|
280
|
-
const lines = entity.lines.map(line => {
|
|
281
|
-
const adjust = input.adjustOrderLines?.find(l => l.orderLineId === line.id);
|
|
282
|
-
return adjust
|
|
283
|
-
? { ...line, quantity: adjust.quantity, customFields: (adjust as any).customFields }
|
|
284
|
-
: line;
|
|
285
|
-
});
|
|
286
|
-
// Add new items (as AddedLine)
|
|
287
|
-
const addedLines = input.addItems
|
|
288
|
-
?.map(item => {
|
|
289
|
-
const variantInfo = addedVariants.get(item.productVariantId);
|
|
290
|
-
return variantInfo
|
|
291
|
-
? ({
|
|
292
|
-
id: `added-${item.productVariantId}`,
|
|
293
|
-
featuredAsset: variantInfo.productAsset ?? null,
|
|
294
|
-
productVariant: {
|
|
295
|
-
id: variantInfo.productVariantId,
|
|
296
|
-
name: variantInfo.productVariantName,
|
|
297
|
-
sku: variantInfo.sku,
|
|
298
|
-
},
|
|
299
|
-
unitPrice: variantInfo.price ?? 0,
|
|
300
|
-
unitPriceWithTax: variantInfo.priceWithTax ?? 0,
|
|
301
|
-
quantity: item.quantity,
|
|
302
|
-
linePrice: (variantInfo.price ?? 0) * item.quantity,
|
|
303
|
-
linePriceWithTax: (variantInfo.priceWithTax ?? 0) * item.quantity,
|
|
304
|
-
} as unknown as Order['lines'][number])
|
|
305
|
-
: null;
|
|
306
|
-
})
|
|
307
|
-
.filter(x => x != null);
|
|
308
|
-
return {
|
|
309
|
-
...entity,
|
|
310
|
-
lines: [...lines, ...(addedLines ?? [])],
|
|
311
|
-
couponCodes: input.couponCodes ?? [],
|
|
312
|
-
shippingLines: input.shippingMethodIds
|
|
313
|
-
? input.shippingMethodIds
|
|
314
|
-
.map(shippingMethodId => {
|
|
315
|
-
const shippingMethod =
|
|
316
|
-
eligibleShippingMethods?.eligibleShippingMethodsForDraftOrder.find(
|
|
317
|
-
method => method.id === shippingMethodId,
|
|
318
|
-
);
|
|
319
|
-
if (!shippingMethod) {
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
return {
|
|
323
|
-
shippingMethod: {
|
|
324
|
-
...shippingMethod,
|
|
325
|
-
fulfillmentHandlerCode: 'manual',
|
|
326
|
-
},
|
|
327
|
-
discountedPriceWithTax: shippingMethod?.priceWithTax ?? 0,
|
|
328
|
-
id: shippingMethodId,
|
|
329
|
-
};
|
|
330
|
-
})
|
|
331
|
-
.filter(x => x !== undefined)
|
|
332
|
-
: entity.shippingLines,
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
|
|
336
104
|
const [previewOpen, setPreviewOpen] = useState(false);
|
|
337
105
|
|
|
338
106
|
if (!entity) {
|
|
339
107
|
return null;
|
|
340
108
|
}
|
|
341
109
|
|
|
342
|
-
const pendingOrder = computePendingOrder(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
modifyOrderInput.updateShippingAddress ||
|
|
349
|
-
modifyOrderInput.updateBillingAddress;
|
|
350
|
-
|
|
351
|
-
if (!pendingOrder) {
|
|
352
|
-
return null;
|
|
353
|
-
}
|
|
110
|
+
const pendingOrder = computePendingOrder(
|
|
111
|
+
entity,
|
|
112
|
+
modifyOrderInput,
|
|
113
|
+
addedVariants,
|
|
114
|
+
eligibleShippingMethods?.eligibleShippingMethodsForDraftOrder,
|
|
115
|
+
);
|
|
354
116
|
|
|
355
117
|
// On successful state transition, invalidate the order detail query and navigate to the order detail page
|
|
356
118
|
const onSuccess = async () => {
|
|
@@ -369,17 +131,22 @@ function ModifyOrderPage() {
|
|
|
369
131
|
};
|
|
370
132
|
|
|
371
133
|
const handleModificationSubmit = async (priceDifference?: number) => {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
? await transitionToState('ArrangingAdditionalPayment')
|
|
375
|
-
: await transitionToPreModifyingState();
|
|
376
|
-
if (!transitionError) {
|
|
377
|
-
const queryKey = getDetailQueryOptions(orderDetailDocument, { id: entity.id }).queryKey;
|
|
378
|
-
await queryClient.invalidateQueries({ queryKey });
|
|
134
|
+
if (priceDifference === undefined) {
|
|
135
|
+
// the preview was cancelled
|
|
379
136
|
setPreviewOpen(false);
|
|
380
|
-
await navigate({ to: `/orders/$id`, params: { id: entity?.id } });
|
|
381
137
|
} else {
|
|
382
|
-
|
|
138
|
+
const transitionError =
|
|
139
|
+
priceDifference > 0
|
|
140
|
+
? await transitionToState('ArrangingAdditionalPayment')
|
|
141
|
+
: await transitionToPreModifyingState();
|
|
142
|
+
if (!transitionError) {
|
|
143
|
+
const queryKey = getDetailQueryOptions(orderDetailDocument, { id: entity.id }).queryKey;
|
|
144
|
+
await queryClient.invalidateQueries({ queryKey });
|
|
145
|
+
setPreviewOpen(false);
|
|
146
|
+
await navigate({ to: `/orders/$id`, params: { id: entity?.id } });
|
|
147
|
+
} else {
|
|
148
|
+
selectNextState({ onSuccess });
|
|
149
|
+
}
|
|
383
150
|
}
|
|
384
151
|
};
|
|
385
152
|
|
|
@@ -405,12 +172,12 @@ function ModifyOrderPage() {
|
|
|
405
172
|
eligibleShippingMethods={
|
|
406
173
|
eligibleShippingMethods?.eligibleShippingMethodsForDraftOrder ?? []
|
|
407
174
|
}
|
|
408
|
-
onAddItem={
|
|
409
|
-
onAdjustLine={
|
|
410
|
-
onRemoveLine={
|
|
411
|
-
onSetShippingMethod={
|
|
412
|
-
onApplyCouponCode={
|
|
413
|
-
onRemoveCouponCode={
|
|
175
|
+
onAddItem={addItem}
|
|
176
|
+
onAdjustLine={adjustLine}
|
|
177
|
+
onRemoveLine={removeLine}
|
|
178
|
+
onSetShippingMethod={setShippingMethod}
|
|
179
|
+
onApplyCouponCode={applyCouponCode}
|
|
180
|
+
onRemoveCouponCode={removeCouponCode}
|
|
414
181
|
displayTotals={false}
|
|
415
182
|
/>
|
|
416
183
|
</PageBlock>
|
|
@@ -449,7 +216,7 @@ function ModifyOrderPage() {
|
|
|
449
216
|
</PageBlock>
|
|
450
217
|
<PageBlock column="side" blockId="customer" title={<Trans>Customer</Trans>}>
|
|
451
218
|
{entity.customer ? (
|
|
452
|
-
<Button variant="
|
|
219
|
+
<Button variant="outline" asChild>
|
|
453
220
|
<Link to={`/customers/${entity?.customer?.id}`}>
|
|
454
221
|
<User className="w-4 h-4" />
|
|
455
222
|
{entity?.customer?.firstName} {entity?.customer?.lastName}
|
|
@@ -86,6 +86,7 @@ function DraftOrderPage() {
|
|
|
86
86
|
const { mutate: setDraftOrderCustomFields } = useMutation({
|
|
87
87
|
mutationFn: api.mutate(setDraftOrderCustomFieldsDocument),
|
|
88
88
|
onSuccess: (result: ResultOf<typeof setDraftOrderCustomFieldsDocument>) => {
|
|
89
|
+
toast.success(t`Order custom fields updated`);
|
|
89
90
|
refreshEntity();
|
|
90
91
|
},
|
|
91
92
|
});
|
|
@@ -110,6 +111,11 @@ function DraftOrderPage() {
|
|
|
110
111
|
break;
|
|
111
112
|
}
|
|
112
113
|
},
|
|
114
|
+
onError: error => {
|
|
115
|
+
if ((error as any).extensions?.code === 'ENTITY_NOT_FOUND') {
|
|
116
|
+
toast.error(t`The variant could not be added. Ensure the parent product is enabled.`);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
113
119
|
});
|
|
114
120
|
|
|
115
121
|
const { mutate: adjustDraftOrderLine } = useMutation({
|
|
@@ -151,6 +157,15 @@ function DraftOrderPage() {
|
|
|
151
157
|
switch (order.__typename) {
|
|
152
158
|
case 'Order':
|
|
153
159
|
toast.success(t`Customer set for order`);
|
|
160
|
+
|
|
161
|
+
// When we change the customer, we should clear
|
|
162
|
+
// any selected shipping/billing address
|
|
163
|
+
if (entity?.shippingAddress) {
|
|
164
|
+
unsetShippingAddressForDraftOrder({ orderId: entity.id });
|
|
165
|
+
}
|
|
166
|
+
if (entity?.billingAddress) {
|
|
167
|
+
unsetBillingAddressForDraftOrder({ orderId: entity.id });
|
|
168
|
+
}
|
|
154
169
|
refreshEntity();
|
|
155
170
|
break;
|
|
156
171
|
default:
|
|
@@ -377,7 +392,7 @@ function DraftOrderPage() {
|
|
|
377
392
|
onClick={e => {
|
|
378
393
|
e.preventDefault();
|
|
379
394
|
e.stopPropagation();
|
|
380
|
-
orderCustomFieldsForm.
|
|
395
|
+
onSaveCustomFields(orderCustomFieldsForm.getValues());
|
|
381
396
|
}}
|
|
382
397
|
>
|
|
383
398
|
<Trans>Set custom fields</Trans>
|
|
@@ -387,7 +402,7 @@ function DraftOrderPage() {
|
|
|
387
402
|
</PageBlock>
|
|
388
403
|
<PageBlock column="side" blockId="customer" title={<Trans>Customer</Trans>}>
|
|
389
404
|
{entity?.customer?.id ? (
|
|
390
|
-
<Button variant="
|
|
405
|
+
<Button variant="outline" asChild className="mb-4">
|
|
391
406
|
<Link to={`/customers/${entity?.customer?.id}`}>
|
|
392
407
|
<User className="w-4 h-4" />
|
|
393
408
|
{entity?.customer?.firstName} {entity?.customer?.lastName}
|
|
@@ -408,25 +423,27 @@ function DraftOrderPage() {
|
|
|
408
423
|
onClick={() => unsetShippingAddressForDraftOrder({ orderId: entity.id })}
|
|
409
424
|
/>
|
|
410
425
|
) : (
|
|
411
|
-
<
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
426
|
+
<div className="mt-4">
|
|
427
|
+
<CustomerAddressSelector
|
|
428
|
+
customerId={entity.customer?.id}
|
|
429
|
+
onSelect={address => {
|
|
430
|
+
setShippingAddressForDraftOrder({
|
|
431
|
+
orderId: entity.id,
|
|
432
|
+
input: {
|
|
433
|
+
fullName: address.fullName,
|
|
434
|
+
company: address.company,
|
|
435
|
+
streetLine1: address.streetLine1,
|
|
436
|
+
streetLine2: address.streetLine2,
|
|
437
|
+
city: address.city,
|
|
438
|
+
province: address.province,
|
|
439
|
+
postalCode: address.postalCode,
|
|
440
|
+
countryCode: address.country.code,
|
|
441
|
+
phoneNumber: address.phoneNumber,
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
}}
|
|
445
|
+
/>
|
|
446
|
+
</div>
|
|
430
447
|
)}
|
|
431
448
|
</div>
|
|
432
449
|
</PageBlock>
|
|
@@ -438,25 +455,27 @@ function DraftOrderPage() {
|
|
|
438
455
|
onClick={() => unsetBillingAddressForDraftOrder({ orderId: entity.id })}
|
|
439
456
|
/>
|
|
440
457
|
) : (
|
|
441
|
-
<
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
458
|
+
<div className="mt-4">
|
|
459
|
+
<CustomerAddressSelector
|
|
460
|
+
customerId={entity.customer?.id}
|
|
461
|
+
onSelect={address => {
|
|
462
|
+
setBillingAddressForDraftOrder({
|
|
463
|
+
orderId: entity.id,
|
|
464
|
+
input: {
|
|
465
|
+
fullName: address.fullName,
|
|
466
|
+
company: address.company,
|
|
467
|
+
streetLine1: address.streetLine1,
|
|
468
|
+
streetLine2: address.streetLine2,
|
|
469
|
+
city: address.city,
|
|
470
|
+
province: address.province,
|
|
471
|
+
postalCode: address.postalCode,
|
|
472
|
+
countryCode: address.country.code,
|
|
473
|
+
phoneNumber: address.phoneNumber,
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
}}
|
|
477
|
+
/>
|
|
478
|
+
</div>
|
|
460
479
|
)}
|
|
461
480
|
</div>
|
|
462
481
|
</PageBlock>
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { DEFAULT_CHANNEL_CODE } from '@/vdb/constants.js';
|
|
2
|
+
import { VariablesOf } from 'gql.tada';
|
|
3
|
+
|
|
4
|
+
import { modifyOrderDocument } from '../orders.graphql.js';
|
|
2
5
|
|
|
3
6
|
import { Fulfillment, Order, Payment } from './order-types.js';
|
|
7
|
+
import { ProductVariantInfo } from './use-modify-order.js';
|
|
8
|
+
|
|
9
|
+
type ModifyOrderInput = VariablesOf<typeof modifyOrderDocument>['input'];
|
|
4
10
|
|
|
5
11
|
/**
|
|
6
12
|
* Calculates the outstanding payment amount for an order
|
|
@@ -84,3 +90,70 @@ export function getSeller<T>(order: { channels: Array<{ code: string; seller: T
|
|
|
84
90
|
const sellerChannel = order.channels.find(channel => channel.code !== DEFAULT_CHANNEL_CODE);
|
|
85
91
|
return sellerChannel?.seller;
|
|
86
92
|
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Computes a pending order based on the current order and modification input
|
|
96
|
+
*/
|
|
97
|
+
export function computePendingOrder(
|
|
98
|
+
order: Order,
|
|
99
|
+
input: ModifyOrderInput,
|
|
100
|
+
addedVariants: Map<string, ProductVariantInfo>,
|
|
101
|
+
eligibleShippingMethods?: Array<{ id: string; name: string; priceWithTax: number }>,
|
|
102
|
+
): Order {
|
|
103
|
+
// Adjust lines
|
|
104
|
+
const lines = order.lines.map(line => {
|
|
105
|
+
const adjust = input.adjustOrderLines?.find(l => l.orderLineId === line.id);
|
|
106
|
+
return adjust
|
|
107
|
+
? { ...line, quantity: adjust.quantity, customFields: (adjust as any).customFields }
|
|
108
|
+
: line;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Add new items (as AddedLine)
|
|
112
|
+
const addedLines = input.addItems
|
|
113
|
+
?.map(item => {
|
|
114
|
+
const variantInfo = addedVariants.get(item.productVariantId);
|
|
115
|
+
return variantInfo
|
|
116
|
+
? ({
|
|
117
|
+
id: `added-${item.productVariantId}`,
|
|
118
|
+
featuredAsset: variantInfo.productAsset ?? null,
|
|
119
|
+
productVariant: {
|
|
120
|
+
id: variantInfo.productVariantId,
|
|
121
|
+
name: variantInfo.productVariantName,
|
|
122
|
+
sku: variantInfo.sku,
|
|
123
|
+
},
|
|
124
|
+
unitPrice: variantInfo.price ?? 0,
|
|
125
|
+
unitPriceWithTax: variantInfo.priceWithTax ?? 0,
|
|
126
|
+
quantity: item.quantity,
|
|
127
|
+
linePrice: (variantInfo.price ?? 0) * item.quantity,
|
|
128
|
+
linePriceWithTax: (variantInfo.priceWithTax ?? 0) * item.quantity,
|
|
129
|
+
} as unknown as Order['lines'][number])
|
|
130
|
+
: null;
|
|
131
|
+
})
|
|
132
|
+
.filter(x => x != null);
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
...order,
|
|
136
|
+
lines: [...lines, ...(addedLines ?? [])],
|
|
137
|
+
couponCodes: input.couponCodes ?? [],
|
|
138
|
+
shippingLines: input.shippingMethodIds
|
|
139
|
+
? input.shippingMethodIds
|
|
140
|
+
.map(shippingMethodId => {
|
|
141
|
+
const shippingMethod = eligibleShippingMethods?.find(
|
|
142
|
+
method => method.id === shippingMethodId,
|
|
143
|
+
);
|
|
144
|
+
if (!shippingMethod) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
shippingMethod: {
|
|
149
|
+
...shippingMethod,
|
|
150
|
+
fulfillmentHandlerCode: 'manual',
|
|
151
|
+
},
|
|
152
|
+
discountedPriceWithTax: shippingMethod?.priceWithTax ?? 0,
|
|
153
|
+
id: shippingMethodId,
|
|
154
|
+
} as any;
|
|
155
|
+
})
|
|
156
|
+
.filter(x => x !== undefined)
|
|
157
|
+
: order.shippingLines,
|
|
158
|
+
};
|
|
159
|
+
}
|