@myparcel-dev/pdk-admin 1.14.2 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +6 -6
  3. package/src/__tests__/__snapshots__/exports.spec.ts.snap +8 -1
  4. package/src/__tests__/doComponentTestTeardown.ts +2 -0
  5. package/src/__tests__/mocks/index.ts +1 -1
  6. package/src/__tests__/mocks/mockDefaultAccount.ts +1 -0
  7. package/src/__tests__/mocks/mockDefaultGlobalContext.ts +2 -2
  8. package/src/__tests__/mocks/{mockDefaultPlatform.ts → mockDefaultProposition.ts} +1 -3
  9. package/src/__tests__/mocks/mockDefaultShop.ts +1 -0
  10. package/src/__tests__/utils/getActionIds.ts +12 -0
  11. package/src/__tests__/utils/mockOrderData.ts +20 -0
  12. package/src/actions/composables/mutations/orders/useExportOrdersMutation.ts +1 -1
  13. package/src/actions/composables/queries/account/useOrderCapabilitiesQuery.ts +67 -0
  14. package/src/actions/composables/queries/account/useShipmentCapabilitiesQuery.ts +96 -0
  15. package/src/actions/definitions/orders.ts +6 -8
  16. package/src/actions/index.ts +1 -0
  17. package/src/actions/orderModeActions.spec.ts +150 -0
  18. package/src/actions/orderModeActions.ts +90 -0
  19. package/src/components/OrderBox/ShipmentOptionsBox.spec.ts +109 -0
  20. package/src/components/OrderBox/ShipmentOptionsBox.vue +9 -25
  21. package/src/components/OrderListItem/OrderModeOrderListItem.spec.ts +125 -0
  22. package/src/components/OrderListItem/OrderModeOrderListItem.vue +10 -6
  23. package/src/components/PluginSettings/PluginSettingsForms.vue +7 -4
  24. package/src/components/common/DeliveryOptionsExcerptCarrierName.vue +3 -3
  25. package/src/components/modals/ShipmentOptionsModal.spec.ts +79 -0
  26. package/src/components/modals/ShipmentOptionsModal.vue +4 -21
  27. package/src/composables/components/useToggleInputContext.ts +2 -2
  28. package/src/composables/components/useTriStateInputContext.ts +2 -2
  29. package/src/composables/context/index.ts +1 -0
  30. package/src/composables/context/useOrderMode.spec.ts +75 -0
  31. package/src/composables/context/useOrderMode.ts +26 -0
  32. package/src/composables/language/useLanguage.spec.ts +2 -2
  33. package/src/composables/shipments/useShipmentData.ts +2 -2
  34. package/src/data/index.ts +1 -0
  35. package/src/data/orderMode.spec.ts +22 -0
  36. package/src/data/orderMode.ts +50 -0
  37. package/src/forms/form-builder/builders/buildAfterUpdate.spec.ts +5 -8
  38. package/src/forms/form-builder/operations/executeOperations.spec.ts +4 -7
  39. package/src/forms/form-builder/operations/executeSetPropOperation.spec.ts +4 -7
  40. package/src/forms/form-builder/operations/executeSetValueOperation.spec.ts +3 -2
  41. package/src/forms/form-builder/utils/createValueSetter.ts +21 -8
  42. package/src/forms/helpers/addCapabilitiesClearNotification.ts +39 -0
  43. package/src/forms/helpers/getDeliveryTypes.ts +5 -10
  44. package/src/forms/helpers/getFieldLabel.ts +7 -2
  45. package/src/forms/helpers/getPackageTypes.ts +7 -12
  46. package/src/forms/helpers/index.ts +3 -10
  47. package/src/forms/helpers/useFormCapabilities.spec.ts +227 -0
  48. package/src/forms/helpers/useFormCapabilities.ts +176 -0
  49. package/src/forms/pluginSettings/createPluginSettingsForm.ts +2 -2
  50. package/src/forms/pluginSettings/filterPluginSettingsView.spec.ts +158 -0
  51. package/src/forms/pluginSettings/filterPluginSettingsView.ts +96 -0
  52. package/src/forms/pluginSettings/index.ts +1 -0
  53. package/src/forms/pluginSettings/resolveSettingsValues.spec.ts +62 -0
  54. package/src/forms/pluginSettings/resolveSettingsValues.ts +16 -0
  55. package/src/forms/shipmentOptions/createShipmentOptionsForm.spec.ts +108 -0
  56. package/src/forms/shipmentOptions/createShipmentOptionsForm.ts +114 -52
  57. package/src/forms/shipmentOptions/field.ts +6 -66
  58. package/src/forms/shipmentOptions/fieldFactoryRegistry.ts +26 -0
  59. package/src/forms/shipmentOptions/fields/createCarrierField.ts +119 -30
  60. package/src/forms/shipmentOptions/fields/createDigitalStampRangeField.ts +3 -3
  61. package/src/forms/shipmentOptions/fields/createInsuranceField.ts +43 -22
  62. package/src/forms/shipmentOptions/fields/createPackageTypeField.ts +1 -2
  63. package/src/forms/shipmentOptions/fields/createShipmentOptionField.spec.ts +177 -0
  64. package/src/forms/shipmentOptions/fields/createShipmentOptionField.ts +90 -8
  65. package/src/forms/shipmentOptions/fields/index.ts +0 -9
  66. package/src/forms/shipmentOptions/useCapabilitiesAutoClear.spec.ts +347 -0
  67. package/src/forms/shipmentOptions/useCapabilitiesAutoClear.ts +452 -0
  68. package/src/forms/shipmentOptions/useCapabilitiesWatcher.spec.ts +14 -0
  69. package/src/forms/shipmentOptions/useCapabilitiesWatcher.ts +29 -0
  70. package/src/forms/shipmentOptions/wireProxyCapabilities.spec.ts +160 -0
  71. package/src/forms/shipmentOptions/wireProxyCapabilities.ts +102 -0
  72. package/src/index.ts +2 -0
  73. package/src/pdk/instance/plugins/createRegisterComponentsPlugin.ts +2 -0
  74. package/src/sdk/composables/api/useFetchCarrier.ts +39 -4
  75. package/src/sdk/composables/usePdkAdminApi.ts +8 -3
  76. package/src/stores/useActionStore.spec.ts +101 -0
  77. package/src/stores/useActionStore.ts +4 -28
  78. package/src/stores/useQueryStore.ts +15 -0
  79. package/src/types/actions/endpoints.types.ts +2 -1
  80. package/src/types/sdk.types.ts +54 -1
  81. package/src/utils/forms/formToBody.ts +2 -1
  82. package/src/utils/forms/getEnabledValues.spec.ts +66 -0
  83. package/src/utils/forms/getEnabledValues.ts +25 -0
  84. package/src/utils/forms/index.ts +1 -0
  85. package/src/utils/translations/getDynamicTranslation.ts +1 -1
  86. package/src/views/OrderBox.vue +11 -5
  87. package/src/views/OrderListItem.spec.ts +71 -0
  88. package/src/views/OrderListItem.vue +6 -7
  89. package/src/forms/helpers/createHasShipmentOptionWatcher.ts +0 -12
  90. package/src/forms/helpers/getCarrier.ts +0 -11
  91. package/src/forms/helpers/getInsuranceOptions.ts +0 -23
  92. package/src/forms/helpers/hasPostNlAgeCheck.ts +0 -11
  93. package/src/forms/helpers/hasShipmentOption.ts +0 -8
  94. package/src/forms/helpers/isPackageTypeMailbox.ts +0 -7
  95. package/src/forms/helpers/isPackageTypePackage.ts +0 -7
  96. package/src/forms/helpers/isPackageTypePackageOrSmall.ts +0 -7
  97. package/src/forms/helpers/isPackageTypeSmallPackage.ts +0 -7
  98. package/src/forms/helpers/setPostNlAgeCheckSubtext.ts +0 -17
  99. package/src/forms/shipmentOptions/fields/createAgeCheckField.ts +0 -33
  100. package/src/forms/shipmentOptions/fields/createDirectReturnField.ts +0 -8
  101. package/src/forms/shipmentOptions/fields/createHideSenderField.ts +0 -8
  102. package/src/forms/shipmentOptions/fields/createLargeFormatField.ts +0 -8
  103. package/src/forms/shipmentOptions/fields/createOnlyRecipientField.ts +0 -21
  104. package/src/forms/shipmentOptions/fields/createPriorityDeliveryField.ts +0 -12
  105. package/src/forms/shipmentOptions/fields/createReceiptCodeField.ts +0 -21
  106. package/src/forms/shipmentOptions/fields/createSameDayDeliveryField.ts +0 -8
  107. package/src/forms/shipmentOptions/fields/createSignatureField.ts +0 -11
