"Enhance booking and basket flow with improved product selection handling, dynamic basket updates, and refined UI interactions for better usability"
This commit is contained in:
@@ -130,6 +130,10 @@ const loadCategories = async () => {
|
||||
// Get the department id from the route
|
||||
const route = useRoute();
|
||||
const departmentId = route.params.departmentId;
|
||||
// If the department id is not set, return
|
||||
if (departmentId === undefined) {
|
||||
return;
|
||||
}
|
||||
// Get the categories
|
||||
await SessionUser.objects.department_categories.get.single(
|
||||
departmentId
|
||||
|
||||
@@ -326,6 +326,9 @@ export const products_category = ref(null);
|
||||
|
||||
/** Define the product category functions */
|
||||
export const setProductsCategory = (category) => {
|
||||
if (products_category.value === category) {
|
||||
return;
|
||||
}
|
||||
products_category.value = category;
|
||||
};
|
||||
|
||||
|
||||
+12
-10
@@ -95,11 +95,12 @@ const reg2TypeVmodel = computed({
|
||||
<button class="button has-icons-left is-fullwidth default-button" @click="onClickSelectReg1"
|
||||
:class="{ 'is-link': reg1 !== '' }" v-show="!isReg1Input">
|
||||
<span>
|
||||
{{ reg1 === '' ? 'Tilføj trækker' : reg1 }}
|
||||
{{ reg1 === '' ? 'Tilføj registreringsnummer' : reg1 }}
|
||||
</span>
|
||||
<span class="icon is-left">
|
||||
<i class="fas fa-truck-pickup"></i>
|
||||
</span>
|
||||
<i class="fas fa-trailer"></i>
|
||||
<i class="fas fa-truck-pickup"></i>
|
||||
</span>
|
||||
</button>
|
||||
<span>
|
||||
<input class="input is-fullwidth"
|
||||
@@ -114,8 +115,9 @@ const reg2TypeVmodel = computed({
|
||||
/>
|
||||
</span>
|
||||
<span class="icon is-left" v-show="isReg1Input">
|
||||
<i class="fas fa-truck-pickup"></i>
|
||||
</span>
|
||||
<i class="fas fa-trailer"></i>
|
||||
<i class="fas fa-truck-pickup"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="control is-expanded has-icons-left has-icons-right is-fixed-half">
|
||||
<div class="select is-fullwidth has-icons-right has-icons-left">
|
||||
@@ -127,8 +129,8 @@ const reg2TypeVmodel = computed({
|
||||
</select>
|
||||
</div>
|
||||
<span class="icon is-left">
|
||||
<i class="fas fa-car"></i>
|
||||
</span>
|
||||
<i class="fas fa-car"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field has-addons">
|
||||
@@ -136,11 +138,11 @@ const reg2TypeVmodel = computed({
|
||||
<button class="button has-icons-left is-fullwidth default-button" @click="onClickSelectReg2"
|
||||
:class="{ 'is-link': reg2 !== '' }" v-show="!isReg2Input">
|
||||
<span>
|
||||
{{ reg2 === '' ? 'Tilføj trailer' : reg2 }}
|
||||
{{ reg2 === '' ? 'Tilføj registreringsnummer' : reg2 }}
|
||||
</span>
|
||||
<span class="icon is-left">
|
||||
<i class="fas fa-trailer"></i>
|
||||
</span>
|
||||
<i class="fas fa-trailer"></i>
|
||||
</span>
|
||||
</button>
|
||||
<span>
|
||||
<input class="input is-fullwidth"
|
||||
|
||||
@@ -382,6 +382,7 @@ const addItemToBasket = (product: PosProduct, options = {quantity: 1}) => {
|
||||
//console.warn('addItemToBasket triggered.', product, options);
|
||||
const newId = 'a' + Math.random().toString(36).substr(2, 9);
|
||||
// Add the product to the basket
|
||||
|
||||
basket.value.push({
|
||||
...product,
|
||||
...options,
|
||||
@@ -430,6 +431,8 @@ const basketAsOrderItems = computed(() => {
|
||||
const selectedProduct = ref<PosProduct | null>(null);
|
||||
const onSelectProduct = (product: PosProduct) => {
|
||||
console.warn('Selecting product.', product);
|
||||
// Replace the selected product in the basket
|
||||
removeProductInBasketById(selectedProduct.value?.id);
|
||||
if (selectedProduct.value !== product) {
|
||||
selectedProduct.value = product;
|
||||
}
|
||||
@@ -460,8 +463,12 @@ const onUpdateAddons = (addons: PosAddon[]) => {
|
||||
onAddProduct(selectedProduct.value);
|
||||
}
|
||||
|
||||
const removeProductInBasketById = (id: number) => {
|
||||
basket.value = basket.value.filter(product => product.product.id !== id);
|
||||
const removeProductInBasketById = (id: string | number | null | undefined) => {
|
||||
console.warn('Removing product from basket by id.', id);
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
basket.value = basket.value.filter(product => product.id !== id);
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -473,6 +480,7 @@ const removeProductInBasketById = (id: number) => {
|
||||
<div>
|
||||
<p><strong>Basket length</strong>: {{ basket.length }}</p>
|
||||
<p><strong>Basket as order items</strong>: {{ basketAsOrderItems.length }}</p>
|
||||
{{ basket }}
|
||||
<div class="columns">
|
||||
<!-- Choose products -->
|
||||
<div class="column is-8-desktop pl-0 pr-3">
|
||||
@@ -540,59 +548,6 @@ const removeProductInBasketById = (id: number) => {
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-multiline" :class="{ 'is-vcentered': isWalkthroughDisplayed() }">
|
||||
<div class="column is-6">
|
||||
<div class="columns">
|
||||
<div class="column is-12">
|
||||
<!-- Location -->
|
||||
<TimeBookingsElementSelectLocation :department="department" @onClickSelectDepartment="onClickSelectDepartment"/>
|
||||
</div>
|
||||
<!--<div class="column is-6"></div>-->
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-6">
|
||||
<!-- Wash type -->
|
||||
<TimeBookingsElementSelectWashType :wash_type="wash_type as 'general' | 'tankcleaning'" @update:wash_type="wash_type = $event"/>
|
||||
</div>
|
||||
<div class="column is-6">
|
||||
<!-- Date & time -->
|
||||
<TimeBookingsElementSelectDateTime :dateTime="dateTime" @onClickDateTime="onClickDateTime" :isDisabled="!department"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-multiline">
|
||||
<div class="column is-6">
|
||||
<!-- Vehicles -->
|
||||
<TimeBookingsElementSelectVehicles :reg1="reg1" :reg1-type="reg1Type" :reg2="reg2" :reg2-type="reg2Type" :is-reg1-input="isReg1Input"
|
||||
:is-reg2-input="isReg2Input" :available-vehicle-types="orderProductsByOrderPriority(availableVehicleTypes)"
|
||||
:show-price-on-booking-page="hasShowPriceOnBookingPageAttribute()" @update:reg1="reg1 = $event" @update:reg1-type="reg1Type = $event"
|
||||
@update:reg2="reg2 = $event" @update:reg2-type="reg2Type = $event" @update:is-reg1-input="isReg1Input = $event"
|
||||
@update:is-reg2-input="isReg2Input = $event" @setInputFocus="setInputFocus($event)"
|
||||
:label="undefined"
|
||||
/>
|
||||
</div>
|
||||
<div class="column is-6">
|
||||
<!-- Reference -->
|
||||
<TimeBookingsElementSelectReference :reference="reference" @update:reference="reference = $event" @onClickReference="onClickReference"
|
||||
:is-reference-input="isReferenceInput" @update:is-reference-input="isReferenceInput = $event"
|
||||
:label="'Reference og PO Nummer (valgfrit)'"
|
||||
/>
|
||||
<!-- PO Number -->
|
||||
<TimeBookingsElementSelectPoNumber :poNumber="poNumber" @update:poNumber="poNumber = $event"
|
||||
@onClickPoNumber="onClickPoNumber" :is-po-number-input="isPoNumberInput"
|
||||
@update:is-po-number-input="isPoNumberInput = $event"
|
||||
:label="null"
|
||||
/>
|
||||
</div>
|
||||
<div class="column is-12">
|
||||
<TimeBookingsElementSelectNotes :notes="notes" @update:notes="notes = $event" @onClickNotes="onClickNotes"
|
||||
:is-notes-input="isNotesInput" @update:is-notes-input="isNotesInput = $event"
|
||||
:label="(notes !== '' || isNotesInput) ? 'Note (valgfrit)' : null"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="column is-6">
|
||||
<div class="columns">
|
||||
<!-- If there's no department selected, show the full width backdrop stating this is required. -->
|
||||
@@ -627,13 +582,13 @@ const removeProductInBasketById = (id: number) => {
|
||||
<template v-else-if="((reg1 === '' || reg1Type === null) && (reg2 === '' || reg2Type === null)) || forceDisplayRightWalkthrough">
|
||||
<div class="column is-12">
|
||||
<div @click="interactingWithRightWalkthrough(true)">
|
||||
<div class="has-text-centered" style="height: 200px; display: flex; flex-direction: column; justify-content: center; align-items: center;">
|
||||
<div class="has-text-centered" style="height: 200px; display: flex; flex-direction: column; justify-content: center; align-items: center;">
|
||||
<span class="icon is-large">
|
||||
<i class="fas fa-car-side fa-3x"></i>
|
||||
</span>
|
||||
<p class="is-size-5 mt-2">Vælg venligst et køretøj for at se tilgængelige tillæg</p>
|
||||
</div>
|
||||
<!-- Vehicles selection quick options -->
|
||||
<p class="is-size-5 mt-2">Vælg venligst et køretøj for at se tilgængelige tillæg</p>
|
||||
</div>
|
||||
<!-- Vehicles selection quick options -->
|
||||
<TimeBookingsElementSelectVehicles :label="null"
|
||||
:reg1="reg1" :reg1-type="reg1Type" :reg2="reg2" :reg2-type="reg2Type" :is-reg1-input="isReg1Input"
|
||||
:is-reg2-input="isReg2Input" :available-vehicle-types="orderProductsByOrderPriority(availableVehicleTypes)"
|
||||
@@ -641,10 +596,10 @@ const removeProductInBasketById = (id: number) => {
|
||||
@update:reg2="reg2 = $event" @update:reg2-type="reg2Type = $event" @update:is-reg1-input="isReg1Input = $event"
|
||||
@update:is-reg2-input="isReg2Input = $event" @setInputFocus="setInputFocus($event)"
|
||||
/>
|
||||
<!-- Vehicles suggestions -->
|
||||
<!-- Vehicles suggestions -->
|
||||
<TimeBookingsElementVehicleSuggestions :label="'Er det et af disse køretøjer?'" :input="(reg1 !== '' && !reg1Type) ? reg1 : (reg2 !== '' && !reg2Type) ? reg2 : ''" :availableVehicleTypes="availableVehicleTypes"
|
||||
@update:isAnySuggestionsDisplayed="setAnyVisibleSuggestions($event)"
|
||||
@onSelectVehicle="
|
||||
@onSelectVehicle="
|
||||
(vehicle) => {
|
||||
if (reg1 !== '' && !reg1Type) {
|
||||
reg1 = vehicle.reg;
|
||||
@@ -672,11 +627,11 @@ const removeProductInBasketById = (id: number) => {
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<SelectProductsFormPOS :isCustomerBooking="true"
|
||||
:isAddonsVisible="false"
|
||||
:emitActionsOnly="true"
|
||||
@onAddProduct="onAddProduct"
|
||||
:automaticallySelectCategory="6"/>
|
||||
<SelectProductsFormPOS :isCustomerBooking="true"
|
||||
:isAddonsVisible="false"
|
||||
:emitActionsOnly="true"
|
||||
@onAddProduct="onAddProduct"
|
||||
:automaticallySelectCategory="6"/>
|
||||
</template>
|
||||
<template v-show="false">
|
||||
<div class="column" :class="{ 'is-12': (reg2 === '' || reg2Type === null), 'is-6': (reg2 !== '' || reg2Type !== null) }" v-if="reg1 !== '' && reg1Type !== null && !forceDisplayRightWalkthrough">
|
||||
@@ -698,6 +653,66 @@ const removeProductInBasketById = (id: number) => {
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-6">
|
||||
<div class="columns">
|
||||
<div class="column is-12" v-show="false">
|
||||
<!-- Location -->
|
||||
<TimeBookingsElementSelectLocation :department="department" @onClickSelectDepartment="onClickSelectDepartment"/>
|
||||
</div>
|
||||
<!--<div class="column is-6"></div>-->
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-6">
|
||||
<!-- Wash type -->
|
||||
<TimeBookingsElementSelectWashType :wash_type="wash_type as 'general' | 'tankcleaning'" @update:wash_type="wash_type = $event"/>
|
||||
</div>
|
||||
<div class="column is-6">
|
||||
<!-- Date & time -->
|
||||
<TimeBookingsElementSelectDateTime :dateTime="dateTime" @onClickDateTime="onClickDateTime" :isDisabled="!department"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-multiline">
|
||||
<div class="column is-12">
|
||||
<label class="label">Reference og PO Nummer (valgfrit)</label>
|
||||
</div>
|
||||
<div class="column is-6" v-show="false">
|
||||
<!-- Vehicles -->
|
||||
<TimeBookingsElementSelectVehicles :reg1="reg1" :reg1-type="reg1Type" :reg2="reg2" :reg2-type="reg2Type" :is-reg1-input="isReg1Input"
|
||||
:is-reg2-input="isReg2Input" :available-vehicle-types="orderProductsByOrderPriority(availableVehicleTypes)"
|
||||
:show-price-on-booking-page="hasShowPriceOnBookingPageAttribute()" @update:reg1="reg1 = $event" @update:reg1-type="reg1Type = $event"
|
||||
@update:reg2="reg2 = $event" @update:reg2-type="reg2Type = $event" @update:is-reg1-input="isReg1Input = $event"
|
||||
@update:is-reg2-input="isReg2Input = $event" @setInputFocus="setInputFocus($event)"
|
||||
:label="undefined"
|
||||
/>
|
||||
</div>
|
||||
<div class="column is-6">
|
||||
<!-- Reference -->
|
||||
<TimeBookingsElementSelectReference :reference="reference" @update:reference="reference = $event" @onClickReference="onClickReference"
|
||||
:is-reference-input="isReferenceInput" @update:is-reference-input="isReferenceInput = $event"
|
||||
:label="null"
|
||||
/>
|
||||
</div>
|
||||
<div class="column is-6">
|
||||
<!-- PO Number -->
|
||||
<TimeBookingsElementSelectPoNumber :poNumber="poNumber" @update:poNumber="poNumber = $event"
|
||||
@onClickPoNumber="onClickPoNumber" :is-po-number-input="isPoNumberInput"
|
||||
@update:is-po-number-input="isPoNumberInput = $event"
|
||||
:label="null"
|
||||
/>
|
||||
</div>
|
||||
<div class="column is-12">
|
||||
<TimeBookingsElementSelectNotes :notes="notes" @update:notes="notes = $event" @onClickNotes="onClickNotes"
|
||||
:is-notes-input="isNotesInput" @update:is-notes-input="isNotesInput = $event"
|
||||
:label="(notes !== '' || isNotesInput) ? 'Note (valgfrit)' : null"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="column is-6"><!--Spacer--></div>
|
||||
<div class="column is-6" v-show="isAnySuggestionsDisplayed">
|
||||
<!-- If the user has selected a vehicle, whilst reactively interacting with the right walkthrough, show the next button -->
|
||||
@@ -802,8 +817,8 @@ const removeProductInBasketById = (id: number) => {
|
||||
<hr />
|
||||
<!-- Time selection -->
|
||||
<div class="columns is-mobile is-vcentered is-multiline">
|
||||
<div class="column is-2" v-for="timeslot in getTimeSlots(dateTime)" :key="timeslot.time">
|
||||
<div class="custom-date-picker-time" :class="{ 'is-selected': isTimeSlotSelected(timeslot, dateTime) }" @click="selectTimeSlot(timeslot, dateTime)">
|
||||
<div class="column is-2" v-for="timeslot in getTimeSlots(dateTime)" :key="timeslot.time" style="cursor: not-allowed;">
|
||||
<div class="custom-date-picker-time custom-disabled" :class="{ 'is-selected': isTimeSlotSelected(timeslot, dateTime) && false }" @click="selectTimeSlot(timeslot, dateTime)">
|
||||
<div class="custom-date-picker-time-hour">{{ timeslot.time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -983,6 +998,11 @@ const removeProductInBasketById = (id: number) => {
|
||||
.is-fixed-half {
|
||||
width: 50%;
|
||||
}
|
||||
.custom-disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.has-invisible-scrollbar {
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
|
||||
Reference in New Issue
Block a user