@vendure/dashboard 3.2.3 → 3.3.0
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/utils/ast-utils.d.ts +10 -0
- package/dist/plugin/utils/ast-utils.js +96 -0
- package/dist/plugin/utils/ast-utils.spec.d.ts +1 -0
- package/dist/plugin/utils/ast-utils.spec.js +120 -0
- package/dist/plugin/{config-loader.d.ts → utils/config-loader.d.ts} +22 -8
- package/dist/plugin/utils/config-loader.js +325 -0
- package/dist/plugin/{schema-generator.d.ts → utils/schema-generator.d.ts} +5 -0
- package/dist/plugin/{schema-generator.js → utils/schema-generator.js} +7 -1
- package/dist/plugin/{ui-config.js → utils/ui-config.js} +2 -3
- package/dist/plugin/vite-plugin-admin-api-schema.js +2 -2
- package/dist/plugin/vite-plugin-config-loader.d.ts +2 -3
- package/dist/plugin/vite-plugin-config-loader.js +18 -9
- package/dist/plugin/vite-plugin-config.js +4 -6
- package/dist/plugin/vite-plugin-dashboard-metadata.js +12 -14
- package/dist/plugin/vite-plugin-gql-tada.js +2 -2
- package/dist/plugin/vite-plugin-ui-config.js +3 -2
- package/package.json +16 -11
- package/src/app/app-providers.tsx +9 -9
- package/src/app/main.tsx +1 -1
- package/src/app/routes/_authenticated/_assets/assets.graphql.ts +26 -0
- package/src/app/routes/_authenticated/_assets/assets.tsx +2 -2
- package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +156 -0
- package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +104 -0
- package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +228 -0
- package/src/app/routes/_authenticated/_orders/components/money-gross-net.tsx +18 -0
- package/src/app/routes/_authenticated/_orders/components/order-address.tsx +2 -1
- package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +38 -0
- package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +53 -0
- package/src/app/routes/_authenticated/_orders/components/order-table.tsx +8 -49
- package/src/app/routes/_authenticated/_orders/components/shipping-method-selector.tsx +65 -0
- package/src/app/routes/_authenticated/_orders/orders.graphql.ts +187 -2
- package/src/app/routes/_authenticated/_orders/orders.tsx +39 -18
- package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +31 -9
- package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +418 -0
- package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +8 -2
- package/src/app/routes/_authenticated/_products/products.tsx +1 -1
- package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +6 -0
- package/src/app/routes/_authenticated/_system/job-queue.tsx +7 -8
- package/src/app/routes/_authenticated/_system/scheduled-tasks.tsx +241 -0
- package/src/app/routes/_authenticated.tsx +12 -1
- package/src/app/styles.css +15 -0
- package/src/lib/components/data-table/add-filter-menu.tsx +61 -0
- package/src/lib/components/data-table/data-table-column-header.tsx +0 -13
- package/src/lib/components/data-table/data-table-filter-badge.tsx +75 -0
- package/src/lib/components/data-table/data-table-filter-dialog.tsx +27 -28
- package/src/lib/components/data-table/data-table-types.ts +1 -0
- package/src/lib/components/data-table/data-table-view-options.tsx +73 -24
- package/src/lib/components/data-table/data-table.tsx +49 -44
- package/src/lib/components/data-table/filters/data-table-boolean-filter.tsx +57 -0
- package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +93 -0
- package/src/lib/components/data-table/filters/data-table-id-filter.tsx +58 -0
- package/src/lib/components/data-table/filters/data-table-number-filter.tsx +119 -0
- package/src/lib/components/data-table/filters/data-table-string-filter.tsx +62 -0
- package/src/lib/components/data-table/human-readable-operator.tsx +65 -0
- package/src/lib/components/data-table/refresh-button.tsx +25 -0
- package/src/lib/components/layout/nav-user.tsx +20 -15
- package/src/lib/components/layout/prerelease-popup.tsx +1 -5
- package/src/lib/components/shared/alerts.tsx +19 -1
- package/src/lib/components/shared/asset/asset-focal-point-editor.tsx +93 -0
- package/src/lib/components/shared/{asset-gallery.tsx → asset/asset-gallery.tsx} +51 -20
- package/src/lib/components/shared/{asset-picker-dialog.tsx → asset/asset-picker-dialog.tsx} +1 -1
- package/src/lib/components/shared/{asset-preview-dialog.tsx → asset/asset-preview-dialog.tsx} +1 -7
- package/src/lib/components/shared/asset/asset-preview-selector.tsx +34 -0
- package/src/lib/components/shared/asset/asset-preview.tsx +128 -0
- package/src/lib/components/shared/asset/asset-properties.tsx +46 -0
- package/src/lib/components/shared/{focal-point-control.tsx → asset/focal-point-control.tsx} +1 -1
- package/src/lib/components/shared/custom-fields-form.tsx +4 -3
- package/src/lib/components/shared/customer-selector.tsx +13 -14
- package/src/lib/components/shared/detail-page-button.tsx +2 -2
- package/src/lib/components/shared/entity-assets.tsx +3 -3
- package/src/lib/components/shared/error-page.tsx +2 -2
- package/src/lib/components/shared/navigation-confirmation.tsx +49 -0
- package/src/lib/components/shared/paginated-list-data-table.tsx +10 -1
- package/src/lib/components/shared/product-variant-selector.tsx +111 -0
- package/src/lib/components/shared/vendure-image.tsx +1 -1
- package/src/lib/components/ui/calendar.tsx +508 -63
- package/src/lib/framework/alert/alert-extensions.tsx +31 -0
- package/src/lib/framework/alert/alert-item.tsx +47 -0
- package/src/lib/framework/alert/alerts-indicator.tsx +23 -0
- package/src/lib/framework/alert/types.ts +13 -0
- package/src/lib/framework/dashboard-widget/base-widget.tsx +1 -0
- package/src/lib/framework/defaults.ts +34 -0
- package/src/lib/framework/document-introspection/get-document-structure.spec.ts +113 -3
- package/src/lib/framework/document-introspection/get-document-structure.ts +71 -13
- package/src/lib/framework/extension-api/define-dashboard-extension.ts +15 -5
- package/src/lib/framework/extension-api/extension-api-types.ts +81 -12
- package/src/lib/framework/form-engine/use-generated-form.tsx +8 -7
- package/src/lib/framework/layout-engine/layout-extensions.ts +3 -3
- package/src/lib/framework/layout-engine/page-layout.tsx +196 -35
- package/src/lib/framework/layout-engine/page-provider.tsx +10 -0
- package/src/lib/framework/page/detail-page.tsx +62 -9
- package/src/lib/framework/page/list-page.tsx +42 -4
- package/src/lib/framework/page/page-api.ts +1 -1
- package/src/lib/framework/page/use-detail-page.ts +82 -0
- package/src/lib/framework/registry/registry-types.ts +6 -2
- package/src/lib/graphql/fragments.tsx +8 -0
- package/src/lib/graphql/graphql-env.d.ts +25 -9
- package/src/lib/hooks/use-auth.tsx +13 -1
- package/src/lib/hooks/use-channel.ts +13 -0
- package/src/lib/hooks/use-local-format.ts +28 -1
- package/src/lib/hooks/use-page.tsx +2 -3
- package/src/lib/hooks/use-permissions.ts +13 -0
- package/src/lib/index.ts +7 -8
- package/src/lib/providers/auth.tsx +22 -9
- package/src/lib/providers/channel-provider.tsx +9 -1
- package/src/lib/providers/server-config.tsx +7 -1
- package/src/lib/providers/user-settings.tsx +24 -0
- package/vite/utils/ast-utils.spec.ts +128 -0
- package/vite/utils/ast-utils.ts +119 -0
- package/vite/utils/config-loader.ts +410 -0
- package/vite/{schema-generator.ts → utils/schema-generator.ts} +11 -6
- package/vite/{ui-config.ts → utils/ui-config.ts} +7 -3
- package/vite/vite-plugin-admin-api-schema.ts +2 -12
- package/vite/vite-plugin-config-loader.ts +25 -13
- package/vite/vite-plugin-config.ts +1 -0
- package/vite/vite-plugin-dashboard-metadata.ts +19 -15
- package/vite/vite-plugin-gql-tada.ts +2 -2
- package/vite/vite-plugin-ui-config.ts +3 -2
- package/dist/plugin/config-loader.js +0 -141
- package/src/lib/components/shared/asset-preview.tsx +0 -345
- package/src/lib/components/ui/avatar.tsx +0 -38
- package/vite/config-loader.ts +0 -181
- /package/dist/plugin/{ui-config.d.ts → utils/ui-config.d.ts} +0 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { ConfirmationDialog } from '@/components/shared/confirmation-dialog.js';
|
|
2
|
+
import { CustomerSelector } from '@/components/shared/customer-selector.js';
|
|
3
|
+
import { ErrorPage } from '@/components/shared/error-page.js';
|
|
4
|
+
import { PermissionGuard } from '@/components/shared/permission-guard.js';
|
|
5
|
+
import { Button } from '@/components/ui/button.js';
|
|
6
|
+
import { Form } from '@/components/ui/form.js';
|
|
7
|
+
import { addCustomFields } from '@/framework/document-introspection/add-custom-fields.js';
|
|
8
|
+
import { useGeneratedForm } from '@/framework/form-engine/use-generated-form.js';
|
|
9
|
+
import { CustomFieldsPageBlock, Page, PageActionBar, PageActionBarRight, PageBlock, PageLayout, PageTitle } from '@/framework/layout-engine/page-layout.js';
|
|
10
|
+
import { getDetailQueryOptions, useDetailPage } from '@/framework/page/use-detail-page.js';
|
|
11
|
+
import { api } from '@/graphql/api.js';
|
|
12
|
+
import { Trans, useLingui } from '@/lib/trans.js';
|
|
13
|
+
import { useMutation, useQuery } from '@tanstack/react-query';
|
|
14
|
+
import { createFileRoute, Link, redirect, useNavigate } from '@tanstack/react-router';
|
|
15
|
+
import { ResultOf } from 'gql.tada';
|
|
16
|
+
import { User } from 'lucide-react';
|
|
17
|
+
import { toast } from 'sonner';
|
|
18
|
+
import { CustomerAddressSelector } from './components/customer-address-selector.js';
|
|
19
|
+
import { EditOrderTable } from './components/edit-order-table.js';
|
|
20
|
+
import { OrderAddress } from './components/order-address.js';
|
|
21
|
+
import { addItemToDraftOrderDocument, adjustDraftOrderLineDocument, applyCouponCodeToDraftOrderDocument, deleteDraftOrderDocument, draftOrderEligibleShippingMethodsDocument, orderDetailDocument, removeCouponCodeFromDraftOrderDocument, removeDraftOrderLineDocument, setBillingAddressForDraftOrderDocument, setCustomerForDraftOrderDocument, setDraftOrderCustomFieldsDocument, setDraftOrderShippingMethodDocument, setShippingAddressForDraftOrderDocument, transitionOrderToStateDocument, unsetBillingAddressForDraftOrderDocument, unsetShippingAddressForDraftOrderDocument } from './orders.graphql.js';
|
|
22
|
+
import { CustomFieldsForm } from '@/components/shared/custom-fields-form.js';
|
|
23
|
+
|
|
24
|
+
export const Route = createFileRoute('/_authenticated/_orders/orders_/draft/$id')({
|
|
25
|
+
component: DraftOrderPage,
|
|
26
|
+
loader: async ({
|
|
27
|
+
context,
|
|
28
|
+
params,
|
|
29
|
+
}) => {
|
|
30
|
+
if (!params.id) {
|
|
31
|
+
throw new Error('ID param is required');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const result: ResultOf<typeof orderDetailDocument> = await context.queryClient.ensureQueryData(
|
|
35
|
+
getDetailQueryOptions(addCustomFields(orderDetailDocument), { id: params.id }),
|
|
36
|
+
{ id: params.id },
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (!result.order) {
|
|
40
|
+
throw new Error(`Order with the ID ${params.id} was not found`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (result.order.state !== 'Draft') {
|
|
44
|
+
throw redirect({
|
|
45
|
+
to: `/orders/${params.id}`,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
breadcrumb: [{ path: '/orders', label: 'Orders' }, result.order.code],
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
errorComponent: ({ error }) => <ErrorPage message={error.message} />,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
function DraftOrderPage() {
|
|
57
|
+
const params = Route.useParams();
|
|
58
|
+
const { i18n } = useLingui();
|
|
59
|
+
const navigate = useNavigate();
|
|
60
|
+
|
|
61
|
+
const { entity, refreshEntity, form } = useDetailPage({
|
|
62
|
+
queryDocument: addCustomFields(orderDetailDocument),
|
|
63
|
+
setValuesForUpdate: entity => {
|
|
64
|
+
return {
|
|
65
|
+
id: entity.id,
|
|
66
|
+
customFields: entity.customFields,
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
params: { id: params.id },
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const { form: orderLineForm } = useGeneratedForm({
|
|
73
|
+
document: addCustomFields(adjustDraftOrderLineDocument),
|
|
74
|
+
varName: undefined,
|
|
75
|
+
entity: entity?.lines[0],
|
|
76
|
+
setValues: entity => {
|
|
77
|
+
return {
|
|
78
|
+
orderId: entity.id,
|
|
79
|
+
input: {
|
|
80
|
+
quantity: entity.quantity,
|
|
81
|
+
orderLineId: entity.id,
|
|
82
|
+
customFields: entity.customFields,
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const { form: orderCustomFieldsForm } = useGeneratedForm({
|
|
89
|
+
document: setDraftOrderCustomFieldsDocument,
|
|
90
|
+
varName: undefined,
|
|
91
|
+
entity: entity,
|
|
92
|
+
setValues: entity => {
|
|
93
|
+
return {
|
|
94
|
+
orderId: entity.id,
|
|
95
|
+
input: {
|
|
96
|
+
id: entity.id,
|
|
97
|
+
customFields: entity.customFields,
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const { mutate: setDraftOrderCustomFields } = useMutation({
|
|
104
|
+
mutationFn: api.mutate(setDraftOrderCustomFieldsDocument),
|
|
105
|
+
onSuccess: (result: ResultOf<typeof setDraftOrderCustomFieldsDocument>) => {
|
|
106
|
+
refreshEntity();
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const { data: eligibleShippingMethods } = useQuery({
|
|
111
|
+
queryKey: ['eligibleShippingMethods', entity?.id],
|
|
112
|
+
queryFn: () => api.query(draftOrderEligibleShippingMethodsDocument, { orderId: entity?.id ?? '' }),
|
|
113
|
+
enabled: !!entity?.shippingAddress?.streetLine1,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const { mutate: addItemToDraftOrder } = useMutation({
|
|
117
|
+
mutationFn: api.mutate(addItemToDraftOrderDocument),
|
|
118
|
+
onSuccess: (result: ResultOf<typeof addItemToDraftOrderDocument>) => {
|
|
119
|
+
const order = result.addItemToDraftOrder;
|
|
120
|
+
switch (order.__typename) {
|
|
121
|
+
case 'Order':
|
|
122
|
+
toast.success(i18n.t('Item added to order'));
|
|
123
|
+
refreshEntity();
|
|
124
|
+
break;
|
|
125
|
+
default:
|
|
126
|
+
toast.error(order.message);
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const { mutate: adjustDraftOrderLine } = useMutation({
|
|
133
|
+
mutationFn: api.mutate(adjustDraftOrderLineDocument),
|
|
134
|
+
onSuccess: (result: ResultOf<typeof adjustDraftOrderLineDocument>) => {
|
|
135
|
+
const order = result.adjustDraftOrderLine;
|
|
136
|
+
switch (order.__typename) {
|
|
137
|
+
case 'Order':
|
|
138
|
+
toast.success(i18n.t('Order line updated'));
|
|
139
|
+
refreshEntity();
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
toast.error(order.message);
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const { mutate: removeDraftOrderLine } = useMutation({
|
|
149
|
+
mutationFn: api.mutate(removeDraftOrderLineDocument),
|
|
150
|
+
onSuccess: (result: ResultOf<typeof removeDraftOrderLineDocument>) => {
|
|
151
|
+
const order = result.removeDraftOrderLine;
|
|
152
|
+
switch (order.__typename) {
|
|
153
|
+
case 'Order':
|
|
154
|
+
toast.success(i18n.t('Order line removed'));
|
|
155
|
+
refreshEntity();
|
|
156
|
+
break;
|
|
157
|
+
default:
|
|
158
|
+
toast.error(order.message);
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const { mutate: setCustomerForDraftOrder } = useMutation({
|
|
165
|
+
mutationFn: api.mutate(setCustomerForDraftOrderDocument),
|
|
166
|
+
onSuccess: (result: ResultOf<typeof setCustomerForDraftOrderDocument>) => {
|
|
167
|
+
const order = result.setCustomerForDraftOrder;
|
|
168
|
+
switch (order.__typename) {
|
|
169
|
+
case 'Order':
|
|
170
|
+
toast.success(i18n.t('Customer set for order'));
|
|
171
|
+
refreshEntity();
|
|
172
|
+
break;
|
|
173
|
+
default:
|
|
174
|
+
toast.error(order.message);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const { mutate: setShippingAddressForDraftOrder } = useMutation({
|
|
181
|
+
mutationFn: api.mutate(setShippingAddressForDraftOrderDocument),
|
|
182
|
+
onSuccess: (result: ResultOf<typeof setShippingAddressForDraftOrderDocument>) => {
|
|
183
|
+
toast.success(i18n.t('Shipping address set for order'));
|
|
184
|
+
refreshEntity();
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const { mutate: setBillingAddressForDraftOrder } = useMutation({
|
|
189
|
+
mutationFn: api.mutate(setBillingAddressForDraftOrderDocument),
|
|
190
|
+
onSuccess: (result: ResultOf<typeof setBillingAddressForDraftOrderDocument>) => {
|
|
191
|
+
toast.success(i18n.t('Billing address set for order'));
|
|
192
|
+
refreshEntity();
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const { mutate: unsetShippingAddressForDraftOrder } = useMutation({
|
|
197
|
+
mutationFn: api.mutate(unsetShippingAddressForDraftOrderDocument),
|
|
198
|
+
onSuccess: (result: ResultOf<typeof unsetShippingAddressForDraftOrderDocument>) => {
|
|
199
|
+
toast.success(i18n.t('Shipping address unset for order'));
|
|
200
|
+
refreshEntity();
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const { mutate: unsetBillingAddressForDraftOrder } = useMutation({
|
|
205
|
+
mutationFn: api.mutate(unsetBillingAddressForDraftOrderDocument),
|
|
206
|
+
onSuccess: (result: ResultOf<typeof unsetBillingAddressForDraftOrderDocument>) => {
|
|
207
|
+
toast.success(i18n.t('Billing address unset for order'));
|
|
208
|
+
refreshEntity();
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const { mutate: setShippingMethodForDraftOrder } = useMutation({
|
|
213
|
+
mutationFn: api.mutate(setDraftOrderShippingMethodDocument),
|
|
214
|
+
onSuccess: (result: ResultOf<typeof setDraftOrderShippingMethodDocument>) => {
|
|
215
|
+
const order = result.setDraftOrderShippingMethod;
|
|
216
|
+
switch (order.__typename) {
|
|
217
|
+
case 'Order':
|
|
218
|
+
toast.success(i18n.t('Shipping method set for order'));
|
|
219
|
+
refreshEntity();
|
|
220
|
+
break;
|
|
221
|
+
default:
|
|
222
|
+
toast.error(order.message);
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const { mutate: setCouponCodeForDraftOrder } = useMutation({
|
|
229
|
+
mutationFn: api.mutate(applyCouponCodeToDraftOrderDocument),
|
|
230
|
+
onSuccess: (result: ResultOf<typeof applyCouponCodeToDraftOrderDocument>) => {
|
|
231
|
+
const order = result.applyCouponCodeToDraftOrder;
|
|
232
|
+
switch (order.__typename) {
|
|
233
|
+
case 'Order':
|
|
234
|
+
toast.success(i18n.t('Coupon code set for order'));
|
|
235
|
+
refreshEntity();
|
|
236
|
+
break;
|
|
237
|
+
default:
|
|
238
|
+
toast.error(order.message);
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const { mutate: removeCouponCodeForDraftOrder } = useMutation({
|
|
245
|
+
mutationFn: api.mutate(removeCouponCodeFromDraftOrderDocument),
|
|
246
|
+
onSuccess: (result: ResultOf<typeof removeCouponCodeFromDraftOrderDocument>) => {
|
|
247
|
+
const order = result.removeCouponCodeFromDraftOrder;
|
|
248
|
+
toast.success(i18n.t('Coupon code removed from order'));
|
|
249
|
+
refreshEntity();
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const { mutate: completeDraftOrder } = useMutation({
|
|
254
|
+
mutationFn: api.mutate(transitionOrderToStateDocument),
|
|
255
|
+
onSuccess: async (result: ResultOf<typeof transitionOrderToStateDocument>) => {
|
|
256
|
+
const order = result.transitionOrderToState;
|
|
257
|
+
switch (order?.__typename) {
|
|
258
|
+
case 'Order':
|
|
259
|
+
toast.success(i18n.t('Draft order completed'));
|
|
260
|
+
refreshEntity();
|
|
261
|
+
setTimeout(() => {
|
|
262
|
+
navigate({ to: `/orders/$id`, params: { id: order.id } });
|
|
263
|
+
}, 500);
|
|
264
|
+
break;
|
|
265
|
+
default:
|
|
266
|
+
toast.error(order ? order.message : 'Unknown error');
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const { mutate: deleteDraftOrder } = useMutation({
|
|
273
|
+
mutationFn: api.mutate(deleteDraftOrderDocument),
|
|
274
|
+
onSuccess: (result: ResultOf<typeof deleteDraftOrderDocument>) => {
|
|
275
|
+
if (result.deleteDraftOrder.result === 'DELETED') {
|
|
276
|
+
toast.success(i18n.t('Draft order deleted'));
|
|
277
|
+
navigate({ to: '/orders' });
|
|
278
|
+
} else {
|
|
279
|
+
toast.error(result.deleteDraftOrder.message);
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
if (!entity) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const onSaveCustomFields = (values: any) => {
|
|
289
|
+
setDraftOrderCustomFields({ input: { id: entity.id, customFields: values.input?.customFields }, orderId: entity.id });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return (
|
|
293
|
+
<Page pageId="draft-order-detail" form={form}>
|
|
294
|
+
<PageTitle><Trans>Draft order</Trans>: {entity?.code ?? ''}</PageTitle>
|
|
295
|
+
<PageActionBar>
|
|
296
|
+
<PageActionBarRight>
|
|
297
|
+
<PermissionGuard requires={['DeleteOrder']}>
|
|
298
|
+
<ConfirmationDialog
|
|
299
|
+
title={i18n.t('Delete draft order')}
|
|
300
|
+
description={i18n.t('Are you sure you want to delete this draft order?')}
|
|
301
|
+
onConfirm={() => {
|
|
302
|
+
deleteDraftOrder({ orderId: entity.id });
|
|
303
|
+
}}
|
|
304
|
+
>
|
|
305
|
+
<Button variant="destructive" type="button">
|
|
306
|
+
<Trans>Delete draft</Trans>
|
|
307
|
+
</Button>
|
|
308
|
+
</ConfirmationDialog>
|
|
309
|
+
|
|
310
|
+
</PermissionGuard>
|
|
311
|
+
<PermissionGuard requires={['UpdateOrder']}>
|
|
312
|
+
<Button type="button"
|
|
313
|
+
disabled={!entity.customer || entity.lines.length === 0 || entity.shippingLines.length === 0 || entity.state !== 'Draft'}
|
|
314
|
+
onClick={() => completeDraftOrder({ id: entity.id, state: 'ArrangingPayment' })}
|
|
315
|
+
>
|
|
316
|
+
<Trans>Complete draft</Trans>
|
|
317
|
+
</Button>
|
|
318
|
+
</PermissionGuard>
|
|
319
|
+
</PageActionBarRight>
|
|
320
|
+
</PageActionBar>
|
|
321
|
+
<PageLayout>
|
|
322
|
+
<PageBlock column="main" blockId="order-table">
|
|
323
|
+
<EditOrderTable order={entity}
|
|
324
|
+
eligibleShippingMethods={eligibleShippingMethods?.eligibleShippingMethodsForDraftOrder ?? []}
|
|
325
|
+
onSetShippingMethod={(e) => setShippingMethodForDraftOrder({ orderId: entity.id, shippingMethodId: e.shippingMethodId })}
|
|
326
|
+
onAddItem={(e) => addItemToDraftOrder({ orderId: entity.id, input: { productVariantId: e.productVariantId, quantity: 1 } })}
|
|
327
|
+
onAdjustLine={(e) => adjustDraftOrderLine({ orderId: entity.id, input: { orderLineId: e.lineId, quantity: e.quantity, customFields: e.customFields } as any })}
|
|
328
|
+
onRemoveLine={(e) => removeDraftOrderLine({ orderId: entity.id, orderLineId: e.lineId })}
|
|
329
|
+
onApplyCouponCode={(e) => setCouponCodeForDraftOrder({ orderId: entity.id, couponCode: e.couponCode })}
|
|
330
|
+
onRemoveCouponCode={(e) => removeCouponCodeForDraftOrder({ orderId: entity.id, couponCode: e.couponCode })}
|
|
331
|
+
orderLineForm={orderLineForm}
|
|
332
|
+
/>
|
|
333
|
+
</PageBlock>
|
|
334
|
+
<PageBlock column="main" blockId="order-custom-fields" title={<Trans>Custom fields</Trans>}>
|
|
335
|
+
<Form {...orderCustomFieldsForm}>
|
|
336
|
+
<CustomFieldsForm entityType="Order" control={orderCustomFieldsForm.control} formPathPrefix='input' />
|
|
337
|
+
<div className="mt-4">
|
|
338
|
+
<Button type="submit" className=""
|
|
339
|
+
disabled={!orderCustomFieldsForm.formState.isValid || !orderCustomFieldsForm.formState.isDirty}
|
|
340
|
+
onClick={(e) => {
|
|
341
|
+
e.preventDefault();
|
|
342
|
+
e.stopPropagation();
|
|
343
|
+
orderCustomFieldsForm.handleSubmit(onSaveCustomFields)();
|
|
344
|
+
}}>
|
|
345
|
+
<Trans>Set custom fields</Trans>
|
|
346
|
+
</Button>
|
|
347
|
+
</div>
|
|
348
|
+
</Form>
|
|
349
|
+
</PageBlock>
|
|
350
|
+
<PageBlock column="side" blockId="customer" title={<Trans>Customer</Trans>}>
|
|
351
|
+
{entity?.customer?.id ? <Button variant="ghost" asChild className="mb-4">
|
|
352
|
+
<Link to={`/customers/${entity?.customer?.id}`}>
|
|
353
|
+
<User className="w-4 h-4" />
|
|
354
|
+
{entity?.customer?.firstName} {entity?.customer?.lastName}
|
|
355
|
+
</Link>
|
|
356
|
+
</Button> : null}
|
|
357
|
+
<CustomerSelector onSelect={customer => {
|
|
358
|
+
setCustomerForDraftOrder({ orderId: entity.id, customerId: customer.id });
|
|
359
|
+
}} />
|
|
360
|
+
</PageBlock>
|
|
361
|
+
<PageBlock column="side" blockId="shipping-address" title={<Trans>Shipping address</Trans>}>
|
|
362
|
+
<div className="flex flex-col">
|
|
363
|
+
<OrderAddress address={entity.shippingAddress ?? undefined} />
|
|
364
|
+
{entity.shippingAddress?.streetLine1
|
|
365
|
+
? <RemoveAddressButton onClick={() => unsetShippingAddressForDraftOrder({ orderId: entity.id })} />
|
|
366
|
+
: <CustomerAddressSelector customerId={entity.customer?.id} onSelect={address => {
|
|
367
|
+
setShippingAddressForDraftOrder({
|
|
368
|
+
orderId: entity.id, input: {
|
|
369
|
+
fullName: address.fullName,
|
|
370
|
+
company: address.company,
|
|
371
|
+
streetLine1: address.streetLine1,
|
|
372
|
+
streetLine2: address.streetLine2,
|
|
373
|
+
city: address.city,
|
|
374
|
+
province: address.province,
|
|
375
|
+
postalCode: address.postalCode,
|
|
376
|
+
countryCode: address.country.code,
|
|
377
|
+
phoneNumber: address.phoneNumber,
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}} />
|
|
381
|
+
}
|
|
382
|
+
</div>
|
|
383
|
+
</PageBlock>
|
|
384
|
+
<PageBlock column="side" blockId="billing-address" title={<Trans>Billing address</Trans>}>
|
|
385
|
+
<div className="flex flex-col">
|
|
386
|
+
<OrderAddress address={entity.billingAddress ?? undefined} />
|
|
387
|
+
{entity.billingAddress?.streetLine1
|
|
388
|
+
? <RemoveAddressButton onClick={() => unsetBillingAddressForDraftOrder({ orderId: entity.id })} />
|
|
389
|
+
: <CustomerAddressSelector customerId={entity.customer?.id} onSelect={address => {
|
|
390
|
+
setBillingAddressForDraftOrder({
|
|
391
|
+
orderId: entity.id, input: {
|
|
392
|
+
fullName: address.fullName,
|
|
393
|
+
company: address.company,
|
|
394
|
+
streetLine1: address.streetLine1,
|
|
395
|
+
streetLine2: address.streetLine2,
|
|
396
|
+
city: address.city,
|
|
397
|
+
province: address.province,
|
|
398
|
+
postalCode: address.postalCode,
|
|
399
|
+
countryCode: address.country.code,
|
|
400
|
+
phoneNumber: address.phoneNumber,
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
}} />
|
|
404
|
+
}
|
|
405
|
+
</div>
|
|
406
|
+
</PageBlock>
|
|
407
|
+
</PageLayout>
|
|
408
|
+
</Page>
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function RemoveAddressButton(props: { onClick: () => void }) {
|
|
413
|
+
return (<div className="">
|
|
414
|
+
<Button variant="outline" className="mt-4" size="sm" onClick={props.onClick}>
|
|
415
|
+
<Trans>Remove</Trans>
|
|
416
|
+
</Button>
|
|
417
|
+
</div>)
|
|
418
|
+
}
|
|
@@ -200,7 +200,9 @@ function ProductVariantDetailPage() {
|
|
|
200
200
|
<Trans>Stock level</Trans>
|
|
201
201
|
</FormLabel>
|
|
202
202
|
<FormControl>
|
|
203
|
-
<Input type="number" {
|
|
203
|
+
<Input type="number" value={field.value} onChange={e => {
|
|
204
|
+
field.onChange(e.target.valueAsNumber);
|
|
205
|
+
}} />
|
|
204
206
|
</FormControl>
|
|
205
207
|
</FormItem>
|
|
206
208
|
)}
|
|
@@ -224,7 +226,11 @@ function ProductVariantDetailPage() {
|
|
|
224
226
|
<FormLabel>
|
|
225
227
|
<Trans>Track inventory</Trans>
|
|
226
228
|
</FormLabel>
|
|
227
|
-
<Select onValueChange={
|
|
229
|
+
<Select onValueChange={val => {
|
|
230
|
+
if (val) {
|
|
231
|
+
field.onChange(val)
|
|
232
|
+
}
|
|
233
|
+
}} value={field.value}>
|
|
228
234
|
<FormControl>
|
|
229
235
|
<SelectTrigger className="">
|
|
230
236
|
<SelectValue placeholder="Track inventory" />
|
|
@@ -57,6 +57,8 @@ function PromotionDetailPage() {
|
|
|
57
57
|
transformCreateInput: values => {
|
|
58
58
|
return {
|
|
59
59
|
...values,
|
|
60
|
+
startsAt: values.startsAt || undefined,
|
|
61
|
+
endsAt: values.endsAt || undefined,
|
|
60
62
|
conditions: values.conditions.filter(c => c.code !== ''),
|
|
61
63
|
actions: values.actions.filter(a => a.code !== ''),
|
|
62
64
|
};
|
|
@@ -98,6 +100,10 @@ function PromotionDetailPage() {
|
|
|
98
100
|
if (creatingNewEntity) {
|
|
99
101
|
await navigate({ to: `../${data.id}`, from: Route.id });
|
|
100
102
|
}
|
|
103
|
+
} else {
|
|
104
|
+
toast.error(i18n.t('Failed to update promotion'), {
|
|
105
|
+
description: data.message,
|
|
106
|
+
});
|
|
101
107
|
}
|
|
102
108
|
},
|
|
103
109
|
onError: err => {
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
+
import { Badge } from '@/components/ui/badge.js';
|
|
2
|
+
import { Button } from '@/components/ui/button.js';
|
|
1
3
|
import { ListPage } from '@/framework/page/list-page.js';
|
|
4
|
+
import { api } from '@/graphql/api.js';
|
|
2
5
|
import { Trans } from '@/lib/trans.js';
|
|
3
6
|
import { createFileRoute } from '@tanstack/react-router';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { Button } from '@/components/ui/button.js';
|
|
7
|
+
import { formatRelative } from 'date-fns';
|
|
8
|
+
import { Ban, CheckCircle2Icon, CircleXIcon, ClockIcon, LoaderIcon, RotateCcw } from 'lucide-react';
|
|
7
9
|
import { PayloadDialog } from './components/payload-dialog.js';
|
|
8
|
-
import {
|
|
9
|
-
import { Ban, CircleXIcon, ClockIcon, LoaderIcon, RotateCcw } from 'lucide-react';
|
|
10
|
-
import { CheckCircle2Icon } from 'lucide-react';
|
|
11
|
-
import { api } from '@/graphql/api.js';
|
|
10
|
+
import { jobListDocument, jobQueueListDocument } from './job-queue.graphql.js';
|
|
12
11
|
|
|
13
12
|
export const Route = createFileRoute('/_authenticated/_system/job-queue')({
|
|
14
13
|
component: JobQueuePage,
|
|
@@ -58,7 +57,7 @@ function JobQueuePage() {
|
|
|
58
57
|
customizeColumns={{
|
|
59
58
|
createdAt: {
|
|
60
59
|
header: 'Created At',
|
|
61
|
-
cell: ({ row }) => formatRelative(row.original.createdAt, new Date())
|
|
60
|
+
cell: ({ row }) => <div title={row.original.createdAt}>{formatRelative(new Date(row.original.createdAt), new Date())}</div>,
|
|
62
61
|
},
|
|
63
62
|
data: {
|
|
64
63
|
header: 'Data',
|