@@ -0,0 +1,109 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import {computed, defineComponent, h} from 'vue';
4
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
5
+ import {config, mount} from '@vue/test-utils';
6
+ import {AdminAction, OrderMode} from '../../data';
7
+ import {useOrderData} from '../../composables/orders/useOrderData';
8
+ import {useOrderMode} from '../../composables/context/useOrderMode';
9
+ import {ORDER_VIEW_IN_BACKOFFICE_ID} from '../../actions';
10
+ import {mockOrderData} from '../../__tests__/utils/mockOrderData';
11
+ import {getActionIds} from '../../__tests__/utils/getActionIds';
12
+ import {doComponentTestSetup, doComponentTestTeardown} from '../../__tests__';
13
+ import ShipmentOptionsBox from './ShipmentOptionsBox.vue';
14
+
15
+ vi.mock('../../composables/context/useOrderMode', () => ({
16
+ useOrderMode: vi.fn(),
17
+ }));
18
+
19
+ vi.mock('../../composables/orders/useOrderData', () => ({
20
+ useOrderData: vi.fn(),
21
+ }));
22
+
23
+ vi.mock('../../composables/language/useLanguage', () => ({
24
+ useLanguage: vi.fn(() => ({
25
+ translate: (key: string) => key,
26
+ })),
27
+ }));
28
+
29
+ const mockedUseOrderMode = vi.mocked(useOrderMode);
30
+ const mockedUseOrderData = vi.mocked(useOrderData);
31
+
32
+ // eslint-disable-next-line @typescript-eslint/naming-convention
33
+ const PdkConceptBoxWrapperStub = defineComponent({
34
+ name: 'PdkConceptBoxWrapper',
35
+ props: {actions: Array, loading: Boolean},
36
+ render() {
37
+ return h('div', this.$slots.default?.());
38
+ },
39
+ });
40
+
41
+ const getBoxActionIds = (wrapper: ReturnType<typeof mount>) => getActionIds(wrapper, PdkConceptBoxWrapperStub);
42
+
43
+ describe('ShipmentOptionsBox', () => {
44
+ beforeEach(() => {
45
+ doComponentTestSetup();
46
+ config.global.stubs.PdkConceptBoxWrapper = PdkConceptBoxWrapperStub as any;
47
+ });
48
+
49
+ afterEach(() => {
50
+ doComponentTestTeardown();
51
+ });
52
+
53
+ it('shows update, export-to-shipments, print, and export-print in Shipments mode', () => {
54
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.Shipments));
55
+ mockOrderData(mockedUseOrderData);
56
+ const wrapper = mount(ShipmentOptionsBox);
57
+ const ids = getBoxActionIds(wrapper);
58
+
59
+ expect(ids).toContain(AdminAction.OrdersUpdate);
60
+ expect(ids).toContain(AdminAction.OrdersExport);
61
+ expect(ids).toContain(AdminAction.OrdersPrint);
62
+ expect(ids).toContain(AdminAction.OrdersExportPrint);
63
+ expect(ids).not.toContain(ORDER_VIEW_IN_BACKOFFICE_ID);
64
+ });
65
+
66
+ it('shows update and export in OrderV1 mode (not exported)', () => {
67
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.OrderV1));
68
+ mockOrderData(mockedUseOrderData);
69
+ const wrapper = mount(ShipmentOptionsBox);
70
+ const ids = getBoxActionIds(wrapper);
71
+
72
+ expect(ids).toContain(AdminAction.OrdersUpdate);
73
+ expect(ids).toContain(AdminAction.OrdersExport);
74
+ expect(ids).not.toContain(AdminAction.OrdersPrint);
75
+ expect(ids).not.toContain(AdminAction.OrdersExportPrint);
76
+ });
77
+
78
+ it('shows backoffice link in OrderV1 mode when exported', () => {
79
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.OrderV1));
80
+ mockOrderData(mockedUseOrderData, {exported: true});
81
+ const wrapper = mount(ShipmentOptionsBox);
82
+ const ids = getBoxActionIds(wrapper);
83
+
84
+ expect(ids).toEqual([ORDER_VIEW_IN_BACKOFFICE_ID]);
85
+ });
86
+
87
+ it('shows update plus the shipment-export action set in OrderV2 mode (hybrid)', () => {
88
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.OrderV2));
89
+ mockOrderData(mockedUseOrderData);
90
+ const wrapper = mount(ShipmentOptionsBox);
91
+ const ids = getBoxActionIds(wrapper);
92
+
93
+ expect(ids).toContain(AdminAction.OrdersUpdate);
94
+ expect(ids).toContain(AdminAction.OrdersExport);
95
+ expect(ids).toContain(AdminAction.OrdersPrint);
96
+ expect(ids).toContain(AdminAction.OrdersExportPrint);
97
+ });
98
+
99
+ it('does not show the V1 backoffice link in OrderV2 mode, even if order has exported flag', () => {
100
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.OrderV2));
101
+ mockOrderData(mockedUseOrderData, {exported: true});
102
+ const wrapper = mount(ShipmentOptionsBox);
103
+ const ids = getBoxActionIds(wrapper);
104
+
105
+ expect(ids).not.toContain(ORDER_VIEW_IN_BACKOFFICE_ID);
106
+ // Hybrid actions still appear regardless of the exported flag.
107
+ expect(ids).toContain(AdminAction.OrdersExport);
108
+ });
109
+ });
@@ -23,8 +23,7 @@
23
23
 
