From c14f0cc1c402193ca97473a3f4286f26c8c13a8d Mon Sep 17 00:00:00 2001 From: Jeppe Bundgaard Date: Tue, 14 Apr 2026 10:09:10 +0200 Subject: [PATCH] Add utilities and tests for mobile order management and Subuser navigation: - Introduced `mobileOrderCompletion.js` for mobile order metadata handling with safety seal syncing, metadata persistence, and booking linking utilities. - Added tests for Subuser navigation menu (`navigation-menu-items-user-subusers.spec.js`) to validate links and permission gating. - Created unit and end-to-end tests for Subuser grant permissions and management, including permission nodes and invites (`subuser-grant-permission-nodes.spec.js`, `subuser-management.spec.ts`). - Implemented `SubuserCompleteRegistrationPage.vue` with validation, token handling, and registration flow for Subuser setup. - Enhanced Playwright e2e tests for Subuser features (`subuserCompleteRegistration.spec.ts`). --- src/components/displays/Subusers.vue | 50 +- .../department/pos/PosOrderItemsCurrent.vue | 5 + .../pos/order/POSOrderCustomerWishes.vue | 23 +- .../pos/steps/PosDepartmentStep1.vue | 73 ++- .../PosDesktopOrderBookingSelectorModal.vue | 427 ++++++++++++++-- .../steps/mobile/PosDepartmentStepMobile2.vue | 99 +++- .../steps/mobile/PosDepartmentStepMobile3.vue | 3 +- .../PosDepartmentStepMobile2Product.vue | 4 +- .../PosDepartmentStepMobileButtonNextStep.vue | 109 +--- ...DepartmentStepMobileFixedBottomControl.vue | 2 +- .../objects/PosDepartmentStepMobileFlow.vue | 57 ++- .../mobile/objects/mobileOrderCompletion.js | 101 ++++ .../pos/utils/orderBookingDisplay.js | 100 ++++ .../department/pos/utils/washCertificate.js | 61 +++ .../SuperUserDashboard/SubusersPagination.vue | 76 +-- .../selectors/SubuserGrantPermissionNodes.vue | 148 +++--- .../superuser/tables/SubusersTable.vue | 300 +++++++++-- .../department/pos/SelectVehicleFormPOS.vue | 67 ++- .../items/NavigationMenuItemsUser.vue | 2 +- src/components/session/token/SessionUser.vue | 2 + .../SessionUser/Objects/OrderBookings.vue | 9 +- .../token/SessionUser/Objects/Orders.vue | 16 + .../SessionUser/Objects/SubuserGrants.vue | 31 +- .../token/SessionUser/Objects/Subusers.vue | 237 +++++++++ src/components/shop/POSDepartmentProcess.vue | 108 ++-- src/router.js | 9 +- .../TimeBookingsElementSelectDateTime.vue | 23 +- .../superUserDashboard/Subusers.vue | 7 +- .../userDashboard/bookings/MyBookingsBook.vue | 297 ++--------- .../elements/UserBookingDateTimeSelector.vue | 475 ++++++++++++++++++ .../displays/steps/NewBookingStep5.vue | 2 +- .../userDashboard/profile/UserProfile.vue | 23 +- .../auth/SubuserCompleteRegistrationPage.vue | 399 +++++++++++++++ tests/e2e/admin-pos-orders.spec.ts | 119 +++++ tests/e2e/pos-flow.spec.js | 364 ++++++++++++-- tests/e2e/pos-mobile-order-flow.spec.js | 53 +- tests/e2e/subuser-management.spec.ts | 469 +++++++++++++++++ tests/e2e/subuserCompleteRegistration.spec.ts | 79 +++ tests/e2e/subuserProfileSecurity.spec.ts | 39 +- tests/e2e/support/bookingFlow.ts | 62 ++- tests/e2e/support/mobilePos.js | 67 +++ tests/e2e/support/network.js | 71 +++ tests/e2e/userProfileSecurity.spec.ts | 29 ++ ...avigation-menu-items-user-subusers.spec.js | 25 + .../subuser-grant-permission-nodes.spec.js | 71 +++ .../user-booking-date-time-selector.spec.js | 139 +++++ 46 files changed, 4258 insertions(+), 674 deletions(-) create mode 100644 src/components/displays/department/pos/steps/mobile/objects/mobileOrderCompletion.js create mode 100644 src/components/displays/department/pos/utils/washCertificate.js create mode 100644 src/components/session/token/SessionUser/Objects/Subusers.vue create mode 100644 src/views/dashboards/userDashboard/bookings/displays/elements/UserBookingDateTimeSelector.vue create mode 100644 src/views/pages/auth/SubuserCompleteRegistrationPage.vue create mode 100644 tests/e2e/subuser-management.spec.ts create mode 100644 tests/e2e/subuserCompleteRegistration.spec.ts create mode 100644 tests/unit/navigation-menu-items-user-subusers.spec.js create mode 100644 tests/unit/subuser-grant-permission-nodes.spec.js create mode 100644 tests/unit/user-booking-date-time-selector.spec.js diff --git a/src/components/displays/Subusers.vue b/src/components/displays/Subusers.vue index d520e415..6619c65d 100644 --- a/src/components/displays/Subusers.vue +++ b/src/components/displays/Subusers.vue @@ -1,19 +1,61 @@ diff --git a/src/components/displays/department/pos/PosOrderItemsCurrent.vue b/src/components/displays/department/pos/PosOrderItemsCurrent.vue index 009ae8bb..bcd91d53 100644 --- a/src/components/displays/department/pos/PosOrderItemsCurrent.vue +++ b/src/components/displays/department/pos/PosOrderItemsCurrent.vue @@ -40,11 +40,13 @@ import { reference, order_notes, order_po, + order_safety_seal, } from "@/components/shop/POSDepartmentProcess.vue"; import SpanSkeleton from "@/components/displays/skeletons/SpanSkeleton.vue"; import PosOrderLicensePlates from "@/components/displays/department/pos/order/PosOrderLicensePlates.vue"; import POSOrderCustomerWishes from "@/components/displays/department/pos/order/POSOrderCustomerWishes.vue"; import POSOrderNote from "@/components/displays/department/pos/order/POSOrderNote.vue"; +import { doesOrderContainWashCertificateProduct } from "@/components/displays/department/pos/utils/washCertificate.js"; const { t } = useI18n(); const slots = useSlots(); @@ -84,6 +86,7 @@ const selectedOrderItem = ref(null); const isOrderItemEditModalOpen = ref(false); const referenceRequired = computed(() => customerRequiresReferenceNumber()); const poRequired = computed(() => customerUsesPONumbers()); +const showSafetySealField = computed(() => doesOrderContainWashCertificateProduct(visibleOrderItems.value)); const buildVisibleOrderItems = (sourceOrderItems = []) => { const pendingIds = new Set(pendingRemovedOrderItemIds.value); @@ -895,6 +898,8 @@ const deleteOrderItem = async (orderItemId) => { v-bind:order_id="activeOrderId" v-bind:reference="reference" v-bind:po="order_po" + v-bind:safetySeal="order_safety_seal" + v-bind:showSafetySeal="showSafetySealField" v-bind:referenceRequired="referenceRequired" v-bind:poRequired="poRequired" /> diff --git a/src/components/displays/department/pos/order/POSOrderCustomerWishes.vue b/src/components/displays/department/pos/order/POSOrderCustomerWishes.vue index 837bbeee..bd1bbd18 100644 --- a/src/components/displays/department/pos/order/POSOrderCustomerWishes.vue +++ b/src/components/displays/department/pos/order/POSOrderCustomerWishes.vue @@ -18,6 +18,14 @@ const props = defineProps({ type: String, required: true, }, + safetySeal: { + type: String, + default: "", + }, + showSafetySeal: { + type: Boolean, + default: false, + }, referenceRequired: { type: Boolean, default: false, @@ -121,7 +129,20 @@ const poField = createCustomerWishField({ warningIconClass: "fas fa-exclamation-triangle", }); -const fields = [referenceField, poField]; +const safetySealField = createCustomerWishField({ + key: "safety_seal", + label: computed(() => t("admin.pos.safety_seal")), + source: () => props.safetySeal, + saveValue: (value) => SessionUser.objects.orders.set.safety_seal(props.order_id, value), + testIdBase: "pos-order-customer-wishes-safety-seal", + isRequired: () => false, + warningStateWhenEmpty: null, + warningIconClass: "fas fa-shield-alt", +}); + +const fields = computed(() => { + return props.showSafetySeal ? [referenceField, poField, safetySealField] : [referenceField, poField]; +}); @@ -2073,96 +1936,10 @@ watch(customerNumber, (newVal) => { diff --git a/src/views/dashboards/userDashboard/bookings/displays/steps/NewBookingStep5.vue b/src/views/dashboards/userDashboard/bookings/displays/steps/NewBookingStep5.vue index 5ef01d30..c7086c53 100644 --- a/src/views/dashboards/userDashboard/bookings/displays/steps/NewBookingStep5.vue +++ b/src/views/dashboards/userDashboard/bookings/displays/steps/NewBookingStep5.vue @@ -91,7 +91,7 @@ const showWarning = computed(() => {
{{ $t('time_booking_flow.date_and_time') }}: -
{{ formattedDateTime || $t('time_booking_flow.not_selected') }}
+
{{ formattedDateTime || $t('time_booking_flow.not_selected') }}
{{ $t('time_booking_flow.selected_services') }}: diff --git a/src/views/dashboards/userDashboard/profile/UserProfile.vue b/src/views/dashboards/userDashboard/profile/UserProfile.vue index e8219bf6..67e29ff4 100644 --- a/src/views/dashboards/userDashboard/profile/UserProfile.vue +++ b/src/views/dashboards/userDashboard/profile/UserProfile.vue @@ -637,6 +637,18 @@ const onClickSaveWashCertificateEmail = async () => {
+ +

@@ -750,7 +762,7 @@ const onClickSaveWashCertificateEmail = async () => { >