@myparcel-dev/pdk-admin 1.14.2 → 2.0.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/CHANGELOG.md +14 -0
- package/package.json +4 -4
- package/src/__tests__/__snapshots__/exports.spec.ts.snap +8 -1
- package/src/__tests__/doComponentTestTeardown.ts +2 -0
- package/src/__tests__/mocks/index.ts +1 -1
- package/src/__tests__/mocks/mockDefaultAccount.ts +1 -0
- package/src/__tests__/mocks/mockDefaultGlobalContext.ts +2 -2
- package/src/__tests__/mocks/{mockDefaultPlatform.ts → mockDefaultProposition.ts} +1 -3
- package/src/__tests__/mocks/mockDefaultShop.ts +1 -0
- package/src/__tests__/utils/getActionIds.ts +12 -0
- package/src/__tests__/utils/mockOrderData.ts +20 -0
- package/src/actions/composables/mutations/orders/useExportOrdersMutation.ts +1 -1
- package/src/actions/composables/queries/account/useOrderCapabilitiesQuery.ts +67 -0
- package/src/actions/composables/queries/account/useShipmentCapabilitiesQuery.ts +96 -0
- package/src/actions/definitions/orders.ts +6 -8
- package/src/actions/index.ts +1 -0
- package/src/actions/orderModeActions.spec.ts +150 -0
- package/src/actions/orderModeActions.ts +90 -0
- package/src/components/OrderBox/ShipmentOptionsBox.spec.ts +109 -0
- package/src/components/OrderBox/ShipmentOptionsBox.vue +9 -25
- package/src/components/OrderListItem/OrderModeOrderListItem.spec.ts +125 -0
- package/src/components/OrderListItem/OrderModeOrderListItem.vue +10 -6
- package/src/components/PluginSettings/PluginSettingsForms.vue +7 -4
- package/src/components/common/DeliveryOptionsExcerptCarrierName.vue +3 -3
- package/src/components/modals/ShipmentOptionsModal.spec.ts +79 -0
- package/src/components/modals/ShipmentOptionsModal.vue +4 -21
- package/src/composables/components/useToggleInputContext.ts +2 -2
- package/src/composables/components/useTriStateInputContext.ts +2 -2
- package/src/composables/context/index.ts +1 -0
- package/src/composables/context/useOrderMode.spec.ts +75 -0
- package/src/composables/context/useOrderMode.ts +26 -0
- package/src/composables/language/useLanguage.spec.ts +2 -2
- package/src/composables/shipments/useShipmentData.ts +2 -2
- package/src/data/index.ts +1 -0
- package/src/data/orderMode.spec.ts +22 -0
- package/src/data/orderMode.ts +50 -0
- package/src/forms/form-builder/builders/buildAfterUpdate.spec.ts +5 -8
- package/src/forms/form-builder/operations/executeOperations.spec.ts +4 -7
- package/src/forms/form-builder/operations/executeSetPropOperation.spec.ts +4 -7
- package/src/forms/form-builder/operations/executeSetValueOperation.spec.ts +3 -2
- package/src/forms/form-builder/utils/createValueSetter.ts +21 -8
- package/src/forms/helpers/addCapabilitiesClearNotification.ts +39 -0
- package/src/forms/helpers/getDeliveryTypes.ts +5 -10
- package/src/forms/helpers/getFieldLabel.ts +7 -2
- package/src/forms/helpers/getPackageTypes.ts +7 -12
- package/src/forms/helpers/index.ts +3 -10
- package/src/forms/helpers/useFormCapabilities.spec.ts +227 -0
- package/src/forms/helpers/useFormCapabilities.ts +176 -0
- package/src/forms/pluginSettings/createPluginSettingsForm.ts +2 -2
- package/src/forms/pluginSettings/filterPluginSettingsView.spec.ts +158 -0
- package/src/forms/pluginSettings/filterPluginSettingsView.ts +96 -0
- package/src/forms/pluginSettings/index.ts +1 -0
- package/src/forms/pluginSettings/resolveSettingsValues.spec.ts +62 -0
- package/src/forms/pluginSettings/resolveSettingsValues.ts +16 -0
- package/src/forms/shipmentOptions/createShipmentOptionsForm.spec.ts +108 -0
- package/src/forms/shipmentOptions/createShipmentOptionsForm.ts +114 -52
- package/src/forms/shipmentOptions/field.ts +6 -66
- package/src/forms/shipmentOptions/fieldFactoryRegistry.ts +26 -0
- package/src/forms/shipmentOptions/fields/createCarrierField.ts +119 -30
- package/src/forms/shipmentOptions/fields/createDigitalStampRangeField.ts +3 -3
- package/src/forms/shipmentOptions/fields/createInsuranceField.ts +43 -22
- package/src/forms/shipmentOptions/fields/createPackageTypeField.ts +1 -2
- package/src/forms/shipmentOptions/fields/createShipmentOptionField.spec.ts +177 -0
- package/src/forms/shipmentOptions/fields/createShipmentOptionField.ts +90 -8
- package/src/forms/shipmentOptions/fields/index.ts +0 -9
- package/src/forms/shipmentOptions/useCapabilitiesAutoClear.spec.ts +347 -0
- package/src/forms/shipmentOptions/useCapabilitiesAutoClear.ts +452 -0
- package/src/forms/shipmentOptions/useCapabilitiesWatcher.spec.ts +14 -0
- package/src/forms/shipmentOptions/useCapabilitiesWatcher.ts +29 -0
- package/src/forms/shipmentOptions/wireProxyCapabilities.spec.ts +160 -0
- package/src/forms/shipmentOptions/wireProxyCapabilities.ts +102 -0
- package/src/index.ts +2 -0
- package/src/pdk/instance/plugins/createRegisterComponentsPlugin.ts +2 -0
- package/src/sdk/composables/api/useFetchCarrier.ts +39 -4
- package/src/sdk/composables/usePdkAdminApi.ts +8 -3
- package/src/stores/useActionStore.spec.ts +101 -0
- package/src/stores/useActionStore.ts +4 -28
- package/src/stores/useQueryStore.ts +15 -0
- package/src/types/actions/endpoints.types.ts +2 -1
- package/src/types/sdk.types.ts +54 -1
- package/src/utils/forms/formToBody.ts +2 -1
- package/src/utils/forms/getEnabledValues.spec.ts +66 -0
- package/src/utils/forms/getEnabledValues.ts +25 -0
- package/src/utils/forms/index.ts +1 -0
- package/src/utils/translations/getDynamicTranslation.ts +1 -1
- package/src/views/OrderBox.vue +11 -5
- package/src/views/OrderListItem.spec.ts +71 -0
- package/src/views/OrderListItem.vue +6 -7
- package/src/forms/helpers/createHasShipmentOptionWatcher.ts +0 -12
- package/src/forms/helpers/getCarrier.ts +0 -11
- package/src/forms/helpers/getInsuranceOptions.ts +0 -23
- package/src/forms/helpers/hasPostNlAgeCheck.ts +0 -11
- package/src/forms/helpers/hasShipmentOption.ts +0 -8
- package/src/forms/helpers/isPackageTypeMailbox.ts +0 -7
- package/src/forms/helpers/isPackageTypePackage.ts +0 -7
- package/src/forms/helpers/isPackageTypePackageOrSmall.ts +0 -7
- package/src/forms/helpers/isPackageTypeSmallPackage.ts +0 -7
- package/src/forms/helpers/setPostNlAgeCheckSubtext.ts +0 -17
- package/src/forms/shipmentOptions/fields/createAgeCheckField.ts +0 -33
- package/src/forms/shipmentOptions/fields/createDirectReturnField.ts +0 -8
- package/src/forms/shipmentOptions/fields/createHideSenderField.ts +0 -8
- package/src/forms/shipmentOptions/fields/createLargeFormatField.ts +0 -8
- package/src/forms/shipmentOptions/fields/createOnlyRecipientField.ts +0 -21
- package/src/forms/shipmentOptions/fields/createPriorityDeliveryField.ts +0 -12
- package/src/forms/shipmentOptions/fields/createReceiptCodeField.ts +0 -21
- package/src/forms/shipmentOptions/fields/createSameDayDeliveryField.ts +0 -8
- package/src/forms/shipmentOptions/fields/createSignatureField.ts +0 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- MONOWEAVE:BELOW -->
|
|
4
4
|
|
|
5
|
+
## [2.0.0](https://github.com/myparcelnl/js-pdk/compare/@myparcel-dev/pdk-admin@1.14.2...@myparcel-dev/pdk-admin@2.0.0) "@myparcel-dev/pdk-admin" (2026-06-04)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ⚠ BREAKING CHANGES
|
|
9
|
+
|
|
10
|
+
* drive admin shipment options and checkout from the PDK capabilities API (#346)
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
* drive admin shipment options and checkout from the PDK capabilities API ([#346](https://github.com/myparcelnl/js-pdk/issues/346)) ([f62b934](https://github.com/myparcelnl/js-pdk/commit/f62b9348d4f4316da28dbb97e4c335fc8aa9df80)), closes [myparcelnl/pdk#459](https://github.com/myparcelnl/pdk/issues/459) [myparcelnl/pdk#459](https://github.com/myparcelnl/pdk/issues/459) [myparcelnl/pdk#459](https://github.com/myparcelnl/pdk/issues/459)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
5
19
|
## [1.14.2](https://github.com/myparcelnl/js-pdk/compare/@myparcel-dev/pdk-admin@1.14.1...@myparcel-dev/pdk-admin@1.14.2) "@myparcel-dev/pdk-admin" (2026-05-29)
|
|
6
20
|
|
|
7
21
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myparcel-dev/pdk-admin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/myparcelnl/js-pdk.git",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"typecheck": "run ws:vue:typecheck \"$(pwd)\""
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@myparcel-dev/constants": "^2.
|
|
40
|
-
"@myparcel-dev/pdk-common": "^
|
|
41
|
-
"@myparcel-dev/sdk": "^5.
|
|
39
|
+
"@myparcel-dev/constants": "^2.9.0",
|
|
40
|
+
"@myparcel-dev/pdk-common": "^2.0.0",
|
|
41
|
+
"@myparcel-dev/sdk": "^5.1.0",
|
|
42
42
|
"@myparcel-dev/vue-form-builder": "1.0.0-beta.42.6",
|
|
43
43
|
"@tanstack/vue-query": "~4.43.0",
|
|
44
44
|
"@vueuse/core": "^10.0.0",
|
|
@@ -10,6 +10,7 @@ exports[`exports > exports from index.ts 1`] = `
|
|
|
10
10
|
"AdminInstanceContextKey",
|
|
11
11
|
"AdminModalKey",
|
|
12
12
|
"AdminView",
|
|
13
|
+
"BOX_MODE_ACTIONS",
|
|
13
14
|
"BackendEndpoint",
|
|
14
15
|
"BulkSelectCheckbox",
|
|
15
16
|
"ChildProductSettingsView",
|
|
@@ -26,13 +27,17 @@ exports[`exports > exports from index.ts 1`] = `
|
|
|
26
27
|
"FrontendEndpoint",
|
|
27
28
|
"INJECT_ADMIN_INSTANCE",
|
|
28
29
|
"INJECT_GLOBAL_PDK_ADMIN",
|
|
30
|
+
"LIST_ITEM_MODE_ACTIONS",
|
|
29
31
|
"LogLevel",
|
|
32
|
+
"MODAL_MODE_ACTIONS",
|
|
30
33
|
"ModalsView",
|
|
31
34
|
"NotificationCategory",
|
|
32
35
|
"NotificationContainer",
|
|
33
36
|
"NotificationsView",
|
|
37
|
+
"ORDER_VIEW_IN_BACKOFFICE_ID",
|
|
34
38
|
"OrderBoxView",
|
|
35
39
|
"OrderListItemView",
|
|
40
|
+
"OrderMode",
|
|
36
41
|
"PackageType",
|
|
37
42
|
"PdkAdmin",
|
|
38
43
|
"PlainElement",
|
|
@@ -42,6 +47,7 @@ exports[`exports > exports from index.ts 1`] = `
|
|
|
42
47
|
"QUERY_KEY_SHIPMENT",
|
|
43
48
|
"ResetButton",
|
|
44
49
|
"ReturnsForm",
|
|
50
|
+
"STORE_MODE_ACTIONS",
|
|
45
51
|
"ShipmentBarcode",
|
|
46
52
|
"ShipmentOptionsForm",
|
|
47
53
|
"ShipmentStatus",
|
|
@@ -145,6 +151,7 @@ exports[`exports > exports from index.ts 1`] = `
|
|
|
145
151
|
"useMultiSelectInputContext",
|
|
146
152
|
"useNotificationStore",
|
|
147
153
|
"useOrderData",
|
|
154
|
+
"useOrderMode",
|
|
148
155
|
"useOrdersData",
|
|
149
156
|
"usePdkMutation",
|
|
150
157
|
"usePrintOrdersMutation",
|
|
@@ -186,9 +193,9 @@ exports[`exports > exports from testing.ts 1`] = `
|
|
|
186
193
|
"mockDefaultEndpoints",
|
|
187
194
|
"mockDefaultGlobalContext",
|
|
188
195
|
"mockDefaultLogger",
|
|
189
|
-
"mockDefaultPlatform",
|
|
190
196
|
"mockDefaultPluginSettings",
|
|
191
197
|
"mockDefaultPrintOptionsView",
|
|
198
|
+
"mockDefaultProposition",
|
|
192
199
|
"mockDefaultShop",
|
|
193
200
|
"mockDefaultTranslations",
|
|
194
201
|
]
|
|
@@ -7,7 +7,7 @@ export * from './mockDefaultDynamicContext';
|
|
|
7
7
|
export * from './mockDefaultEndpoints';
|
|
8
8
|
export * from './mockDefaultGlobalContext';
|
|
9
9
|
export * from './mockDefaultLogger';
|
|
10
|
-
export * from './
|
|
10
|
+
export * from './mockDefaultProposition';
|
|
11
11
|
export * from './mockDefaultPluginSettings';
|
|
12
12
|
export * from './mockDefaultPrintOptionsView';
|
|
13
13
|
export * from './mockDefaultShop';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {vi} from 'vitest';
|
|
2
2
|
import {type GlobalAdminContext} from '../../types';
|
|
3
3
|
import {mockDefaultTranslations} from './mockDefaultTranslations';
|
|
4
|
-
import {
|
|
4
|
+
import {mockDefaultProposition} from './mockDefaultProposition';
|
|
5
5
|
import {mockDefaultEndpoints} from './mockDefaultEndpoints';
|
|
6
6
|
import {mockDefaultAppInfo} from './mockDefaultAppInfo';
|
|
7
7
|
|
|
@@ -15,7 +15,7 @@ export const mockDefaultGlobalContext = vi.fn((): GlobalAdminContext => {
|
|
|
15
15
|
eventPong: 'test2',
|
|
16
16
|
language: 'en-US',
|
|
17
17
|
mode: '',
|
|
18
|
-
|
|
18
|
+
proposition: mockDefaultProposition(),
|
|
19
19
|
translations: mockDefaultTranslations(),
|
|
20
20
|
};
|
|
21
21
|
});
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import {vi} from 'vitest';
|
|
2
2
|
import {type GlobalAdminContext} from '../../types';
|
|
3
3
|
|
|
4
|
-
export const
|
|
4
|
+
export const mockDefaultProposition = vi.fn((): GlobalAdminContext['proposition'] => {
|
|
5
5
|
return {
|
|
6
6
|
backofficeUrl: 'https://backoffice.test.myparcel.nl',
|
|
7
|
-
defaultCarrier: 'postnl',
|
|
8
|
-
defaultCarrierId: 1,
|
|
9
7
|
human: 'Test',
|
|
10
8
|
localCountry: 'NL',
|
|
11
9
|
name: 'test',
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {type Component, type ComponentPublicInstance} from 'vue';
|
|
2
|
+
import {type VueWrapper} from '@vue/test-utils';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extract action IDs from a mounted component that passes actions as props to a child stub.
|
|
6
|
+
*/
|
|
7
|
+
export const getActionIds = (wrapper: VueWrapper<ComponentPublicInstance>, childComponent: Component): string[] => {
|
|
8
|
+
const child = wrapper.findComponent(childComponent);
|
|
9
|
+
const actions = child.props('actions') as {id: string}[];
|
|
10
|
+
|
|
11
|
+
return actions.map((a) => a.id);
|
|
12
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {computed, ref} from 'vue';
|
|
2
|
+
import {type Mock} from 'vitest';
|
|
3
|
+
|
|
4
|
+
interface MockOrderDataOptions {
|
|
5
|
+
exported?: boolean;
|
|
6
|
+
shipments?: {id: number; deleted?: boolean}[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const mockOrderData = (
|
|
10
|
+
mockedUseOrderData: Mock,
|
|
11
|
+
{exported = false, shipments = []}: MockOrderDataOptions = {},
|
|
12
|
+
): void => {
|
|
13
|
+
const orderData = {externalIdentifier: 'TEST-1', exported, shipments};
|
|
14
|
+
|
|
15
|
+
mockedUseOrderData.mockReturnValue({
|
|
16
|
+
order: computed(() => orderData),
|
|
17
|
+
loading: computed(() => false),
|
|
18
|
+
query: {data: ref(orderData), isLoading: ref(false)} as any,
|
|
19
|
+
});
|
|
20
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {computed, type Ref} from 'vue';
|
|
2
|
+
import {useQuery} from '@tanstack/vue-query';
|
|
3
|
+
import {BackendEndpoint} from '@myparcel-dev/pdk-common';
|
|
4
|
+
import {type ProxyCapabilitiesBody, type ProxyCapabilitiesCall} from '../../../../types';
|
|
5
|
+
import {type ResolvedQuery} from '../../../../stores';
|
|
6
|
+
import {globalLogger} from '../../../../services';
|
|
7
|
+
import {usePdkAdminApi} from '../../../../sdk';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The form-facing input for {@link useOrderCapabilitiesQuery} — flat fields from the order
|
|
11
|
+
* context. Carrier / packageType / deliveryType are intentionally omitted: this query asks
|
|
12
|
+
* "what's possible for this order" across all carriers, regardless of what the user has
|
|
13
|
+
* currently selected on the form. The shipment-scoped query handles the chosen-combo case.
|
|
14
|
+
*/
|
|
15
|
+
export type OrderCapabilitiesInput = {
|
|
16
|
+
cc?: string;
|
|
17
|
+
weight?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Order-scoped capabilities query — fires the shared CapabilitiesAction with ONLY destination
|
|
22
|
+
* and weight, no carrier/packageType/deliveryType filters. Returns one entry per carrier with
|
|
23
|
+
* the union of `packageTypes`, `deliveryTypes`, and `options` valid for the order context.
|
|
24
|
+
*
|
|
25
|
+
* Drives the carrier / packageType / deliveryType dropdowns. Refetches only on cc / weight
|
|
26
|
+
* changes so option toggles and dropdown picks don't trigger needless network round-trips.
|
|
27
|
+
*
|
|
28
|
+
* Errors are logged via `globalLogger.error` so we have a breadcrumb for support, but we
|
|
29
|
+
* deliberately don't surface a user-facing toast — an intermittent capabilities failure
|
|
30
|
+
* shouldn't block the form, and the auto-clear treats the errored state as still-loading.
|
|
31
|
+
*/
|
|
32
|
+
export const useOrderCapabilitiesQuery = (
|
|
33
|
+
input: Ref<OrderCapabilitiesInput>,
|
|
34
|
+
): ResolvedQuery<BackendEndpoint.ProxyCapabilities> => {
|
|
35
|
+
const enabled = computed(() => Boolean(input.value.cc));
|
|
36
|
+
const queryKey = computed(() => [BackendEndpoint.ProxyCapabilities, 'order', input.value] as const);
|
|
37
|
+
|
|
38
|
+
return useQuery(
|
|
39
|
+
queryKey,
|
|
40
|
+
async () => {
|
|
41
|
+
const {cc, weight} = input.value;
|
|
42
|
+
|
|
43
|
+
if (!cc) return [];
|
|
44
|
+
|
|
45
|
+
const pdk = usePdkAdminApi();
|
|
46
|
+
const proxyCapabilities = pdk.proxyCapabilities as unknown as ProxyCapabilitiesCall;
|
|
47
|
+
|
|
48
|
+
const body: ProxyCapabilitiesBody = {
|
|
49
|
+
recipient: {countryCode: cc},
|
|
50
|
+
...(weight !== undefined ? {physicalProperties: {weight: {value: weight, unit: 'g'}}} : null),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const response = await proxyCapabilities({body, parameters: {filterSupported: true}});
|
|
54
|
+
|
|
55
|
+
return response.results ?? [];
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
enabled,
|
|
59
|
+
refetchOnWindowFocus: false,
|
|
60
|
+
refetchOnMount: false,
|
|
61
|
+
refetchOnReconnect: false,
|
|
62
|
+
onError: (error) => {
|
|
63
|
+
globalLogger.error('order-capabilities-query', 'request failed', error);
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {computed, type Ref} from 'vue';
|
|
2
|
+
import {useQuery} from '@tanstack/vue-query';
|
|
3
|
+
import {BackendEndpoint} from '@myparcel-dev/pdk-common';
|
|
4
|
+
import {type ProxyCapabilitiesBody, type ProxyCapabilitiesCall} from '../../../../types';
|
|
5
|
+
import {type ResolvedQuery} from '../../../../stores';
|
|
6
|
+
import {globalLogger} from '../../../../services';
|
|
7
|
+
import {usePdkAdminApi} from '../../../../sdk';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The form-facing selection input — flat fields the form already has. The composable maps this
|
|
11
|
+
* to the actual capabilities API request body shape (`recipient`, `physicalProperties`, …) at
|
|
12
|
+
* the call site so consumers don't need to know the API's nesting conventions. Every field is
|
|
13
|
+
* optional because the form may not have all values yet; the query gates on `cc` via `enabled`
|
|
14
|
+
* and only fires once a destination is known. The shipment query also requires `carrier`,
|
|
15
|
+
* `packageType` AND `deliveryType` to be set before firing — anything less wouldn't yield
|
|
16
|
+
* shipment-specific metadata.
|
|
17
|
+
*
|
|
18
|
+
* `options` and `filterSupported` are intentionally not part of the selection: option toggles
|
|
19
|
+
* don't drive refetches (option interactions resolve client-side), and the composable always
|
|
20
|
+
* opts in to server-side allowlist filtering via the `filterSupported` query parameter.
|
|
21
|
+
*/
|
|
22
|
+
export type CapabilitiesSelection = {
|
|
23
|
+
cc?: string;
|
|
24
|
+
weight?: number;
|
|
25
|
+
carrier?: string;
|
|
26
|
+
packageType?: string;
|
|
27
|
+
deliveryType?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Shipment-scoped capabilities query — fires the shared CapabilitiesAction with the FULL
|
|
32
|
+
* selection (carrier + packageType + deliveryType + cc + weight) and returns the matching
|
|
33
|
+
* carrier entry (the one shipment configuration the user has chosen). Drives the option panel
|
|
34
|
+
* and acts as the invalid-combo signal: when `results` is empty, the chosen combination isn't
|
|
35
|
+
* valid for the order context.
|
|
36
|
+
*
|
|
37
|
+
* The selection ref is the only refetch trigger — no window-focus, mount, or reconnect refetches.
|
|
38
|
+
* Server-side option filtering is opted in via the `filterSupported` query parameter so admin sees
|
|
39
|
+
* the same option allowlist that `Carrier::attributesToArray` applies on the contract-definition
|
|
40
|
+
* path.
|
|
41
|
+
*
|
|
42
|
+
* Errors are logged via `globalLogger.error` so we have a breadcrumb for support, but we
|
|
43
|
+
* deliberately don't surface a user-facing toast — an intermittent capabilities failure
|
|
44
|
+
* shouldn't block the form, and the auto-clear treats the errored state as still-loading.
|
|
45
|
+
*
|
|
46
|
+
* The selection ref is accepted as `Readonly<Ref>` because callers typically pass the
|
|
47
|
+
* debounced ref from `refDebounced`, which is read-only — and the composable never mutates
|
|
48
|
+
* the selection internally.
|
|
49
|
+
*/
|
|
50
|
+
export const useShipmentCapabilitiesQuery = (
|
|
51
|
+
selection: Readonly<Ref<CapabilitiesSelection>>,
|
|
52
|
+
): ResolvedQuery<BackendEndpoint.ProxyCapabilities> => {
|
|
53
|
+
const enabled = computed(() =>
|
|
54
|
+
Boolean(
|
|
55
|
+
selection.value.cc && selection.value.carrier && selection.value.packageType && selection.value.deliveryType,
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
const queryKey = computed(() => [BackendEndpoint.ProxyCapabilities, 'shipment', selection.value] as const);
|
|
59
|
+
|
|
60
|
+
return useQuery(
|
|
61
|
+
queryKey,
|
|
62
|
+
async () => {
|
|
63
|
+
const {cc, weight, carrier, packageType, deliveryType} = selection.value;
|
|
64
|
+
|
|
65
|
+
// The `enabled` gate guarantees these are all present when this runs; TypeScript can't
|
|
66
|
+
// see through the runtime check, so we narrow explicitly here.
|
|
67
|
+
if (!cc || !carrier || !packageType || !deliveryType) return [];
|
|
68
|
+
|
|
69
|
+
const pdk = usePdkAdminApi();
|
|
70
|
+
const proxyCapabilities = pdk.proxyCapabilities as unknown as ProxyCapabilitiesCall;
|
|
71
|
+
|
|
72
|
+
const body: ProxyCapabilitiesBody = {
|
|
73
|
+
recipient: {countryCode: cc},
|
|
74
|
+
// PDK orders carry physical-properties weight in grams (see WeightServiceInterface::UNIT_GRAMS),
|
|
75
|
+
// and the SDK's PhysicalPropertiesWeightV2 expects a {value, unit} object — not a primitive.
|
|
76
|
+
...(weight !== undefined ? {physicalProperties: {weight: {value: weight, unit: 'g'}}} : null),
|
|
77
|
+
carrier,
|
|
78
|
+
packageType,
|
|
79
|
+
deliveryType,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const response = await proxyCapabilities({body, parameters: {filterSupported: true}});
|
|
83
|
+
|
|
84
|
+
return response.results ?? [];
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
enabled,
|
|
88
|
+
refetchOnWindowFocus: false,
|
|
89
|
+
refetchOnMount: false,
|
|
90
|
+
refetchOnReconnect: false,
|
|
91
|
+
onError: (error) => {
|
|
92
|
+
globalLogger.error('shipment-capabilities-query', 'request failed', error);
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
};
|
|
@@ -112,17 +112,15 @@ export const ordersPrintAction = defineAction({
|
|
|
112
112
|
/**
|
|
113
113
|
* Shown on orders that have been exported to MyParcel using order mode.
|
|
114
114
|
*/
|
|
115
|
+
export const ORDER_VIEW_IN_BACKOFFICE_ID = 'show-exported-order';
|
|
116
|
+
|
|
115
117
|
export const orderViewInBackofficeAction = defineAction({
|
|
116
|
-
id:
|
|
118
|
+
id: ORDER_VIEW_IN_BACKOFFICE_ID,
|
|
117
119
|
icon: AdminIcon.External,
|
|
118
120
|
label: 'order_view_in_backoffice',
|
|
119
121
|
handler() {
|
|
120
|
-
const {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
BE: 'https://backoffice.sendmyparcel.be/orders',
|
|
124
|
-
};
|
|
125
|
-
const url = backofficeUrls[platform.localCountry as 'NL' | 'BE'] || backofficeUrls['NL'];
|
|
126
|
-
window.open(url, '_blank');
|
|
122
|
+
const {proposition} = useGlobalContext();
|
|
123
|
+
const baseUrl = proposition.backofficeUrl.replace(/\/$/, '');
|
|
124
|
+
window.open(`${baseUrl}/orders`, '_blank');
|
|
127
125
|
},
|
|
128
126
|
});
|
package/src/actions/index.ts
CHANGED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest';
|
|
2
|
+
import {AdminAction, OrderMode} from '../data';
|
|
3
|
+
import {
|
|
4
|
+
BOX_MODE_ACTIONS,
|
|
5
|
+
LIST_ITEM_MODE_ACTIONS,
|
|
6
|
+
MODAL_MODE_ACTIONS,
|
|
7
|
+
ORDER_VIEW_IN_BACKOFFICE_ID,
|
|
8
|
+
STORE_MODE_ACTIONS,
|
|
9
|
+
} from './orderModeActions';
|
|
10
|
+
|
|
11
|
+
const getDefinitionIds = (actions: {name?: string; id?: string}[]): string[] =>
|
|
12
|
+
actions.map((a) => a.name ?? a.id ?? '');
|
|
13
|
+
|
|
14
|
+
describe('orderModeActions', () => {
|
|
15
|
+
describe('all mappings cover every OrderMode', () => {
|
|
16
|
+
const allModes = Object.values(OrderMode);
|
|
17
|
+
|
|
18
|
+
it.each([
|
|
19
|
+
{name: 'STORE_MODE_ACTIONS', mapping: STORE_MODE_ACTIONS},
|
|
20
|
+
{name: 'BOX_MODE_ACTIONS', mapping: BOX_MODE_ACTIONS},
|
|
21
|
+
{name: 'MODAL_MODE_ACTIONS', mapping: MODAL_MODE_ACTIONS},
|
|
22
|
+
{name: 'LIST_ITEM_MODE_ACTIONS', mapping: LIST_ITEM_MODE_ACTIONS},
|
|
23
|
+
])('$name has an entry for every OrderMode', ({mapping}) => {
|
|
24
|
+
expect(Object.keys(mapping)).toEqual(expect.arrayContaining(allModes));
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('LIST_ITEM_MODE_ACTIONS', () => {
|
|
29
|
+
it('includes edit and export for OrderV1', () => {
|
|
30
|
+
const ids = getDefinitionIds(LIST_ITEM_MODE_ACTIONS[OrderMode.OrderV1]);
|
|
31
|
+
|
|
32
|
+
expect(ids).toEqual([AdminAction.OrdersEdit, AdminAction.OrdersExport]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('includes edit and export-to-shipments for OrderV2 (hybrid)', () => {
|
|
36
|
+
const ids = getDefinitionIds(LIST_ITEM_MODE_ACTIONS[OrderMode.OrderV2]);
|
|
37
|
+
|
|
38
|
+
expect(ids).toEqual([AdminAction.OrdersEdit, AdminAction.OrdersExport]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('has no actions for Shipments mode', () => {
|
|
42
|
+
expect(LIST_ITEM_MODE_ACTIONS[OrderMode.Shipments]).toHaveLength(0);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('STORE_MODE_ACTIONS', () => {
|
|
47
|
+
it('registers export and backoffice actions for OrderV1', () => {
|
|
48
|
+
const ids = getDefinitionIds(STORE_MODE_ACTIONS[OrderMode.OrderV1]);
|
|
49
|
+
|
|
50
|
+
expect(ids).toContain(AdminAction.OrdersExport);
|
|
51
|
+
expect(ids).toContain(ORDER_VIEW_IN_BACKOFFICE_ID);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('registers all shipment actions for Shipments mode', () => {
|
|
55
|
+
const ids = getDefinitionIds(STORE_MODE_ACTIONS[OrderMode.Shipments]);
|
|
56
|
+
|
|
57
|
+
expect(ids).toContain(AdminAction.OrdersPrint);
|
|
58
|
+
expect(ids).toContain(AdminAction.OrdersExport);
|
|
59
|
+
expect(ids).toContain(AdminAction.OrdersExportPrint);
|
|
60
|
+
expect(ids).toContain(AdminAction.ShipmentsExportReturn);
|
|
61
|
+
expect(ids).toContain(AdminAction.ShipmentsDelete);
|
|
62
|
+
expect(ids).toContain(AdminAction.ShipmentsUpdate);
|
|
63
|
+
expect(ids).toContain(AdminAction.ShipmentsPrint);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('registers the full shipment action set for OrderV2 (hybrid mirrors Shipments)', () => {
|
|
67
|
+
const ids = getDefinitionIds(STORE_MODE_ACTIONS[OrderMode.OrderV2]);
|
|
68
|
+
|
|
69
|
+
expect(ids).toContain(AdminAction.OrdersPrint);
|
|
70
|
+
expect(ids).toContain(AdminAction.OrdersExport);
|
|
71
|
+
expect(ids).toContain(AdminAction.OrdersExportPrint);
|
|
72
|
+
expect(ids).toContain(AdminAction.ShipmentsExportReturn);
|
|
73
|
+
expect(ids).toContain(AdminAction.ShipmentsDelete);
|
|
74
|
+
expect(ids).toContain(AdminAction.ShipmentsUpdate);
|
|
75
|
+
expect(ids).toContain(AdminAction.ShipmentsPrint);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('BOX_MODE_ACTIONS', () => {
|
|
80
|
+
it('includes export for OrderV1', () => {
|
|
81
|
+
const ids = getDefinitionIds(BOX_MODE_ACTIONS[OrderMode.OrderV1]);
|
|
82
|
+
|
|
83
|
+
expect(ids).toEqual([AdminAction.OrdersExport]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('includes export-to-shipments, print, and export-print for Shipments', () => {
|
|
87
|
+
const ids = getDefinitionIds(BOX_MODE_ACTIONS[OrderMode.Shipments]);
|
|
88
|
+
|
|
89
|
+
expect(ids).toContain(AdminAction.OrdersExport);
|
|
90
|
+
expect(ids).toContain(AdminAction.OrdersPrint);
|
|
91
|
+
expect(ids).toContain(AdminAction.OrdersExportPrint);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('mirrors the Shipments set for OrderV2 (hybrid)', () => {
|
|
95
|
+
const ids = getDefinitionIds(BOX_MODE_ACTIONS[OrderMode.OrderV2]);
|
|
96
|
+
|
|
97
|
+
expect(ids).toContain(AdminAction.OrdersExport);
|
|
98
|
+
expect(ids).toContain(AdminAction.OrdersPrint);
|
|
99
|
+
expect(ids).toContain(AdminAction.OrdersExportPrint);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('MODAL_MODE_ACTIONS', () => {
|
|
104
|
+
it('includes export for OrderV1', () => {
|
|
105
|
+
const ids = getDefinitionIds(MODAL_MODE_ACTIONS[OrderMode.OrderV1]);
|
|
106
|
+
|
|
107
|
+
expect(ids).toEqual([AdminAction.OrdersExport]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('includes export-to-shipments and export-print for Shipments (no standalone print)', () => {
|
|
111
|
+
const ids = getDefinitionIds(MODAL_MODE_ACTIONS[OrderMode.Shipments]);
|
|
112
|
+
|
|
113
|
+
expect(ids).toContain(AdminAction.OrdersExport);
|
|
114
|
+
expect(ids).toContain(AdminAction.OrdersExportPrint);
|
|
115
|
+
expect(ids).not.toContain(AdminAction.OrdersPrint);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('mirrors the Shipments set for OrderV2 (hybrid)', () => {
|
|
119
|
+
const ids = getDefinitionIds(MODAL_MODE_ACTIONS[OrderMode.OrderV2]);
|
|
120
|
+
|
|
121
|
+
expect(ids).toContain(AdminAction.OrdersExport);
|
|
122
|
+
expect(ids).toContain(AdminAction.OrdersExportPrint);
|
|
123
|
+
expect(ids).not.toContain(AdminAction.OrdersPrint);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('consistency between mappings', () => {
|
|
128
|
+
it('BOX actions are a subset of STORE actions for each mode', () => {
|
|
129
|
+
for (const mode of Object.values(OrderMode)) {
|
|
130
|
+
const storeIds = getDefinitionIds(STORE_MODE_ACTIONS[mode]);
|
|
131
|
+
const boxIds = getDefinitionIds(BOX_MODE_ACTIONS[mode]);
|
|
132
|
+
|
|
133
|
+
for (const id of boxIds) {
|
|
134
|
+
expect(storeIds, `BOX action ${id} in ${mode} mode should also be in STORE`).toContain(id);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('MODAL actions are a subset of STORE actions for each mode', () => {
|
|
140
|
+
for (const mode of Object.values(OrderMode)) {
|
|
141
|
+
const storeIds = getDefinitionIds(STORE_MODE_ACTIONS[mode]);
|
|
142
|
+
const modalIds = getDefinitionIds(MODAL_MODE_ACTIONS[mode]);
|
|
143
|
+
|
|
144
|
+
for (const id of modalIds) {
|
|
145
|
+
expect(storeIds, `MODAL action ${id} in ${mode} mode should also be in STORE`).toContain(id);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {type AnyActionDefinition} from '../types';
|
|
2
|
+
import {OrderMode} from '../data';
|
|
3
|
+
import {
|
|
4
|
+
orderExportAction,
|
|
5
|
+
orderExportToShipmentsAction,
|
|
6
|
+
ordersEditAction,
|
|
7
|
+
ordersExportPrintShipmentsAction,
|
|
8
|
+
ordersPrintAction,
|
|
9
|
+
orderViewInBackofficeAction,
|
|
10
|
+
shipmentsDeleteAction,
|
|
11
|
+
shipmentsExportReturnAction,
|
|
12
|
+
shipmentsPrintAction,
|
|
13
|
+
shipmentsUpdateAction,
|
|
14
|
+
} from './definitions';
|
|
15
|
+
|
|
16
|
+
export {ORDER_VIEW_IN_BACKOFFICE_ID} from './definitions';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Mode-specific actions for global store registration (superset of all available actions per mode).
|
|
20
|
+
*
|
|
21
|
+
* OrderV2 currently mirrors the Shipments set so manual shipment export and the related
|
|
22
|
+
* actions remain available while V2 fulfilment continues externally. INT-1590 will
|
|
23
|
+
* make this conditional on sales-channel detection so V2 with an active sales channel
|
|
24
|
+
* goes back to an empty set.
|
|
25
|
+
*/
|
|
26
|
+
export const STORE_MODE_ACTIONS: Record<OrderMode, AnyActionDefinition[]> = {
|
|
27
|
+
[OrderMode.OrderV1]: [orderExportAction, orderViewInBackofficeAction],
|
|
28
|
+
[OrderMode.Shipments]: [
|
|
29
|
+
ordersPrintAction,
|
|
30
|
+
orderExportToShipmentsAction,
|
|
31
|
+
ordersExportPrintShipmentsAction,
|
|
32
|
+
shipmentsExportReturnAction,
|
|
33
|
+
shipmentsDeleteAction,
|
|
34
|
+
shipmentsUpdateAction,
|
|
35
|
+
shipmentsPrintAction,
|
|
36
|
+
],
|
|
37
|
+
// @TODO INT-1590: empty this array again once sales-channel detection gates V2.
|
|
38
|
+
[OrderMode.OrderV2]: [
|
|
39
|
+
ordersPrintAction,
|
|
40
|
+
orderExportToShipmentsAction,
|
|
41
|
+
ordersExportPrintShipmentsAction,
|
|
42
|
+
shipmentsExportReturnAction,
|
|
43
|
+
shipmentsDeleteAction,
|
|
44
|
+
shipmentsUpdateAction,
|
|
45
|
+
shipmentsPrintAction,
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Mode-specific actions for the ShipmentOptionsBox component.
|
|
51
|
+
*
|
|
52
|
+
* OrderV2 currently mirrors the Shipments set so the order-detail box exposes manual
|
|
53
|
+
* export, print and export-print actions. {@see STORE_MODE_ACTIONS} for the
|
|
54
|
+
* INT-1590 follow-up note.
|
|
55
|
+
*/
|
|
56
|
+
export const BOX_MODE_ACTIONS: Record<OrderMode, AnyActionDefinition[]> = {
|
|
57
|
+
[OrderMode.OrderV1]: [orderExportAction],
|
|
58
|
+
[OrderMode.Shipments]: [orderExportToShipmentsAction, ordersPrintAction, ordersExportPrintShipmentsAction],
|
|
59
|
+
// @TODO INT-1590: empty this array again once sales-channel detection gates V2.
|
|
60
|
+
[OrderMode.OrderV2]: [orderExportToShipmentsAction, ordersPrintAction, ordersExportPrintShipmentsAction],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Mode-specific actions for the ShipmentOptionsModal component.
|
|
65
|
+
*
|
|
66
|
+
* OrderV2 currently mirrors the Shipments set so the modal exposes manual export and
|
|
67
|
+
* export-print actions. {@see STORE_MODE_ACTIONS} for the INT-1590 follow-up note.
|
|
68
|
+
*/
|
|
69
|
+
export const MODAL_MODE_ACTIONS: Record<OrderMode, AnyActionDefinition[]> = {
|
|
70
|
+
[OrderMode.OrderV1]: [orderExportAction],
|
|
71
|
+
[OrderMode.Shipments]: [orderExportToShipmentsAction, ordersExportPrintShipmentsAction],
|
|
72
|
+
// @TODO INT-1590: empty this array again once sales-channel detection gates V2.
|
|
73
|
+
[OrderMode.OrderV2]: [orderExportToShipmentsAction, ordersExportPrintShipmentsAction],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Mode-specific actions rendered in the order-list-item button group.
|
|
78
|
+
* Shipments mode uses its own computed action logic in ShipmentModeOrderListItem.vue.
|
|
79
|
+
*
|
|
80
|
+
* OrderV2 surfaces the same edit + manual export combination as OrderV1, but routes
|
|
81
|
+
* export through the shipments-export action because in V2 the backend falls through
|
|
82
|
+
* to the shipments-export path. {@see STORE_MODE_ACTIONS} for the INT-1590 follow-up
|
|
83
|
+
* note.
|
|
84
|
+
*/
|
|
85
|
+
export const LIST_ITEM_MODE_ACTIONS: Record<OrderMode, AnyActionDefinition[]> = {
|
|
86
|
+
[OrderMode.OrderV1]: [ordersEditAction, orderExportAction],
|
|
87
|
+
// @TODO INT-1590: drop orderExportToShipmentsAction once sales-channel detection gates V2.
|
|
88
|
+
[OrderMode.OrderV2]: [ordersEditAction, orderExportToShipmentsAction],
|
|
89
|
+
[OrderMode.Shipments]: [],
|
|
90
|
+
};
|