24
24
  <PdkRow>
25
25
  <PdkCol>
26
- <NotificationContainer
27
- :category="NotificationCategory.Action" />
26
+ <NotificationContainer :category="NotificationCategory.Action" />
28
27
  </PdkCol>
29
28
  </PdkRow>
30
29
  </template>
@@ -35,40 +34,25 @@
35
34
  import {computed, toValue} from 'vue';
36
35
  import {ShipmentOptionsForm} from '../common';
37
36
  import {instantiateActions} from '../../services';
38
- import {AdminIcon, NotificationCategory} from '../../data';
39
- import {useLanguage, useOrderData, usePluginSettings} from '../../composables';
40
- import {
41
- orderExportAction,
42
- orderExportToShipmentsAction,
43
- ordersExportPrintShipmentsAction,
44
- ordersPrintAction,
45
- ordersUpdateAction,
46
- orderViewInBackofficeAction,
47
- } from '../../actions';
37
+ import {AdminIcon, NotificationCategory, OrderMode} from '../../data';
38
+ import {useLanguage, useOrderData, useOrderMode} from '../../composables';
48
39
  import {NotificationContainer} from '../../components';
40
+ import {BOX_MODE_ACTIONS, ordersUpdateAction, orderViewInBackofficeAction} from '../../actions';
49
41
 
