Add vehicle type management and summary display in time booking process
This commit is contained in:
@@ -59,7 +59,7 @@ const uniqueId = Math.random().toString(36).substring(7);
|
||||
</div>
|
||||
<!-- Optional price display -->
|
||||
<div class="level-item" v-if="price !== null">
|
||||
<span class="tag is-success">{{ SessionUser.functions.currency.toLocal(price) }}</span>
|
||||
<span class="tag is-rounded is-light" v-bind:class="{ 'is-success': props.value }">{{ SessionUser.functions.currency.toLocal(price) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -34,6 +34,7 @@ export const ObjectsGlobal = {
|
||||
import: "Importer",
|
||||
phone_country_code: "Landekode",
|
||||
succeeded: "Fuldførte",
|
||||
summary: "Oversigt",
|
||||
opening_hours: "Åbningstider",
|
||||
customers: "Kunder",
|
||||
selected: "Valgt",
|
||||
|
||||
+11
-16
@@ -3,21 +3,14 @@ import RestrictedPageWrapper from "@/components/page/wrappers/RestrictedPageWrap
|
||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||
import DepartmentDashboardPageWrapper from "@/views/dashboards/departmentDashboard/DepartmentDashboardPageWrapper.vue";
|
||||
import NotFoundFallBackPageWrapper from "@/components/page/wrappers/NotFoundFallBackPageWrapper.vue";
|
||||
import TimeBookingsCalendar from "@/components/timebookings/TimeBookingsCalendar.vue";
|
||||
import TimeBookingsTypesPagination
|
||||
from "@/components/displays/pagination/models/DepartmentPos/TimeBookingsTypesPagination.vue";
|
||||
import TimeBookingsNewStep1
|
||||
from "@/views/dashboards/departmentDashboard/modules/time-bookings/book/steps/TimeBookingsNewStep1.vue";
|
||||
|
||||
|
||||
import {timeBookingsNewProcess}
|
||||
from "@/views/dashboards/departmentDashboard/modules/time-bookings/book/TimeBookingsNewProcess.vue";
|
||||
import ButtonsBox from "@/components/displays/boxes/ButtonsBox.vue";
|
||||
import {timeBookingsNewProcess} from "@/views/dashboards/departmentDashboard/modules/time-bookings/book/TimeBookingsNewProcess.vue";
|
||||
import TimeBookingsNewControls
|
||||
from "@/views/dashboards/departmentDashboard/modules/time-bookings/book/displays/TimeBookingsNewControls.vue";
|
||||
import ShowErrorField from "@/components/global/ShowErrorField.vue";
|
||||
import TimeBookingsNewStepDivider
|
||||
from "@/views/dashboards/departmentDashboard/modules/time-bookings/book/displays/TimeBookingsNewStepDivider.vue";
|
||||
import TimeBookingsNewSummaryView
|
||||
from "@/views/dashboards/departmentDashboard/modules/time-bookings/book/displays/TimeBookingsNewSummaryView.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -29,12 +22,14 @@ import TimeBookingsNewStepDivider
|
||||
<TimeBookingsNewStepDivider>
|
||||
<!-- If no component is available, show a fallback -->
|
||||
<NotFoundFallBackPageWrapper :exists="timeBookingsNewProcess.getCurrentComponentView() !== null" error="Der er ingen trin tilgængelige for denne tidsbooking.">
|
||||
<!-- Steps for creating a new time booking -->
|
||||
<component :is="timeBookingsNewProcess.getCurrentComponentView()" v-bind="timeBookingsNewProcess" v-if="timeBookingsNewProcess.getCurrentComponentView()" v-bind:timeBookingsNewProcess="timeBookingsNewProcess"/>
|
||||
{{ timeBookingsNewProcess.phone_country_code}}
|
||||
{{ timeBookingsNewProcess.phone }}
|
||||
{{ timeBookingsNewProcess.reg_1}}
|
||||
{{timeBookingsNewProcess.vehicleType}}
|
||||
<TimeBookingsNewSummaryView>
|
||||
<template #content-left>
|
||||
<!-- Steps for creating a new time booking -->
|
||||
<component :is="timeBookingsNewProcess.getCurrentComponentView()" v-bind="timeBookingsNewProcess" v-if="timeBookingsNewProcess.getCurrentComponentView()" v-bind:timeBookingsNewProcess="timeBookingsNewProcess"/>
|
||||
</template>
|
||||
<template #content-bottom>
|
||||
</template>
|
||||
</TimeBookingsNewSummaryView>
|
||||
<!-- If the step is not found, show an error message -->
|
||||
<template #error>
|
||||
<ShowErrorField :message="`Trin ${timeBookingsNewProcess.step.value}'s komponent blev ikke fundet. Dette er en fejl i systemet, og formodentligvis en fejl i udviklingen. Kontakt venligst support, hvis du ser denne fejl.`"/>
|
||||
|
||||
+39
-1
@@ -16,6 +16,7 @@ const isBookingForCustomer = ref(false); // Flag to indicate if the booking is f
|
||||
const persistentCookiesEnabled = ref(false); // Flag to indicate if persistent cookies are enabled
|
||||
const cookie_expires = '365d'; // Cookie expiration time
|
||||
const addons = ref([]); // Placeholder for addons, if any
|
||||
const vehicleTypeObject = ref(null); // Placeholder for the vehicle type object
|
||||
// Persistent cookies for storing user data
|
||||
const cookies = useCookies().cookies;
|
||||
const getCookieName = (name) => {
|
||||
@@ -202,12 +203,36 @@ const getAddons = () => {
|
||||
return addons.value;
|
||||
};
|
||||
|
||||
const toggleAddon = (addon) => {
|
||||
const addonObjects = ref([]); // Addon objects to be used in the booking process
|
||||
|
||||
const getAddonObject = (addon) => {
|
||||
// Function to get the addon object by its ID
|
||||
return addonObjects.value.find(obj => obj.id === addon) || null;
|
||||
};
|
||||
|
||||
const addAddonObject = (addon) => {
|
||||
// Function to add an addon object to the list
|
||||
if (!addonObjects.value.some(obj => obj.id === addon.id)) {
|
||||
addonObjects.value.push(addon);
|
||||
}
|
||||
};
|
||||
|
||||
const removeAddonObject = (addon) => {
|
||||
// Function to remove an addon object from the list
|
||||
const index = addonObjects.value.findIndex(obj => obj.id === addon.id);
|
||||
if (index > -1) {
|
||||
addonObjects.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleAddon = (addon, addon_object) => {
|
||||
// Function to toggle an addon
|
||||
if (hasAddon(addon)) {
|
||||
removeAddon(addon);
|
||||
removeAddonObject(addon_object);
|
||||
} else {
|
||||
addAddon(addon);
|
||||
addAddonObject(addon_object);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -246,6 +271,14 @@ watch([reg_1], ([newReg1]) => {
|
||||
if (newReg1) reg_1.value = formatRegistrationNumber(newReg1);
|
||||
});
|
||||
|
||||
// Watch for changes in the vehicle type, remove the addon objects if the vehicle type is changed
|
||||
watch(vehicleType, (newVehicleType) => {
|
||||
if (newVehicleType !== null) {
|
||||
// If a new vehicle type is selected, clear the addons
|
||||
addonObjects.value = [];
|
||||
addons.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
// Expose the nextStep and previousStep methods
|
||||
export const timeBookingsNewProcess = {
|
||||
@@ -272,5 +305,10 @@ export const timeBookingsNewProcess = {
|
||||
hasAddon,
|
||||
getAddons,
|
||||
toggleAddon,
|
||||
getAddonObject,
|
||||
addonObjects,
|
||||
addAddonObject,
|
||||
removeAddonObject,
|
||||
vehicleTypeObject
|
||||
};
|
||||
</script>
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
import { SessionUser } from '@/components/session/token/SessionUser.vue';
|
||||
import TimeBookingsNewSummaryLine
|
||||
from "@/views/dashboards/departmentDashboard/modules/time-bookings/book/displays/TimeBookingsNewSummaryLine.vue";
|
||||
import { timeBookingsNewProcess } from "@/views/dashboards/departmentDashboard/modules/time-bookings/book/TimeBookingsNewProcess.vue";
|
||||
import {ref, computed} from "vue";
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<label class="label">{{ SessionUser.objects.global.language.summary }}</label>
|
||||
<!-- Phone number summary line -->
|
||||
<TimeBookingsNewSummaryLine label="Telefonnummer" :value="`${timeBookingsNewProcess.phone_country_code.value} ${timeBookingsNewProcess.phone.value}`"/>
|
||||
<!-- Registration number summary line -->
|
||||
<TimeBookingsNewSummaryLine label="Registreringsnummer" :value="timeBookingsNewProcess.reg_1.value"/>
|
||||
<!-- Primary vehicle type summary line -->
|
||||
<TimeBookingsNewSummaryLine
|
||||
v-if="timeBookingsNewProcess.vehicleTypeObject.value"
|
||||
label="Primær køretøjstype"
|
||||
v-bind:value="timeBookingsNewProcess.vehicleTypeObject.value ? timeBookingsNewProcess.vehicleTypeObject.value.label : 'Ikke valgt'"
|
||||
/>
|
||||
<!-- Price summary line -->
|
||||
<TimeBookingsNewSummaryLine
|
||||
v-if="timeBookingsNewProcess.vehicleTypeObject.value"
|
||||
v-bind:label="timeBookingsNewProcess.vehicleTypeObject.value ? timeBookingsNewProcess.vehicleTypeObject.value.label : 'Ikke valgt'"
|
||||
v-bind:value="timeBookingsNewProcess.vehicleTypeObject.value ? SessionUser.functions.currency.toLocal(timeBookingsNewProcess.vehicleTypeObject.value.price) : 'Ikke valgt'"
|
||||
color="is-success"
|
||||
/>
|
||||
<!-- Addons summary line -->
|
||||
<template v-for="addon in timeBookingsNewProcess.addonObjects.value" :key="addon.id">
|
||||
<TimeBookingsNewSummaryLine
|
||||
:label="addon.name"
|
||||
v-bind:value="SessionUser.functions.currency.toLocal(addon.product.price)"
|
||||
icon="fas fa-plus"
|
||||
color="is-info"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
import { defineProps, ref, computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: 'is-primary',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Level summary line -->
|
||||
<nav class="level is-mobile" style="width: 100%;">
|
||||
<div class="level-left">
|
||||
<!-- Left side with icon and label -->
|
||||
<div class="level-item">
|
||||
<span class="icon" v-if="icon">
|
||||
<i :class="icon"></i>
|
||||
</span>
|
||||
<span>{{ label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<!-- Right side with value -->
|
||||
<div class="level-item">
|
||||
<span style="font-weight: bold;">
|
||||
{{ value }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import TimeBookingsNewSummary
|
||||
from "@/views/dashboards/departmentDashboard/modules/time-bookings/book/displays/TimeBookingsNewSummary.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="columns is-multiline">
|
||||
<!-- Left column -->
|
||||
<div class="column is-half">
|
||||
<slot name="content-left"></slot>
|
||||
</div>
|
||||
<!-- Summary of the new time booking -->
|
||||
<div class="column is-half">
|
||||
<TimeBookingsNewSummary />
|
||||
<!-- Additional slot -->
|
||||
<slot name="content-right"></slot>
|
||||
</div>
|
||||
<!-- Bottom column -->
|
||||
<div class="column is-full">
|
||||
<slot name="content-bottom"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+25
-5
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import ConfigurationSelect from "@/components/displays/superuser/configuration/ConfigurationSelect.vue";
|
||||
import {SessionUser} from "@/components/session/token/SessionUser.vue";
|
||||
import {defineEmits, defineProps, ref} from "vue";
|
||||
import {defineEmits, defineProps, ref, watch} from "vue";
|
||||
import ConfigurationSwitch from "@/components/displays/superuser/configuration/ConfigurationSwitch.vue";
|
||||
|
||||
const props = defineProps({
|
||||
@@ -13,7 +13,7 @@ const props = defineProps({
|
||||
|
||||
const emits = defineEmits(['update:selected']);
|
||||
const countryCodes = ref(null);
|
||||
const onSelect = (value) => {
|
||||
const onSelect = (value, automated = false) => {
|
||||
// Attempt to set the selected vehicle type
|
||||
props.timeBookingsNewProcess.vehicleType.value = value;
|
||||
emits('update:selected', value);
|
||||
@@ -42,6 +42,27 @@ const getVehicleType = (id) => {
|
||||
return vehicleTypes.value.filter(vehicle => vehicle.value === parseInt(id))[0];
|
||||
};
|
||||
|
||||
watch(() => props.timeBookingsNewProcess.vehicleType.value, (newValue) => {
|
||||
if (newValue !== null) {
|
||||
props.timeBookingsNewProcess.vehicleTypeObject.value = getVehicleType(newValue);
|
||||
} else {
|
||||
props.timeBookingsNewProcess.vehicleTypeObject.value = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Watch for changes in the vehicleTypes array to update the vehicle type object
|
||||
watch(vehicleTypes, (newVehicleTypes) => {
|
||||
if (props.timeBookingsNewProcess.vehicleType.value !== null) {
|
||||
props.timeBookingsNewProcess.vehicleTypeObject.value = getVehicleType(props.timeBookingsNewProcess.vehicleType.value);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize the vehicle type object based on the current value
|
||||
if (props.timeBookingsNewProcess.vehicleType.value !== null) {
|
||||
onSelect(props.timeBookingsNewProcess.vehicleType.value, true);
|
||||
} else {
|
||||
props.timeBookingsNewProcess.vehicleTypeObject.value = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -63,13 +84,12 @@ const getVehicleType = (id) => {
|
||||
<ConfigurationSwitch
|
||||
v-bind:is-compact="true"
|
||||
v-bind:value="props.timeBookingsNewProcess.hasAddon(addon.option_id)"
|
||||
v-bind:on-switch="() => props.timeBookingsNewProcess.toggleAddon(addon.option_id)"
|
||||
v-bind:on-switch="() => props.timeBookingsNewProcess.toggleAddon(addon.option_id, addon)"
|
||||
v-bind:title="addon.name"
|
||||
v-bind:description="addon.description"
|
||||
v-bind:price="addon.price"
|
||||
v-bind:price="addon.product.price"
|
||||
/>
|
||||
</template>
|
||||
{{ props.timeBookingsNewProcess.getAddons()}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user