"Refactor booking-related components: update terminology in OrderBookings.vue, enhance error handling in ShowErrorField.vue, and improve action handling with extended support and dynamic display in ActionSettingsWheelButton.vue."

This commit is contained in:
Jeppe Bundgaard
2025-11-17 10:15:29 +01:00
parent 9c35e34ce6
commit 3ae84cac1c
5 changed files with 646 additions and 233 deletions
@@ -7,6 +7,7 @@ import Swal from "sweetalert2";
import { ref } from "vue";
import CustomerModal from "@/components/displays/modals/CustomerModal.vue";
import ActionSettingsWheelItemLabel from "@/components/displays/buttons/ActionSettingsWheelItemLabel.vue";
import ShowErrorField from "@/components/global/ShowErrorField.vue";
const emit = defineEmits(['deleted']);
const emitDeleted = () => {
emit('deleted');
@@ -61,6 +62,10 @@ const props = defineProps({
attachments: {
type: Array,
default: () => []
},
displayActionsDirectly: {
type: Boolean,
default: false
}
});
@@ -155,6 +160,7 @@ const hasUser = () => {
}
const attachmentsFromOrder = ref([]);
const attachmentsFromOrderError = ref(null);
onMounted(() => {
if (props.order_id) {
SessionUser.objects.orders.functions.fetchAttachments(props.order_id).then((response) => {
@@ -181,220 +187,495 @@ onMounted(() => {
response = [];
}
attachmentsFromOrder.value = response;
}).catch((error) => {});
}).catch((error) => {
const parsedErrorMessage = SessionUser.functions.parseErrorMessage(error);
console.warn('Error fetching attachments from order:', error, parsedErrorMessage);
// Check if there's an error message in the response
attachmentsFromOrderError.value = `ERROR: ${parsedErrorMessage || 'Failed to fetch attachments'}`;
});
}
});
const defaultActions = computed(() => {
return [
/**
* Example action
{
icon: 'fas fa-eye',
label: 'View Order',
clickAction: () => {
console.warn('View Order clicked');
},
showFunction: () => {
return true; // Logic to determine if action should be shown
},
disabled: false,
template: 'success' // Optional: Set the template to 'success' or 'danger'
isLabel: false // Optional: Set to true to render the label as a button
}
*/
/** Bookings actions */
{
label: 'Booking',
isLabel: true,
showFunction: () => {
return !!props.order_booking_id;
}
},
{
icon: 'fas fa-check-circle',
label: 'Marker som gennemført',
template: 'success',
clickAction: () => {
return SessionUser.objects.order_bookings.functions.showCompleteConfirmationModal(props.order_booking_id, () => {
props.refreshFunction();
});
},
showFunction: () => {
return !!props.order_booking_id && SessionUser.canAccessAdmin() && !props.order_id;
},
disabled: false,
},
{
icon: 'fas fa-external-link-alt',
label: 'Se booking i ny fane',
clickAction: () => {
return SessionUser.functions.redirectTo.department(props.department_id, 'modules/bookings/order/' + props.order_booking_id, true);
},
showFunction: () => {
return !!props.order_booking_id;
},
},
{
icon: 'fas fa-edit',
label: 'Ændr tilknytning',
clickAction: () => {
return SessionUser.objects.order_bookings.showEditObjectFieldForm(props.order_booking_id, 'order_id', props.order_id, () => {
props.refreshFunction();
}, {
filters: {
...(props.customer_number ? {customer_id: props.customer_number} : {}),
...(props.department_id ? {department_id: props.department_id} : {})
},
pagination: {
page: 1,
limit: 100
}
});
},
showFunction: () => {
return !!props.order_booking_id && SessionUser.canAccessAdmin();
},
disabled: false,
},
{
icon: 'fas fa-trash-alt',
label: 'Slet booking',
clickAction: () => {
return SessionUser.objects.order_bookings.functions.showDeleteConfirmationModal(props.order_booking_id, () => {
props.refreshFunction();
});
},
showFunction: () => {
return !!props.order_booking_id && SessionUser.canAccessAdmin() && !props.order_id;
},
disabled: false,
template: 'danger'
},
/** Orders actions */
{
label: SessionUser.functions.ucFirst(SessionUser.objects.orders.meta.labels.single),
isLabel: true,
showFunction: () => {
return !!props.order_id;
}
},
{
icon: 'fas fa-external-link-alt',
label: 'Se ordre i ny fane',
clickAction: async () => {
return redirectDepartmentOrderPage(props.order_id, true);
},
showFunction: () => {
return !!props.order_id && (SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser());
},
},
{
icon: 'fas fa-user-edit',
label: 'Skift kunde',
clickAction: () => {
return SessionUser.objects.orders.functions.showChangeCustomerForm(props.order_id);
},
showFunction: () => {
return !!props.order_id && SessionUser.canAccessSuperUser();
},
disabled: false,
},
{
icon: 'fas fa-file-invoice-dollar',
label: 'Skift faktura samling',
clickAction: () => {
return SessionUser.objects.orders.functions.showChangeInvoiceCollectionForm(props.order_id);
},
showFunction: () => {
return !!props.order_id && SessionUser.canAccessSuperUser();
},
disabled: false,
},
{
icon: 'fas fa-trash-alt',
label: 'Slet ordre',
clickAction: () => {
return SessionUser.objects.orders.functions.showDeleteConfirmationModal(props.order_id, () => {
emitDeleted();
});
},
showFunction: () => {
return !!props.order_id && (SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser());
},
disabled: false,
template: 'danger'
},
{
icon: 'fas fa-file-invoice-dollar',
label: 'Vis faktura samling i ny fane',
clickAction: () => {
return redirectSuperUserInvoiceCollectionPage(props.invoice_collection_id);
},
showFunction: () => {
return !!props.invoice_collection_id && SessionUser.canAccessSuperUser();
},
},
/** Vehicle actions */
{
label: props.reg_1 && props.reg_2 ? `${SessionUser.objects.vehicles.meta.labels.multiple}` : `${SessionUser.objects.vehicles.meta.labels.single}`,
isLabel: true,
},
{
icon: 'fas fa-car',
label: `Se køretøj i ny fane`,
clickAction: () => {
return SessionUser.functions.redirectTo.superUser('/vehicles/' + props.reg_1, true);
},
showFunction: () => {
return !!props.reg_1 && SessionUser.canAccessSuperUser();
},
},
{
icon: 'fas fa-car',
label: `Se køretøj i ny fane`,
clickAction: () => {
return SessionUser.functions.redirectTo.superUser('/vehicles/' + props.reg_2, true);
},
showFunction: () => {
return !!props.reg_2 && SessionUser.canAccessSuperUser();
},
},
/** User actions */
{ label: SessionUser.objects.global.language.customer,
isLabel: true,
showFunction: () => {
return hasUser() && SessionUser.canAccessSuperUser();
}
},
{
icon: 'fas fa-user-edit',
label: 'Se kunde i ny fane',
clickAction: () => {
return SessionUser.functions.redirectTo.superUser('/users/' + userIdFromCustomerNumber.value, true);
},
showFunction: () => {
return hasUser() && SessionUser.canAccessSuperUser() && userIdFromCustomerNumber.value;
},
},
{
icon: 'fas fa-user-shield',
label: 'Log ind som kunde',
clickAction: () => {
return SessionUser.superUser.intimidate.intimidateUser(userIdFromCustomerNumber.value);
},
showFunction: () => {
return hasUser() && SessionUser.canAccessSuperUser();
},
},
{
icon: 'fas fa-key',
label: 'Skift kodeord',
clickAction: () => {
return showSetCustomerPassword(userIdFromCustomerNumber.value);
},
showFunction: () => {
return hasUser() && SessionUser.canAccessSuperUser();
},
},
{
icon: 'fas fa-user',
label: 'Vis kunde',
clickAction: () => {
customerModalVisible.value = true;
},
showFunction: () => {
return hasUser() && SessionUser.canAccessSuperUser();
},
},
];
});
</script>
<template>
<div class="dropdown is-right is-hoverable">
<div class="dropdown-trigger">
<button class="button is-small is-dark" aria-haspopup="true" aria-controls="dropdown-menu">
<span class="icon">
<i :class="props.icon"></i>
</span>
<span v-if="props.label.length > 0" class="ml-2">{{ props.label }}</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
<!-- Actions (If defined) -->
<template v-if="useSlots().actions">
<slot name="actions"></slot>
<!-- If there is an order_booking_id, show the order booking actions -->
<template v-if="props.order_booking_id">
<ActionSettingsWheelItemLabel :label="'Booking'"/>
<ActionSettingsWheelItem
v-if="(SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser()) && !props.order_id"
:icon="'fas fa-check-circle'"
:label="'Marker som gennemført'"
template="success"
:click-action="() => SessionUser.objects.order_bookings.functions.showCompleteConfirmationModal(props.order_booking_id, () => {
props.refreshFunction();
})"
<div>
<template v-if="displayActionsDirectly">
<!-- Error -->
<template v-if="attachmentsFromOrderError">
<div class="message is-danger">
<div class="message-body">
{{ attachmentsFromOrderError }}
</div>
</div>
</template>
<template v-for="(action, index) in [ ...defaultActions, ...(useSlots().actions ? [] : []) ]" :key="index">
<ActionSettingsWheelItem
v-if="(!action.showFunction || action.showFunction()) && (!action.isLabel || action.isLabel === false)"
:icon="action.icon"
:label="action.label"
:template="action.template"
:click-action="action.clickAction"
:show-function="action.showFunction"
:disabled="action.disabled"
/>
<ActionSettingsWheelItemLabel
v-else-if="action.isLabel && action.isLabel === true && (!action.showFunction || action.showFunction())"
:label="action.label"
/>
<ActionSettingsWheelItem
:icon="'fas fa-external-link-alt'"
:label="'Se booking i ny fane'"
:click-action="() => SessionUser.functions.redirectTo.department(props.department_id, 'modules/bookings/order/' + props.order_booking_id, true)"
/>
<ActionSettingsWheelItem
v-if="SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser()"
:icon="'fas fa-edit'"
:label="(!props.order_id) ? `Tilknyt ${SessionUser.objects.orders.meta.labels.single.toLowerCase()}` : `Ændr ${SessionUser.objects.orders.meta.labels.single.toLowerCase()}`"
:click-action="() => SessionUser.objects.order_bookings.showEditObjectFieldForm(props.order_booking_id, 'order_id', props.order_id, () => {
props.refreshFunction();
}, {
filters: {
...(customer_number ? {customer_id: props.customer_number} : {}),
...(props.department_id ? {department_id: props.department_id} : {})
},
pagination: {
page: 1,
limit: 100
}
})"
/>
<ActionSettingsWheelItem
:icon="'fas fa-trash-alt'"
:label="'Slet booking'"
template="danger"
v-show="!props.order_id"
:click-action="() => SessionUser.objects.order_bookings.functions.showDeleteConfirmationModal(props.order_booking_id, () => {
props.refreshFunction();
})"
/>
</template>
<!-- If there's an order_id, show the order actions -->
<template v-if="props.order_id">
<!-- Label -->
<action-settings-wheel-item-label
:label="SessionUser.functions.ucFirst(SessionUser.objects.orders.meta.labels.single)"
/>
<!-- Go to the order page, in a new tab -->
<action-settings-wheel-item
v-if="SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser()"
:label="`Se ${SessionUser.objects.orders.meta.labels.single.toLowerCase()} i ny fane`"
icon="fas fa-external-link-alt"
:click-action="async () => redirectDepartmentOrderPage(props.order_id, true)"
:disabled="false"
/>
<!-- Go to the user/order/:order_id page, in a new tab -->
<action-settings-wheel-item
v-if="!SessionUser.canAccessAdmin() && !SessionUser.canAccessSuperUser()"
:label="`Se ${SessionUser.objects.orders.meta.labels.single.toLowerCase()} i ny fane`"
icon="fas fa-external-link-alt"
:click-action="() => SessionUser.functions.redirectTo.user('/orders/' + props.order_id, true)"
:disabled="false"
/>
<!-- Change the customer (of the order) -->
<action-settings-wheel-item
v-if="SessionUser.canAccessSuperUser()"
label="Skift kunde"
icon="fas fa-user-edit"
:click-action="() => SessionUser.objects.orders.functions.showChangeCustomerForm(props.order_id)"
:disabled="false"
/>
<!-- Change the invoice collection (of the order) -->
<action-settings-wheel-item
v-if="SessionUser.canAccessSuperUser()"
label="Skift faktura samling"
icon="fas fa-file-invoice-dollar"
:click-action="() => SessionUser.objects.orders.functions.showChangeInvoiceCollectionForm(props.order_id)"
:disabled="false"
/>
<!-- Delete the order -->
<action-settings-wheel-item
v-if="SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser()"
:label="`Slet ${SessionUser.objects.orders.meta.labels.single.toLowerCase()}`"
template="danger"
icon="fas fa-trash-alt"
:click-action="() => SessionUser.objects.orders.functions.showDeleteConfirmationModal(props.order_id, () => {
emitDeleted();
})"
:disabled="false"
/>
</template>
<!-- If there is an invoice collection id, show the invoice actions -->
<template v-if="props.invoice_collection_id">
<!-- Label -->
<action-settings-wheel-item-label
:label="SessionUser.objects.collectedOrderInvoices.meta.title"
v-show="SessionUser.canAccessSuperUser()"
/>
<!-- Show the invoice collection, in a new tab -->
<action-settings-wheel-item
v-if="SessionUser.canAccessSuperUser()"
label="Vis faktura samling i ny fane"
icon="fas fa-file-invoice-dollar"
:click-action="() => redirectSuperUserInvoiceCollectionPage(props.invoice_collection_id)"
/>
</template>
<!-- If there's a user_id, show the user actions -->
<template v-if="hasUser()">
<!-- Label -->
<ActionSettingsWheelItemLabel
:label="SessionUser.objects.global.language.customer"
v-show="SessionUser.canAccessSuperUser()"
/>
<!-- Se bruger -->
<ActionSettingsWheelItem
label="Se kunde i ny fane"
icon="fas fa-user-edit"
:click-action="() => SessionUser.functions.redirectTo.superUser('/users/' + userIdFromCustomerNumber, true)"
v-if="SessionUser.canAccessSuperUser() && userIdFromCustomerNumber"
/>
<!-- Log ind som kunde -->
<ActionSettingsWheelItem
label="Log ind som kunde"
icon="fas fa-user-shield"
:click-action="() => SessionUser.superUser.intimidate.intimidateUser(userIdFromCustomerNumber)"
v-if="SessionUser.canAccessSuperUser()"
/>
<!-- Set password -->
<ActionSettingsWheelItem
label="Skift kodeord"
icon="fas fa-key"
:click-action="() => showSetCustomerPassword(userIdFromCustomerNumber)"
v-if="SessionUser.canAccessSuperUser()"
/>
<!-- Show customer modal -->
<ActionSettingsWheelItem
label="Vis kunde"
icon="fas fa-user"
:click-action="() => customerModalVisible = true"
v-if="SessionUser.canAccessSuperUser()"
/>
</template>
<!-- If there is a reg_1, show the reg_1 actions -->
<template v-if="props.reg_1 || props.reg_2">
<!-- Label -->
<action-settings-wheel-item-label
:label="props.reg_1 && props.reg_2 ? `${SessionUser.objects.vehicles.meta.labels.multiple}` : `${SessionUser.objects.vehicles.meta.labels.single}`"
v-show="SessionUser.canAccessSuperUser()"
/>
<!-- Show the vehicle, in a new tab -->
<action-settings-wheel-item
:label="`Se ${SessionUser.objects.vehicles.meta.labels.single.toLowerCase()} ${props.reg_1} i ny fane`"
icon="fas fa-car"
v-if="props.reg_1 && SessionUser.canAccessSuperUser()"
:click-action="() => SessionUser.functions.redirectTo.superUser('/vehicles/' + props.reg_1, true)"
/>
<action-settings-wheel-item
:label="`Se ${SessionUser.objects.vehicles.meta.labels.single.toLowerCase()} ${props.reg_2} i ny fane`"
icon="fas fa-car"
v-if="props.reg_2 && SessionUser.canAccessSuperUser()"
:click-action="() => SessionUser.functions.redirectTo.superUser('/vehicles/' + props.reg_2, true)"
/>
<!-- If there are any attachments, show the attachments actions -->
<template v-if="attachmentsFromOrder.length > 0">
<action-settings-wheel-item-label
:label="'Vedhæftede filer'"
</template>
<!-- If there are attachments, show the attachments -->
<template v-if="attachmentsFromOrder.length > 0">
<ActionSettingsWheelItemLabel :label="'Vedhæftede filer'"/>
<ActionSettingsWheelItem
v-for="attachment in attachmentsFromOrder"
:key="attachment.id"
:icon="'fas fa-paperclip'"
:label="`Åbn vedhæftede fil #${attachment.id}`"
:click-action="() => SessionUser.objects.orders.functions.downloadAttachment(props.order_id, attachment.id, true)
.catch((error) => {
Swal.fire({
title: 'Fejl',
text: 'Der opstod en fejl under download af vedhæftede fil.',
icon: 'error',
});
})"
/>
</template>
</template>
<div class="dropdown is-right is-hoverable" v-else>
<div class="dropdown-trigger">
<button class="button is-small is-dark" aria-haspopup="true" aria-controls="dropdown-menu">
<span class="icon">
<i :class="props.icon"></i>
</span>
<span v-if="props.label.length > 0" class="ml-2">{{ props.label }}</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
<!-- Actions (If defined) -->
<template v-if="useSlots().actions">
<slot name="actions"></slot>
<!-- If there is an order_booking_id, show the order booking actions -->
<template v-if="props.order_booking_id">
<ActionSettingsWheelItemLabel :label="'Booking'"/>
<ActionSettingsWheelItem
v-if="(SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser()) && !props.order_id"
:icon="'fas fa-check-circle'"
:label="'Marker som gennemført'"
template="success"
:click-action="() => SessionUser.objects.order_bookings.functions.showCompleteConfirmationModal(props.order_booking_id, () => {
props.refreshFunction();
})"
/>
<template v-for="(attachment, index) in attachmentsFromOrder" :key="index">
<action-settings-wheel-item
:label="`${attachment.content?.document || attachment.content?.other}`"
icon="fas fa-paperclip"
:click-action="() => SessionUser.objects.orders.functions.downloadAttachment(props.order_id, attachment.id, true)
.catch((error) => {
Swal.fire({
title: 'Fejl',
text: 'Der opstod en fejl under download af vedhæftede fil.',
icon: 'error',
});
})"
<ActionSettingsWheelItem
:icon="'fas fa-external-link-alt'"
:label="'Se booking i ny fane'"
:click-action="() => SessionUser.functions.redirectTo.department(props.department_id, 'modules/bookings/order/' + props.order_booking_id, true)"
/>
<ActionSettingsWheelItem
v-if="SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser()"
:icon="'fas fa-edit'"
:label="(!props.order_id) ? `Tilknyt ${SessionUser.objects.orders.meta.labels.single.toLowerCase()}` : `Ændr ${SessionUser.objects.orders.meta.labels.single.toLowerCase()}`"
:click-action="() => SessionUser.objects.order_bookings.showEditObjectFieldForm(props.order_booking_id, 'order_id', props.order_id, () => {
props.refreshFunction();
}, {
filters: {
...(customer_number ? {customer_id: props.customer_number} : {}),
...(props.department_id ? {department_id: props.department_id} : {})
},
pagination: {
page: 1,
limit: 100
}
})"
/>
<ActionSettingsWheelItem
:icon="'fas fa-trash-alt'"
:label="'Slet booking'"
template="danger"
v-show="!props.order_id"
:click-action="() => SessionUser.objects.order_bookings.functions.showDeleteConfirmationModal(props.order_booking_id, () => {
props.refreshFunction();
})"
/>
</template>
<!-- If there's an order_id, show the order actions -->
<template v-if="props.order_id">
<!-- Label -->
<action-settings-wheel-item-label
:label="SessionUser.functions.ucFirst(SessionUser.objects.orders.meta.labels.single)"
/>
<!-- Go to the order page, in a new tab -->
<action-settings-wheel-item
v-if="SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser()"
:label="`Se ${SessionUser.objects.orders.meta.labels.single.toLowerCase()} i ny fane`"
icon="fas fa-external-link-alt"
:click-action="async () => redirectDepartmentOrderPage(props.order_id, true)"
:disabled="false"
/>
<!-- Go to the user/order/:order_id page, in a new tab -->
<action-settings-wheel-item
v-if="!SessionUser.canAccessAdmin() && !SessionUser.canAccessSuperUser()"
:label="`Se ${SessionUser.objects.orders.meta.labels.single.toLowerCase()} i ny fane`"
icon="fas fa-external-link-alt"
:click-action="() => SessionUser.functions.redirectTo.user('/orders/' + props.order_id, true)"
:disabled="false"
/>
<!-- Change the customer (of the order) -->
<action-settings-wheel-item
v-if="SessionUser.canAccessSuperUser()"
label="Skift kunde"
icon="fas fa-user-edit"
:click-action="() => SessionUser.objects.orders.functions.showChangeCustomerForm(props.order_id)"
:disabled="false"
/>
<!-- Change the invoice collection (of the order) -->
<action-settings-wheel-item
v-if="SessionUser.canAccessSuperUser()"
label="Skift faktura samling"
icon="fas fa-file-invoice-dollar"
:click-action="() => SessionUser.objects.orders.functions.showChangeInvoiceCollectionForm(props.order_id)"
:disabled="false"
/>
<!-- Delete the order -->
<action-settings-wheel-item
v-if="SessionUser.canAccessAdmin() || SessionUser.canAccessSuperUser()"
:label="`Slet ${SessionUser.objects.orders.meta.labels.single.toLowerCase()}`"
template="danger"
icon="fas fa-trash-alt"
:click-action="() => SessionUser.objects.orders.functions.showDeleteConfirmationModal(props.order_id, () => {
emitDeleted();
})"
:disabled="false"
/>
</template>
<!-- If there is an invoice collection id, show the invoice actions -->
<template v-if="props.invoice_collection_id">
<!-- Label -->
<action-settings-wheel-item-label
:label="SessionUser.objects.collectedOrderInvoices.meta.title"
v-show="SessionUser.canAccessSuperUser()"
/>
<!-- Show the invoice collection, in a new tab -->
<action-settings-wheel-item
v-if="SessionUser.canAccessSuperUser()"
label="Vis faktura samling i ny fane"
icon="fas fa-file-invoice-dollar"
:click-action="() => redirectSuperUserInvoiceCollectionPage(props.invoice_collection_id)"
/>
</template>
<!-- If there's a user_id, show the user actions -->
<template v-if="hasUser()">
<!-- Label -->
<ActionSettingsWheelItemLabel
:label="SessionUser.objects.global.language.customer"
v-show="SessionUser.canAccessSuperUser()"
/>
<!-- Se bruger -->
<ActionSettingsWheelItem
label="Se kunde i ny fane"
icon="fas fa-user-edit"
:click-action="() => SessionUser.functions.redirectTo.superUser('/users/' + userIdFromCustomerNumber, true)"
v-if="SessionUser.canAccessSuperUser() && userIdFromCustomerNumber"
/>
<!-- Log ind som kunde -->
<ActionSettingsWheelItem
label="Log ind som kunde"
icon="fas fa-user-shield"
:click-action="() => SessionUser.superUser.intimidate.intimidateUser(userIdFromCustomerNumber)"
v-if="SessionUser.canAccessSuperUser()"
/>
<!-- Set password -->
<ActionSettingsWheelItem
label="Skift kodeord"
icon="fas fa-key"
:click-action="() => showSetCustomerPassword(userIdFromCustomerNumber)"
v-if="SessionUser.canAccessSuperUser()"
/>
<!-- Show customer modal -->
<ActionSettingsWheelItem
label="Vis kunde"
icon="fas fa-user"
:click-action="() => customerModalVisible = true"
v-if="SessionUser.canAccessSuperUser()"
/>
</template>
<!-- If there is a reg_1, show the reg_1 actions -->
<template v-if="props.reg_1 || props.reg_2">
<!-- Label -->
<action-settings-wheel-item-label
:label="props.reg_1 && props.reg_2 ? `${SessionUser.objects.vehicles.meta.labels.multiple}` : `${SessionUser.objects.vehicles.meta.labels.single}`"
v-show="SessionUser.canAccessSuperUser()"
/>
<!-- Show the vehicle, in a new tab -->
<action-settings-wheel-item
:label="`Se ${SessionUser.objects.vehicles.meta.labels.single.toLowerCase()} ${props.reg_1} i ny fane`"
icon="fas fa-car"
v-if="props.reg_1 && SessionUser.canAccessSuperUser()"
:click-action="() => SessionUser.functions.redirectTo.superUser('/vehicles/' + props.reg_1, true)"
/>
<action-settings-wheel-item
:label="`Se ${SessionUser.objects.vehicles.meta.labels.single.toLowerCase()} ${props.reg_2} i ny fane`"
icon="fas fa-car"
v-if="props.reg_2 && SessionUser.canAccessSuperUser()"
:click-action="() => SessionUser.functions.redirectTo.superUser('/vehicles/' + props.reg_2, true)"
/>
<!-- If there are any attachments, show the attachments actions -->
<template v-if="attachmentsFromOrder.length > 0">
<action-settings-wheel-item-label
:label="'Vedhæftede filer'"
/>
<template v-for="(attachment, index) in attachmentsFromOrder" :key="index">
<action-settings-wheel-item
:label="`${attachment.content?.document || attachment.content?.other}`"
icon="fas fa-paperclip"
:click-action="() => SessionUser.objects.orders.functions.downloadAttachment(props.order_id, attachment.id, true)
.catch((error) => {
Swal.fire({
title: 'Fejl',
text: 'Der opstod en fejl under download af vedhæftede fil.',
icon: 'error',
});
})"
/>
</template>
</template>
</template>
</template>
</template>
<!-- If no actions are defined, then show that no actions are defined -->
<template v-else>
<div class="dropdown-item">
<p class="has-text-centered">Ingen handlinger defineret</p>
</div>
</template>
<!-- If no actions are defined, then show that no actions are defined -->
<template v-else>
<div class="dropdown-item">
<p class="has-text-centered">Ingen handlinger defineret</p>
</div>
</template>
</div>
</div>
</div>
<CustomerModal
+6 -2
View File
@@ -12,14 +12,18 @@ const props = defineProps({
type: [String, Function, Object],
default: null,
required: false
},
template: {
type: String,
default: 'is-warning',
}
});
</script>
<template>
<template v-if="message">
<div class="notification is-warning">
<button class="delete"
<div class="notification" :class="template">
<button class="delete" v-show="false"
@click="clearErrors()"
></button>
{{ typeof message === 'function' ? message() : message }}
@@ -11,13 +11,13 @@ import { getDepartmentName } from "@/components/pagination/departmentTabs.vue";
*/
export const OrderBookings = {
meta: {
title: "Order Bookinger",
title: "Bookinger",
icon: "fas fa-list",
description: "Oversigt over order bookinger",
endpoint: "/order-bookings",
labels: {
single: "orderbooking",
multiple: "orderbookinger",
single: "booking",
multiple: "bookinger",
fields: {
names: {
customer_number: {
@@ -605,6 +605,7 @@ const showChangeOrderInvoiceCollectionForm = async (id) => {
return response.data.data;
}).catch((error) => {
console.error(error);
throw error;
});
},
/**
@@ -300,10 +300,24 @@ const fieldVisibility = reactive({
</div>
</div>
</div>
<div class="column is-narrow">
<div class="field mb-0">
<label class="label is-small">Kun i dag</label>
<div class="control">
<div class="select">
<select :value="onlyTodayFilter" @change="onOnlyTodayFilterChange">
<option value="*">Alle</option>
<option :value="new Date().toISOString().split('T')[0]">Ja</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<table class="table is-fullwidth">
<table class="table is-fullwidth is-hoverable is-striped"
style="table-layout: fixed;">
<tr>
<th>Dato</th>
<th v-show="fieldVisibility.customer_number">Kunde</th>
@@ -323,7 +337,37 @@ const fieldVisibility = reactive({
<template v-else>
<template v-for="booking in sortedList" :key="booking.id">
<tr v-if="isFirstBookingOfDay(booking)">
<td colspan="9"><strong>{{ new Date(booking.datetime).toLocaleDateString('da-DK', { year: 'numeric', month: 'long', day: 'numeric' }) }}</strong></td>
<td colspan="9" class="has-text-left">
<span class="has-text-grey">
<i class="fas fa-calendar-day"></i>
</span>
<span class="has-text-grey">
<!-- Human readable date (Today, Yesterday, etc.) -->
{{
(() => {
const bookingDate = new Date(booking.datetime);
const today = new Date();
const yesterday = new Date();
yesterday.setDate(today.getDate() - 1);
if (bookingDate.toDateString() === today.toDateString()) {
return 'I dag';
} else if (bookingDate.toDateString() === yesterday.toDateString()) {
return 'I går';
} else {
return bookingDate.toLocaleDateString('da-DK', { year: 'numeric', month: 'long', day: 'numeric' });
}
})()
}}
<!-- Number of bookings on the date -->
{{ sortedList.filter((b) => {
const bDate = new Date(b.datetime);
const bookingDate = new Date(booking.datetime);
return bDate.getFullYear() === bookingDate.getFullYear() &&
bDate.getMonth() === bookingDate.getMonth() &&
bDate.getDate() === bookingDate.getDate();
}).length }} {{SessionUser.objects.order_bookings.meta.labels.multiple.toLowerCase()}}
</span>
</td>
</tr>
<tr>
<td>
@@ -436,10 +480,22 @@ const fieldVisibility = reactive({
</td>
<!-- Actions -->
<td>
<ActionSettingsWheelButton :order_booking_id="booking.id" :order_id="booking.order_id" :customer_number="booking.customer_number" :department_id="booking.department" :refresh-function="loadList" :reg_1="booking.reg_1" :reg_2="booking.reg_2">
<template #actions>
</template>
</ActionSettingsWheelButton>
<div class="buttons is-right">
<button v-if="canEditBooking(booking) && !booking.order_id" class="button is-small is-warning" @click="SessionUser.objects.order_bookings.functions.showCompleteConfirmationModal(booking.id, loadList)">
<span class="icon">
<i class="fas fa-edit"></i>
</span>
</button>
<button v-if="!!booking.order_id" class="button is-small is-success" disabled>
<span class="icon">
<i class="fas fa-check-circle"></i>
</span>
</button>
<ActionSettingsWheelButton :order_booking_id="booking.id" :order_id="booking.order_id" :customer_number="booking.customer_number" :department_id="booking.department" :refresh-function="loadList" :reg_1="booking.reg_1" :reg_2="booking.reg_2" :icon="'fas fa-ellipsis-v'">
<template #actions>
</template>
</ActionSettingsWheelButton>
</div>
</td>
</tr>
</template>
@@ -523,33 +579,104 @@ const fieldVisibility = reactive({
<template v-else>
<div class="columns is-multiline is-mobile">
<template v-for="booking in sortedList" :key="booking.id">
<!-- Date separator -->
<div class="column is-full" v-if="isFirstBookingOfDay(booking)">
<hr/>
<strong>{{ new Date(booking.datetime).toLocaleDateString('da-DK', { year: 'numeric', month: 'long', day: 'numeric' }) }}</strong>
<hr/>
</div>
<div class="column is-full">
<WhiteBoxCard :forceStateFooter="false" :forceState="false" :defaultOpen="false" :toggleable="false" class="is-clickable" :has-hover-effect="true" :hasSelectionStyle="false">
<WhiteBoxCard :forceStateFooter="false" :forceState="false" :defaultOpen="false" :toggleable="true" class="is-clickable" :has-hover-effect="true" :hasSelectionStyle="false">
<template v-slot:header>
<div class="card-header-title is-flex is-justify-content-space-between is-align-items-center" style="width: 100%;">
<div>
<strong>{{ new Date(booking.datetime).toLocaleDateString('da-DK', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }) }}</strong>
<div>{{ booking.customer_name || 'Ukendt kunde' }}</div>
<div>{{ booking.reg_1 || 'Ingen registreret' }} {{ booking.reg_2 ? ', ' + booking.reg_2 : '' }}</div>
<div>{{ getDepartmentName(parseInt(booking.department)) }}</div>
<div>Ydelser:
<span
@mouseenter="onItemsMouseEnter($event, booking)"
@mouseleave="onItemsMouseLeave"
@click="onItemsClick($event, booking)"
style="cursor: pointer; text-overflow: ellipsis; overflow: hidden;"
>
{{ formatSummaryString(booking.items) }}
</span>
<div><small>{{ booking.customer_name || 'Ukendt kunde' }}</small></div>
</div>
</div>
<div class="is-pulled-right" style="width: fit-content">
<div>
<div class="columns is-multiline is-mobile is-gapless is-justify-content-flex-end is-align-items-center">
<!-- Department tag and status icon -->
<div class="column is-narrow has-text-right mb-2">
<span class="tag is-light is-small mr-1">
<span class="icon">
<!-- Clock, when pending -->
<i class="fas fa-clock has-text-warning" v-if="!booking.order_id"></i>
<!-- Check, when completed -->
<i class="fas fa-check-circle has-text-success" v-else></i>
</span>
<small v-if="!departmentFilter || departmentFilter === '*'">
{{ getDepartmentName(parseInt(booking.department)) }}
</small>
<small v-else>
<!-- Status message -->
{{ booking.order_id
? 'Udført'
: (!booking.pickup)
? `${SessionUser.functions.ucFirst(getTimeUntilString(booking.datetime))}`
: `Pickup ${getTimeUntilString(booking.datetime)}` }}
</small>
</span>
</div>
<!-- Edit button -->
<div class="column is-12 has-text-right mb-2">
<span class="buttons is-right mr-1">
<span class="button is-light is-danger is-small" @click="SessionUser.objects.order_bookings.functions.showDeleteConfirmationModal(booking.id, loadList)">
<span class="icon">
<i class="fas fa-trash-alt fa-lg"></i>
</span>
</span>
<span class="button is-warning is-small" v-if="canEditBooking(booking) && !booking.order_id" @click="SessionUser.objects.order_bookings.functions.showCompleteConfirmationModal(booking.id, loadList)">
<span class="icon">
<i class="fas fa-edit fa-lg"></i>
</span>
</span>
</span>
</div>
</div>
</div>
<div>
<ActionSettingsWheelButton :order_booking_id="booking.id" :order_id="booking.order_id" :customer_number="booking.customer_number" :department_id="booking.department" :refresh-function="loadList" :reg_1="booking.reg_1" :reg_2="booking.reg_2">
<template #actions>
</template>
</ActionSettingsWheelButton>
</div>
</template>
<template v-slot:default>
<div class="card-content">
<div><small>{{ booking.reg_1 || 'Ingen registreret' }} {{ booking.reg_2 ? ', ' + booking.reg_2 : '' }}</small></div>
<div>
<div class="columns is-mobile is-multiline">
<div class="column is-narrow">
<span
@mouseenter="onItemsMouseEnter($event, booking)"
@mouseleave="onItemsMouseLeave"
@click="onItemsClick($event, booking)"
style="cursor: pointer; text-overflow: ellipsis; overflow: hidden;"
class="mr-1"
>
<small class="is-size-7">{{ formatSummaryString(booking.items) }}</small>
</span>
</div>
<div class="column is-fixed-bottom is-12">
<strong><small class="is-size-7" >{{ new Date(booking.datetime).toLocaleDateString('da-DK', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }) }}</small></strong>
</div>
</div>
</div>
</div>
</template>
<template v-slot:content>
<div>
<p v-show="!!booking.po"><strong>PO:</strong> {{ booking.po || '-' }}</p>
<p v-show="!!booking.reference"><strong>Reference:</strong> {{ booking.reference || '-' }}</p>
<strong>Ydelser:</strong>
<ul>
<li v-for="item in booking.items" :key="item.id">
{{ item.name }} <span v-if="item.quantity && item.quantity !== 1">
({{ item.quantity }} stk)
</span>
</li>
</ul>
<hr/>
<ActionSettingsWheelButton :order_booking_id="booking.id" :order_id="booking.order_id" :customer_number="booking.customer_number" :department_id="booking.department" :refresh-function="loadList" :reg_1="booking.reg_1" :reg_2="booking.reg_2" :displayActionsDirectly="true">
<template #actions/>
</ActionSettingsWheelButton>
</div>
</template>
</WhiteBoxCard>
</div>