50
42
  const {order: data, loading} = useOrderData();
51
43
 
52
44
  const {translate} = useLanguage();
53
- const pluginSettings = usePluginSettings();
45
+ const orderMode = useOrderMode();
54
46
 
55
- const {orderMode} = pluginSettings.order;
56
-
57
- const isExported = computed(() => pluginSettings.order.orderMode && toValue(data)?.exported);
47
+ const isExported = computed(() => orderMode.value === OrderMode.OrderV1 && toValue(data)?.exported);
58
48
 
59
49
  const actions = computed(() => {
60
50
  if (isExported.value) {
61
51
  return instantiateActions(orderViewInBackofficeAction);
62
52
  }
63
53
 
64
- return instantiateActions(
65
- [
66
- ordersUpdateAction,
67
- ...(orderMode
68
- ? [orderExportAction]
69
- : [orderExportToShipmentsAction, ordersPrintAction, ordersExportPrintShipmentsAction]),
70
- ],
71
- {orderIds: toValue(data)?.externalIdentifier},
72
- );
54
+ return instantiateActions([ordersUpdateAction, ...BOX_MODE_ACTIONS[orderMode.value]], {
55
+ orderIds: toValue(data)?.externalIdentifier,
56
+ });
73
57
  });
74
58
  </script>
@@ -0,0 +1,125 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import {computed, defineComponent, h} from 'vue';
4
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
5
+ import {config, mount} from '@vue/test-utils';
6
+ import {AdminAction, OrderMode} from '../../data';
7
+ import {useOrderMode} from '../../composables/context/useOrderMode';
8
+ import {useOrderData} from '../../composables/orders/useOrderData';
9
+ import {ORDER_VIEW_IN_BACKOFFICE_ID} from '../../actions';
10
+ import {mockOrderData} from '../../__tests__/utils/mockOrderData';
11
+ import {doComponentTestSetup, doComponentTestTeardown} from '../../__tests__';
12
+ import OrderModeOrderListItem from './OrderModeOrderListItem.vue';
13
+
14
+ vi.mock('../../composables/orders/useOrderData', () => ({
15
+ useOrderData: vi.fn(),
16
+ }));
17
+
18
+ vi.mock('../../composables/context/useOrderMode', () => ({
19
+ useOrderMode: vi.fn(),
20
+ }));
21
+
22
+ const mockedUseOrderData = vi.mocked(useOrderData);
23
+ const mockedUseOrderMode = vi.mocked(useOrderMode);
24
+
25
+ // eslint-disable-next-line @typescript-eslint/naming-convention
26
+ const ShipmentLabelStub = defineComponent({
27
+ name: 'ShipmentLabel',
28
+ props: {shipmentId: Number},
29
+ render() {
30
+ return h('div', {class: 'shipment-label'}, `Shipment #${this.shipmentId}`);
31
+ },
32
+ });
33
+
34
+ // eslint-disable-next-line @typescript-eslint/naming-convention
35
+ const PdkLinkStub = defineComponent({
36
+ name: 'PdkLink',
37
+ props: {action: {type: Object, required: true}},
38
+ render() {
39
+ return h('a', {'data-action-id': (this.action as {id?: string})?.id}, 'PdkLink');
40
+ },
41
+ });
42
+
43
+ describe('OrderModeOrderListItem', () => {
44
+ beforeEach(() => {
45
+ doComponentTestSetup();
46
+ config.global.stubs.ShipmentLabel = ShipmentLabelStub as any;
47
+ config.global.stubs.PdkLink = PdkLinkStub as any;
48
+ });
49
+
50
+ afterEach(() => {
51
+ doComponentTestTeardown();
52
+ });
53
+
54
+ describe('OrderV1 mode', () => {
55
+ beforeEach(() => {
56
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.OrderV1));
57
+ });
58
+
59
+ it('shows edit and export actions when the order is not exported', () => {
60
+ mockOrderData(mockedUseOrderData);
61
+ const wrapper = mount(OrderModeOrderListItem);
62
+ const html = wrapper.html();
63
+
64
+ expect(html).toContain(AdminAction.OrdersEdit);
65
+ expect(html).toContain(AdminAction.OrdersExport);
66
+ expect(html).not.toContain(ORDER_VIEW_IN_BACKOFFICE_ID);
67
+ });
68
+
69
+ it('shows only the backoffice link when the order is exported', () => {
70
+ mockOrderData(mockedUseOrderData, {exported: true});
71
+ const wrapper = mount(OrderModeOrderListItem);
72
+ const pdkLink = wrapper.findComponent(PdkLinkStub);
73
+ const html = wrapper.html();
74
+
75
+ expect(pdkLink.exists()).toBe(true);
76
+ expect(html).toContain(`data-action-id="${ORDER_VIEW_IN_BACKOFFICE_ID}"`);
77
+ expect(html).not.toContain(AdminAction.OrdersEdit);
78
+ expect(html).not.toContain(AdminAction.OrdersExport);
79
+ });
80
+ });
81
+
82
+ describe('OrderV2 mode', () => {
83
+ beforeEach(() => {
84
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.OrderV2));
85
+ });
86
+
87
+ it('shows the edit action and the shipment-export action (hybrid)', () => {
88
+ mockOrderData(mockedUseOrderData);
89
+ const wrapper = mount(OrderModeOrderListItem);
90
+ const html = wrapper.html();
91
+
92
+ expect(html).toContain(AdminAction.OrdersEdit);
93
+ expect(html).toContain(AdminAction.OrdersExport);
94
+ });
95
+
96
+ it('does not show the V1 backoffice link, even when the order is exported', () => {
97
+ mockOrderData(mockedUseOrderData, {exported: true});
98
+ const wrapper = mount(OrderModeOrderListItem);
99
+ const html = wrapper.html();
100
+
101
+ expect(html).not.toContain(ORDER_VIEW_IN_BACKOFFICE_ID);
102
+ });
103
+ });
104
+
105
+ describe('structural rendering', () => {
106
+ beforeEach(() => {
107
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.OrderV2));
108
+ });
109
+
110
+ it('shows DeliveryOptionsExcerpt when there are no shipments', () => {
111
+ mockOrderData(mockedUseOrderData, {shipments: []});
112
+ const wrapper = mount(OrderModeOrderListItem);
113
+
114
+ expect(wrapper.findComponent({name: 'DeliveryOptionsExcerpt'}).exists()).toBe(true);
115
+ });
116
+
117
+ it('renders a ShipmentLabel for each shipment', () => {
118
+ mockOrderData(mockedUseOrderData, {shipments: [{id: 1}, {id: 2}, {id: 3}]});
119
+ const wrapper = mount(OrderModeOrderListItem);
120
+ const labels = wrapper.findAllComponents(ShipmentLabelStub);
121
+
122
+ expect(labels).toHaveLength(3);
123
+ });
124
+ });
125
+ });
@@ -7,7 +7,7 @@
7
7
  :shipment-id="shipment.id" />
8
8
 
9
9
  <PdkButtonGroup v-test="[$.type.__name, order?.externalIdentifier]">
10
- <template v-if="order?.exported">
10
+ <template v-if="orderMode === OrderMode.OrderV1 && order?.exported">
11
11
  <PdkLink :action="showExportedOrderAction" />
12
12
  </template>
13
13
 
@@ -27,11 +27,13 @@ import {computed, toValue} from 'vue';
27
27
  import {Size} from '@myparcel-dev/pdk-common';
28
28
  import {ActionButton, DeliveryOptionsExcerpt} from '../common';
29
29
  import {instantiateAction, instantiateActions} from '../../services';
30
- import {useOrderData} from '../../composables';
31
- import {orderExportAction, ordersEditAction, orderViewInBackofficeAction} from '../../actions';
30
+ import {useOrderData, useOrderMode} from '../../composables';
31
+ import {OrderMode} from '../../data';
32
+ import {LIST_ITEM_MODE_ACTIONS, orderViewInBackofficeAction} from '../../actions';
32
33
  import ShipmentLabel from './ShipmentLabel.vue';
33
34
 
34
35
  const {query, order} = useOrderData();
36
+ const orderMode = useOrderMode();
35
37
 
36
38
  const data = computed(() => toValue(query.data));
37
39
 
@@ -39,7 +41,9 @@ const shipments = computed(() => toValue(data)?.shipments ?? []);
39
41
 
40
42
  const showExportedOrderAction = instantiateAction(orderViewInBackofficeAction);
41
43
 
42
- const orderActions = instantiateActions([ordersEditAction, orderExportAction], {
43
- orderIds: order.value?.externalIdentifier,
44
- });
44
+ const orderActions = computed(() =>
45
+ instantiateActions(LIST_ITEM_MODE_ACTIONS[orderMode.value], {
46
+ orderIds: order.value?.externalIdentifier,
47
+ }),
48
+ );
45
49
  </script>
@@ -14,12 +14,13 @@ import {AdminContextKey} from '@myparcel-dev/pdk-common';
14
14
  import {TabNavigation} from '../common';
15
15
  import {type TabDefinition} from '../../types';
16
16
  import {createActionContext} from '../../services';
17
- import {createPluginSettingsTabs} from '../../forms';
17
+ import {createPluginSettingsTabs, filterPluginSettingsView} from '../../forms';
18
18
  import {type AdminAction} from '../../data';
19
- import {useAdminConfig, useStoreContextQuery} from '../../composables';
19
+ import {useAdminConfig, useOrderMode, useStoreContextQuery} from '../../composables';
20
20
  import {pluginSettingsUpdateAction} from '../../actions';
21
21
 
22
22
  const config = useAdminConfig();
23
+ const orderMode = useOrderMode();
23
24
 
24
25
  const dynamicContextQuery = useStoreContextQuery();
25
26
  const pluginSettingsContextQuery = useStoreContextQuery(AdminContextKey.PluginSettingsView);
@@ -37,14 +38,16 @@ watch(
37
38
  return;
38
39
  }
39
40
 
40
- const pluginSettingsView = toValue(pluginSettingsContextQuery.data);
41
+ const rawPluginSettingsView = toValue(pluginSettingsContextQuery.data);
41
42
  const dynamicContext = toValue(dynamicContextQuery.data);
42
43
 
43
- if (!pluginSettingsView || !dynamicContext?.pluginSettings) {
44
+ if (!rawPluginSettingsView || !dynamicContext?.pluginSettings) {
44
45
  tabs.value = [];
45
46
  return;
46
47
  }
47
48
 
49
+ const pluginSettingsView = filterPluginSettingsView(rawPluginSettingsView, orderMode.value);
50
+
48
51
  tabs.value = createPluginSettingsTabs(pluginSettingsView, {
49
52
  pluginSettings: dynamicContext.pluginSettings,
50
53
  actionContext,
@@ -8,14 +8,14 @@
8
8
 
9
9
  <script lang="ts" setup>
10
10
  import {computed, toRefs, toValue} from 'vue';
11
- import {type Carrier} from '@myparcel-dev/pdk-common';
11
+ import {type CarrierModel} from '@myparcel-dev/pdk-common';
12
12
  import {useFetchCarrier} from '../../sdk';
13
13
  import CarrierLogo from './CarrierLogo.vue';
14
14
 
15
- const props = defineProps<{carrier: Carrier.ModelCarrier}>();
15
+ const props = defineProps<{carrier: CarrierModel}>();
16
16
  const propRefs = toRefs(props);
17
17
 
18
- const carriersQuery = useFetchCarrier(propRefs.carrier.value.name);
18
+ const carriersQuery = useFetchCarrier(propRefs.carrier.value.carrier);
19
19
 
20
20
  const carrierData = computed(() => toValue(carriersQuery.data));
21
21
  </script>
@@ -0,0 +1,79 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import {computed, defineComponent, h} from 'vue';
4
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
5
+ import {config, mount} from '@vue/test-utils';
6
+ import {AdminAction, OrderMode} from '../../data';
7
+ import {useOrderMode} from '../../composables/context/useOrderMode';
8
+ import {getActionIds} from '../../__tests__/utils/getActionIds';
9
+ import {doComponentTestSetup, doComponentTestTeardown} from '../../__tests__';
10
+ import ShipmentOptionsModal from './ShipmentOptionsModal.vue';
11
+
12
+ vi.mock('../../composables/context/useOrderMode', () => ({
13
+ useOrderMode: vi.fn(),
14
+ }));
15
+
16
+ vi.mock('../../composables/orders/useOrdersData', () => ({
17
+ useOrdersData: vi.fn(() => []),
18
+ }));
19
+
20
+ const mockedUseOrderMode = vi.mocked(useOrderMode);
21
+
22
+ const MODAL_CLOSE_ID = 'modal_close';
23
+
24
+ // eslint-disable-next-line @typescript-eslint/naming-convention
25
+ const PdkModalStub = defineComponent({
26
+ name: 'PdkModal',
27
+ props: {actions: Array, modalKey: String, title: String},
28
+ render() {
29
+ return h('div');
30
+ },
31
+ });
32
+
33
+ const getModalActionIds = (wrapper: ReturnType<typeof mount>) => getActionIds(wrapper, PdkModalStub);
34
+
35
+ describe('ShipmentOptionsModal', () => {
36
+ beforeEach(() => {
37
+ doComponentTestSetup();
38
+ config.global.stubs.PdkModal = PdkModalStub as any;
39
+ });
40
+
41
+ afterEach(() => {
42
+ doComponentTestTeardown();
43
+ });
44
+
45
+ it('shows close, update, export-to-shipments, and export-print in Shipments mode', () => {
46
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.Shipments));
47
+ const wrapper = mount(ShipmentOptionsModal);
48
+ const ids = getModalActionIds(wrapper);
49
+
50
+ expect(ids).toContain(MODAL_CLOSE_ID);
51
+ expect(ids).toContain(AdminAction.OrdersUpdate);
52
+ expect(ids).toContain(AdminAction.OrdersExport);
53
+ expect(ids).toContain(AdminAction.OrdersExportPrint);
54
+ });
55
+
56
+ it('shows close, update, and export in OrderV1 mode', () => {
57
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.OrderV1));
58
+ const wrapper = mount(ShipmentOptionsModal);
59
+ const ids = getModalActionIds(wrapper);
60
+
61
+ expect(ids).toContain(MODAL_CLOSE_ID);
62
+ expect(ids).toContain(AdminAction.OrdersUpdate);
63
+ expect(ids).toContain(AdminAction.OrdersExport);
64
+ expect(ids).not.toContain(AdminAction.OrdersExportPrint);
65
+ });
66
+
67
+ it('shows close, update, export and export-print in OrderV2 mode (hybrid)', () => {
68
+ mockedUseOrderMode.mockReturnValue(computed(() => OrderMode.OrderV2));
69
+ const wrapper = mount(ShipmentOptionsModal);
70
+ const ids = getModalActionIds(wrapper);
71
+
72
+ expect(ids).toContain(MODAL_CLOSE_ID);
73
+ expect(ids).toContain(AdminAction.OrdersUpdate);
74
+ expect(ids).toContain(AdminAction.OrdersExport);
75
+ expect(ids).toContain(AdminAction.OrdersExportPrint);
76
+ // Standalone print is intentionally absent from the modal in both Shipments and V2.
77
+ expect(ids).not.toContain(AdminAction.OrdersPrint);
78
+ });
79
+ });
@@ -15,14 +15,8 @@
15
15
  import {computed, defineAsyncComponent, toValue} from 'vue';
16
16
  import {instantiateActions} from '../../services';
17
17
  import {AdminModalKey} from '../../data';
18
- import {useOrdersData, usePluginSettings} from '../../composables';
19
- import {
20
- modalCloseAction,
21
- orderExportAction,
22
- orderExportToShipmentsAction,
23
- ordersExportPrintShipmentsAction,
24
- ordersUpdateAction,
25
- } from '../../actions';
18
+ import {useOrderMode, useOrdersData} from '../../composables';
19
+ import {modalCloseAction, MODAL_MODE_ACTIONS, ordersUpdateAction} from '../../actions';
26
20
 
27
21
  /**
28
22
  * Shipment options modal. Opened by clicking the "Create" button in the "Labels" column in the orders list.
@@ -31,9 +25,7 @@ import {
31
25
  // eslint-disable-next-line @typescript-eslint/naming-convention
32
26
  const ShipmentOptionsModalForm = defineAsyncComponent(() => import('./ShipmentOptionsModalForm.vue'));
33
27
 
34
- const pluginSettings = usePluginSettings();
35
-
36
- const {orderMode} = pluginSettings.order;
28
+ const orderMode = useOrderMode();
37
29
 
38
30
  const ordersData = computed(() => useOrdersData());
39
31
 
@@ -42,15 +34,6 @@ const orderIds = computed(() => toValue(ordersData).map((data) => toValue(data.o
42
34
  const actions = computed(() => {
43
35
  const disabled = toValue(ordersData).some((data) => toValue(data.query.isLoading));
44
36
 
45
- const actions = [
46
- modalCloseAction,
47
- ordersUpdateAction,
48
- ...(orderMode ? [orderExportAction] : [orderExportToShipmentsAction, ordersExportPrintShipmentsAction]),
49
- ];
50
-
51
- // TODO: figure out why this throws an error in build
52
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
53
- // @ts-ignore
54
- return instantiateActions(actions, {disabled});
37
+ return instantiateActions([modalCloseAction, ordersUpdateAction, ...MODAL_MODE_ACTIONS[orderMode.value]], {disabled});
55
38
  });
56
39
  </script>
@@ -22,8 +22,8 @@ export const useToggleInputContext: UseToggleInputContext = (props, emit) => {
22
22
  });
23
23
 
24
24
  // When the model is changed manually, like from another element, the toggle is updated
25
- watch(model, (model) => {
26
- toggleModel.value = triStateToBoolean(model);
25
+ watch(model, (modelVal) => {
26
+ toggleModel.value = triStateToBoolean(modelVal);
27
27
  });
28
28
 
29
29
  return {
@@ -99,12 +99,12 @@ export const useTriStateInputContext: UseTriStateInputContext = (props, emit) =>
99
99
  });
100
100
 
101
101
  // When the model is changed manually, like from another element, the toggle is updated if inherit is disabled.
102
- watch(model, (model) => {
102
+ watch(model, (modelVal) => {
103
103
  if (inheritModel.value) {
104
104
  return;
105
105
  }
106
106
 
107
- toggleModel.value = triStateToBoolean(model);
107
+ toggleModel.value = triStateToBoolean(modelVal);
108
108
  });
109
109
 
110
110
  return {
@@ -4,4 +4,5 @@ export * from './useGlobalContext';
4
4
  export * from './useInstanceContext';
5
5
  export * from './useModalContext';
6
6
  export * from './useModalOrder';
7
+ export * from './useOrderMode';
7
8
  export * from './usePluginSettings';
@@ -0,0 +1,75 @@
1
+ import {describe, expect, it, vi} from 'vitest';
2
+ import {OrderMode, SubscriptionFeature} from '../../data';
3
+ import {useOrderMode} from './useOrderMode';
4
+ import {useContext} from './useContext';
5
+
6
+ vi.mock('./useContext', () => ({
7
+ useContext: vi.fn(),
8
+ }));
9
+
10
+ const mockedUseContext = vi.mocked(useContext);
11
+
12
+ const mockAccount = (subscriptionFeatures: string[], effectiveOrderMode?: unknown) => {
13
+ mockedUseContext.mockReturnValue({account: {subscriptionFeatures, effectiveOrderMode}} as any);
14
+ };
15
+
16
+ describe('useOrderMode', () => {
17
+ it('returns OrderV2 when both ORDER_MANAGEMENT and LEGACY_ORDER_MANAGEMENT are present', () => {
18
+ mockAccount([SubscriptionFeature.OrderManagement, SubscriptionFeature.LegacyOrderManagement]);
19
+ expect(useOrderMode().value).toBe(OrderMode.OrderV2);
20
+ });
21
+
22
+ it('returns OrderV2 when only ORDER_MANAGEMENT is present', () => {
23
+ mockAccount([SubscriptionFeature.OrderManagement]);
24
+ expect(useOrderMode().value).toBe(OrderMode.OrderV2);
25
+ });
26
+
27
+ it('returns OrderV1 when only LEGACY_ORDER_MANAGEMENT is present', () => {
28
+ mockAccount([SubscriptionFeature.LegacyOrderManagement]);
29
+ expect(useOrderMode().value).toBe(OrderMode.OrderV1);
30
+ });
31
+
32
+ it('returns Shipments when no order management features are present', () => {
33
+ mockAccount([]);
34
+ expect(useOrderMode().value).toBe(OrderMode.Shipments);
35
+ });
36
+
37
+ it('returns Shipments when subscriptionFeatures contains unrelated features', () => {
38
+ mockAccount(['SOME_OTHER_FEATURE', 'ANOTHER_FEATURE']);
39
+ expect(useOrderMode().value).toBe(OrderMode.Shipments);
40
+ });
41
+
42
+ it('returns Shipments when subscriptionFeatures is undefined', () => {
43
+ mockedUseContext.mockReturnValue({account: {}} as any);
44
+ expect(useOrderMode().value).toBe(OrderMode.Shipments);
45
+ });
46
+
47
+ it('returns Shipments when account is undefined', () => {
48
+ mockedUseContext.mockReturnValue({} as any);
49
+ expect(useOrderMode().value).toBe(OrderMode.Shipments);
50
+ });
51
+
52
+ it('prefers a PHP-supplied effectiveOrderMode of 2 (V2) over the feature derivation', () => {
53
+ // Feature would resolve to Shipments here; effectiveOrderMode overrides it.
54
+ mockAccount([], 2);
55
+ expect(useOrderMode().value).toBe(OrderMode.OrderV2);
56
+ });
57
+
58
+ it('prefers a PHP-supplied effectiveOrderMode of 1 (V1) over the feature derivation', () => {
59
+ // Feature would resolve to OrderV2; effectiveOrderMode overrides to V1.
60
+ mockAccount([SubscriptionFeature.OrderManagement], 1);
61
+ expect(useOrderMode().value).toBe(OrderMode.OrderV1);
62
+ });
63
+
64
+ it('prefers a PHP-supplied effectiveOrderMode of 0 (Shipments) over the feature derivation', () => {
65
+ // Feature would resolve to OrderV2; effectiveOrderMode downgrades to Shipments
66
+ // (the shape INT-1590 will produce).
67
+ mockAccount([SubscriptionFeature.OrderManagement], 0);
68
+ expect(useOrderMode().value).toBe(OrderMode.Shipments);
69
+ });
70
+
71
+ it('falls back to feature derivation when effectiveOrderMode is not a known numeric mode', () => {
72
+ mockAccount([SubscriptionFeature.LegacyOrderManagement], 'unexpected-string');
73
+ expect(useOrderMode().value).toBe(OrderMode.OrderV1);
74
+ });
75
+ });
@@ -0,0 +1,26 @@
1
+ import {type ComputedRef, computed} from 'vue';
2
+ import {type OrderMode, orderModeFromContextValue, resolveOrderMode} from '../../data';
3
+ import {useContext} from './useContext';
4
+
5
+ /**
6
+ * Resolve the order mode the admin UI should adapt to.
7
+ *
8
+ * Prefers the pre-computed `effectiveOrderMode` emitted by the PDK admin context
9
+ * (so any business rules applied on the PHP side — e.g. INT-1590's
10
+ * sales-channel-conditional downgrade — naturally flow through). Falls back to
11
+ * resolving from the raw IAM subscription features when the context does not
12
+ * (yet) carry that value, which keeps older PDK versions working.
13
+ */
14
+ export const useOrderMode = (): ComputedRef<OrderMode> => {
15
+ const context = useContext();
16
+
17
+ return computed(() => {
18
+ // `effectiveOrderMode` is forward-looking — the PHP context generator may not
19
+ // emit it yet. Cast locally rather than augmenting the generated type.
20
+ const effective = orderModeFromContextValue(
21
+ (context.account as {effectiveOrderMode?: unknown} | undefined)?.effectiveOrderMode,
22
+ );
23
+
24
+ return effective ?? resolveOrderMode(context.account?.subscriptionFeatures ?? []);
25
+ });
26
+ };
@@ -21,8 +21,8 @@ describe('useLanguage', () => {
21
21
  beforeAll(() => {
22
22
  mockDefaultTranslations.mockReturnValue({
23
23
  my_translation: 'My translation',
24
- my_translation_with_replacement: 'My translation with replacement: {platform.backofficeUrl}',
25
- translation_with_custom_replacer: '{platform.name} said {word}',
24
+ my_translation_with_replacement: 'My translation with replacement: {proposition.backofficeUrl}',
25
+ translation_with_custom_replacer: '{proposition.name} said {word}',
26
26
  });
27
27
  });
28
28