"Refactor OrderBookingsTable.vue: simplify table logic by removing unnecessary filters and pagination handling, migrate pagination to OrderBookingsPagination.vue, and update components for improved code maintainability and usability."

This commit is contained in:
Jeppe Bundgaard
2025-12-10 14:01:24 +01:00
parent b34bb0b0dc
commit 2314f02239
7 changed files with 275 additions and 397 deletions
@@ -363,6 +363,17 @@ const defaultActions = computed(() => {
},
disabled: false,
},
{
icon: 'fas fa-download',
label: 'Download faktura',
clickAction: () => {
return SessionUser.objects.collectedOrderInvoices.functions.download(props.invoice_collection_id);
},
showFunction: () => {
return true;
},
disabled: false,
},
{
icon: 'fas fa-trash-alt',
label: 'Slet ordre',
@@ -739,6 +750,19 @@ const defaultActions = computed(() => {
</template>
</template>
</template>
<!-- Collected order invoice actions -->
<template v-if="props.invoice_collection_id">
<ActionSettingsWheelItemLabel
:label="SessionUser.objects.collectedOrderInvoices.meta.title"
v-show="props.invoice_collection_id"
/>
<!-- Download invoice -->
<ActionSettingsWheelItem
label="Download faktura"
icon="fas fa-download"
:click-action="() => SessionUser.objects.collectedOrderInvoices.functions.download(props.invoice_collection_id)"
/>
</template>
</template>
<!-- If no actions are defined, then show that no actions are defined -->
<template v-else>
@@ -0,0 +1,207 @@
<script setup>
import {
list,
loadList,
setEndpoint,
setFilter,
} from "@/components/pagination/paginatedList.vue";
import {SessionUser} from "@/components/session/token/SessionUser.vue";
import { useRouter } from "vue-router";
import { defineProps, onMounted, ref} from "vue";
import TableLabeledPagination from "@/components/displays/pagination/TableLabeledPagination.vue";
import OrderBookingsTable from "@/views/dashboards/userDashboard/bookings/displays/tables/OrderBookingsTable.vue";
import { departments, getDepartments } from "@/components/pagination/departmentTabs.vue";
import { Colors } from "@/ThemeConfig.vue";
/**
* Router
*/
const router = useRouter();
/**
* Props
*/
const props = defineProps({
filters: {
type: Object,
default: () => ({}),
required: false
},
})
const orderIdFilter = ref('*');
const onOrderIdFilterChange = (event) => {
const val = event.target.value;
orderIdFilter.value = val;
// Apply filter: '*' clears, 'is null' shows without order, 'not null' shows with order
setFilter('order_id', val);
};
// Department filter
const departmentFilter = ref('*');
const onDepartmentFilterChange = (event) => {
const val = event.target.value;
departmentFilter.value = val;
setFilter('department', val);
};
// Only today filter
const onlyTodayFilter = ref('*');
const onOnlyTodayFilterChange = (event, autoLoadList = true) => {
const val = event.target.value;
onlyTodayFilter.value = val;
if (val === '*') {
setFilter('datetime', val, false); // Clear filter
setFilter('datetime-date_from', null, false);
setFilter('datetime-date_to', null, false);
} else {
const startOfDay = new Date().setHours(0, 0, 0, 0);
const endOfDay = new Date().setHours(23, 59, 59, 999);
setFilter('datetime', null, false);
setFilter('datetime-date_from', new Date(startOfDay).toISOString(), false);
setFilter('datetime-date_to', new Date(endOfDay).toISOString(), false);
}
if (autoLoadList) {
loadList();
}
};
// Version selector + helpers
const versionSelector = ref('new');
const resetVersionToNew = () => {
versionSelector.value = 'new';
};
const showLegacyOrderBookingsPortal = () => {
const departmentId = SessionUser.functions.getDepartmentIdFromUrl();
if (!departmentId) {
SessionUser.functions.redirectTo.user('/bookings-legacy', true);
return;
}
SessionUser.functions.redirectTo.department(SessionUser.functions.getDepartmentIdFromUrl(), 'modules/bookings-legacy', true);
};
// Filters
onMounted(() => {
setEndpoint(SessionUser.objects.order_bookings.meta.endpoint, false);
for (const [key, value] of Object.entries(props.filters)) {
let setFilterKey = true;
// Also set the filter controls if applicable
if (key === 'order_id') {
orderIdFilter.value = value;
} else if (key === 'department') {
departmentFilter.value = value;
} else if (key === 'only_today' && value === true) {
setFilterKey = false; // Since the only_today filter is handled separately
onOnlyTodayFilterChange({ target: { value: new Date().toISOString().split('T')[0] } }, false);
}
if (setFilterKey) {
setFilter(key, value, false);
}
}
loadList();
// Load departments for filter options
getDepartments().catch(() => {});
});
</script>
<template>
<div>
<TableLabeledPagination label="Oversigt over bookinger">
<template #paginationDisplayFiltersElement>
<!-- Status filter -->
<div class="column is-narrow">
<div class="field mb-0">
<label class="label is-small">Status</label>
<div class="control">
<div class="select">
<select :value="orderIdFilter" @change="onOrderIdFilterChange">
<option value="*">Alle</option>
<option value="is null">Ikke udført</option>
<option value="not null">Udført</option>
</select>
</div>
</div>
</div>
</div>
<!-- Department filter -->
<div class="column is-narrow">
<div class="field mb-0">
<label class="label is-small">Afdeling</label>
<div class="control">
<div class="select">
<select :value="departmentFilter" @change="onDepartmentFilterChange">
<option value="*">Alle</option>
<option v-for="department in departments" :key="department.id" :value="department.id">{{ department.name }}</option>
</select>
</div>
</div>
</div>
</div>
<!-- Version selector -->
<div class="column is-narrow">
<div class="field mb-0">
<label class="label is-small">Version</label>
<div class="control">
<div class="select">
<select @change="showLegacyOrderBookingsPortal(); resetVersionToNew()" v-model="versionSelector">
<option value="new">Ny</option>
<option value="legacy">Gammel</option>
</select>
</div>
</div>
</div>
</div>
<!-- Only today filter -->
<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>
</template>
<template #leftPaginationColumns>
</template>
<template #rightPaginationColumns>
<!-- Toggles and actions depending on route -->
<!-- User: Only today switch + New booking button -->
<div class="column is-narrow my-3" v-if="router.currentRoute.value.path.startsWith('/user')">
<label class="label is-small">Vis kun dagens bookinger</label>
<div class="field">
<input id="today" type="checkbox" class="switch is-rounded" @change="(event) => { onOnlyTodayFilterChange({ target: { value: event.target.checked ? new Date().toISOString().split('T')[0] : '*' } }) }" :class="{ 'is-link': onlyTodayFilter !== '*' }" :checked="onlyTodayFilter !== '*'" />
<label for="today"></label>
</div>
</div>
<div class="column is-narrow my-3" v-if="router.currentRoute.value.path.startsWith('/user')">
<button
class="button is-link"
@click="SessionUser.functions.redirectTo.external('/user/bookings/new', true)"
:style="{ 'color': Colors.buttons.textColor, 'background-color': Colors.buttons.backgroundColor }"
>
Ny booking
</button>
</div>
<!-- Admin: pending + only today combined switch -->
<div class="column is-narrow my-3" v-if="router.currentRoute.value.path.startsWith('/admin')">
<label class="label is-small">Vis kun dagens afventende</label>
<div class="field">
<input id="today-pending" type="checkbox" class="switch is-rounded" @change="(event) => { onOnlyTodayFilterChange({ target: { value: event.target.checked ? new Date().toISOString().split('T')[0] : '*' } }, false); onOrderIdFilterChange({ target: { value: event.target.checked ? 'is null' : '*' } }); }" :class="{ 'is-link': onlyTodayFilter !== '*' && orderIdFilter === 'is null' }" :checked="onlyTodayFilter !== '*' && orderIdFilter === 'is null'" />
<label for="today-pending"></label>
</div>
</div>
</template>
<template #default>
<OrderBookingsTable :objects="list" />
</template>
</TableLabeledPagination>
</div>
</template>
<style scoped>
</style>
@@ -13,6 +13,7 @@ import { ref } from 'vue';
import { departments, getDepartments, isLoading, getDepartmentName} from "@/components/pagination/departmentTabs.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import ColorIndicator from "@/components/displays/buttons/ColorIndicator.vue";
import ActionSettingsWheelButton from "@/components/displays/buttons/ActionSettingsWheelButton.vue";
const su_object = SessionUser.objects.collectedOrderInvoices;
@@ -115,6 +116,7 @@ const dropdown_content = (object) => {
<th>{{su_object.columns.created_at.label}}</th>
<th>{{su_object.columns.po_number.label}}</th>
<th>{{SessionUser.objects.global.language.total}}</th>
<th><!-- Actions --></th>
</tr>
</thead>
<tbody>
@@ -136,6 +138,13 @@ const dropdown_content = (object) => {
:permission-check-function="() => canUserEditObject(object)"
/>
<td>{{ SessionUser.functions.currency.toLocal(object.total_net_amount) }}</td>
<td>
<ActionSettingsWheelButton
:invoice_collection_id="object.id"
>
<template #actions></template>
</ActionSettingsWheelButton>
</td>
</tr>
</template>
</tbody>
@@ -254,6 +254,24 @@ export const CollectedOrderInvoices = {
}
},
functions: {
download: async (id) => {
// Send the request
return authenticatedRequest('/invoices/pdf', 'GET', {
id: parseInt(id)
}).then((response) => {
//console.warn(response);
// Create a blob from the response
window.open(response.data.data.url, '_blank');
return response;
}).catch((error) => {
Swal.fire({
title: 'Der skete en fejl under hentning af PDF',
text: SessionUser.functions.parseErrorMessage(error),
icon: 'error'
});
throw error;
});
},
split_invoice: async (id) => {
// Send the request
return authenticatedRequest('/collected-invoices/split', 'POST', {
@@ -1,10 +1,9 @@
<script setup>
import RestrictedPageWrapper from "@/components/page/wrappers/RestrictedPageWrapper.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import BookingsPagination from "@/components/displays/pagination/models/UserDashboard/BookingsPagination.vue";
import DepartmentDashboardPageWrapper from "@/views/dashboards/departmentDashboard/DepartmentDashboardPageWrapper.vue";
import NotFoundFallBackPageWrapper from "@/components/page/wrappers/NotFoundFallBackPageWrapper.vue";
import OrderBookingsTable from "@/views/dashboards/userDashboard/bookings/displays/tables/OrderBookingsTable.vue";
import OrderBookingsPagination from "@/components/displays/pagination/models/UserDashboard/OrderBookingsPagination.vue";
</script>
<template>
@@ -14,8 +13,7 @@ import OrderBookingsTable from "@/views/dashboards/userDashboard/bookings/displa
subtitle="Oversigt over bookinger"
>
<NotFoundFallBackPageWrapper :exists="SessionUser.functions.getDepartmentIdFromUrl() && SessionUser.canAccessDepartment(SessionUser.functions.getDepartmentIdFromUrl())" error="Please select a department, that you have access to. If you believe this is an error, please contact support.">
<OrderBookingsTable :filters="{ department: SessionUser.functions.getDepartmentIdFromUrl(), order_id: 'is null', only_today: true }"/>
<!--<BookingsPagination :filters="{ department: SessionUser.functions.getDepartmentIdFromUrl() }" v-else/> -->
<OrderBookingsPagination :filters="{ department: SessionUser.functions.getDepartmentIdFromUrl(), order_id: 'is null', only_today: true }"/>
</NotFoundFallBackPageWrapper>
</DepartmentDashboardPageWrapper>
</RestrictedPageWrapper>
@@ -1,15 +1,13 @@
<script setup>
import BookingsPagination from "@/components/displays/pagination/models/UserDashboard/BookingsPagination.vue";
import UserDashboardPageWrapper from "@/views/dashboards/userDashboard/UserDashboardPageWrapper.vue";
import OrderBookingsTable from "@/views/dashboards/userDashboard/bookings/displays/tables/OrderBookingsTable.vue";
import OrderBookingsPagination from "@/components/displays/pagination/models/UserDashboard/OrderBookingsPagination.vue";
</script>
<template>
<div>
<UserDashboardPageWrapper title="Mine bookinger" subtitle="Se dine bookinger">
<!--<BookingsPagination/>-->
<OrderBookingsTable :filters="{ only_today: true }"/>
<OrderBookingsPagination :filters="{ only_today: true }"/>
<!-- TODO: Complete implementation! -->
</UserDashboardPageWrapper>
</div>
@@ -1,105 +1,23 @@
<script setup>
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import {onMounted, ref, defineProps, computed, reactive} from "vue";
import { getDepartmentName, departments, getDepartments } from "@/components/pagination/departmentTabs.vue";
import { ref, defineProps, computed, reactive } from "vue";
import { getDepartmentName } from "@/components/pagination/departmentTabs.vue";
import EditableTableColumn from "@/components/displays/buttons/EditableTableColumn.vue";
import ActionSettingsWheelButton from "@/components/displays/buttons/ActionSettingsWheelButton.vue";
import {
list,
isLoading,
setEndpoint,
loadList,
metaCurrentPage,
metaItemsPerPage,
metaTotalItems,
setMetaItemsPerPage,
setPage,
search,
metaSearch,
setFilter
} from "@/components/pagination/paginatedList.vue";
import PaginationDisplay from "@/components/displays/pagination/PaginationDisplay.vue";
import PaginationNavigation from "@/components/displays/pagination/PaginationNavigation.vue";
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
import { loadList } from "@/components/pagination/paginatedList.vue";
import { showPopper, removePopperIfOpen, popperBox } from "@/components/displays/PopperDefault.vue";
import ViewportResponsiveWrapper from "@/components/viewport/conditions/elements/ViewportResponsiveWrapper.vue";
import WhiteBoxCard from "@/components/displays/boxes/WhiteBoxCard.vue";
import { useRouter } from "vue-router";
import { Colors } from "@/ThemeConfig.vue";
import PaginationDisplayGeneralSearchReload
from "@/components/displays/pagination/PaginationDisplayGeneralSearchReload.vue";
/**
* Router
*/
const router = useRouter();
/**
* Props
*/
const props = defineProps({
filters: {
type: Object,
default: () => ({}),
required: false
objects: {
type: Array,
required: true,
default: () => [],
}
})
const orderIdFilter = ref('*');
const onOrderIdFilterChange = (event) => {
const val = event.target.value;
orderIdFilter.value = val;
// Apply filter: '*' clears, 'is null' shows without order, 'not null' shows with order
setFilter('order_id', val);
};
// Department filter
const departmentFilter = ref('*');
const onDepartmentFilterChange = (event) => {
const val = event.target.value;
departmentFilter.value = val;
setFilter('department', val);
};
// Only today filter
const onlyTodayFilter = ref('*');
const onOnlyTodayFilterChange = (event, autoLoadList = true) => {
const val = event.target.value;
onlyTodayFilter.value = val;
if (val === '*') {
setFilter('datetime', val, false); // Clear filter
setFilter('datetime-date_from', null, false);
setFilter('datetime-date_to', null, false);
} else {
const startOfDay = new Date().setHours(0, 0, 0, 0);
const endOfDay = new Date().setHours(23, 59, 59, 999);
setFilter('datetime', null, false);
setFilter('datetime-date_from', new Date(startOfDay).toISOString(), false);
setFilter('datetime-date_to', new Date(endOfDay).toISOString(), false);
}
if (autoLoadList) {
loadList();
}
};
onMounted(() => {
setEndpoint("/order-bookings", false);
for (const [key, value] of Object.entries(props.filters)) {
let setFilterKey = true;
// Also set the filter controls if applicable
if (key === 'order_id') {
orderIdFilter.value = value;
} else if (key === 'department') {
departmentFilter.value = value;
} else if (key === 'only_today' && value === true) {
setFilterKey = false; // Since the only_today filter is handled separately
onOnlyTodayFilterChange({ target: { value: new Date().toISOString().split('T')[0] } }, false);
}
if (setFilterKey) {
setFilter(key, value, false);
}
}
loadList();
// Load departments for filter options
getDepartments().catch(() => {});
});
/**
* This function will return a string that tells how long until the given time
@@ -219,7 +137,7 @@ const canEditBooking = (bookingobj) => {
}
const sortedList = computed(() => {
return list.value.slice().sort((a, b) => {
return (props.objects || []).slice().sort((a, b) => {
const aDate = new Date(a.datetime);
const bDate = new Date(b.datetime);
return aDate - bDate;
@@ -258,58 +176,7 @@ const getColspan = () => {
return colspan;
}
const showLegacyOrderBookingsPortal = () => {
const departmentId = SessionUser.functions.getDepartmentIdFromUrl();
if (!departmentId) {
SessionUser.functions.redirectTo.user('/bookings-legacy', true);
return;
}
SessionUser.functions.redirectTo.department(SessionUser.functions.getDepartmentIdFromUrl(), 'modules/bookings-legacy', true);
}
// If the screen is mobile, set the is small variable to true
const isSmall = ref(window.innerWidth < 1024);
/**
* Switch between only today and all bookings
*/
const switchOnlyToday = (event) => {
onOnlyTodayFilterChange({ target: { value: event.target.checked ? new Date().toISOString().split('T')[0] : '*' } });
}
const switchOnlyPending = (event) => {
onOrderIdFilterChange({ target: { value: event.target.checked ? 'is null' : '*' } });
}
const switchOnlyPendingAndOnlyToday = (event) => {
onOnlyTodayFilterChange({ target: { value: event.target.checked ? new Date().toISOString().split('T')[0] : '*' } }, false);
onOrderIdFilterChange({ target: { value: event.target.checked ? 'is null' : '*' } }, false);
loadList();
}
const isFilterApplied = () => {
if (!router.currentRoute.value.path.startsWith('/admin')) {
if (departmentFilter.value !== '*') {
return true;
}
}
return orderIdFilter.value !== '*' || onlyTodayFilter.value !== '*';
}
const showAllBookings = () => {
if (!router.currentRoute.value.path.startsWith('/admin')) {
departmentFilter.value = '*';
setFilter('department', '*', false);
}
switchOnlyToday({ target: { checked: false } }, false);
switchOnlyPending({ target: { checked: false } }, false);
loadList();
}
const versionSelector = ref('new');
const resetVersionToNew = () => {
versionSelector.value = 'new';
};
// No pagination or filter handling here. This component is presentational only.
</script>
@@ -317,95 +184,6 @@ const resetVersionToNew = () => {
<div>
<ViewportResponsiveWrapper>
<template #desktop>
<div class="mb-3">
<PaginationDisplayGeneralSearchReload/>
<!-- Second row: Amount per page, Filters -->
<div class="columns is-vcentered is-multiline">
<div class="column is-narrow">
<PaginationDisplay :metaItemsPerPage="metaItemsPerPage" :loadFunction="loadList" :isLoading="isLoading" :setMetaItemsPerPage="setMetaItemsPerPage" />
</div>
<div class="column is-narrow">
<div class="field mb-0">
<label class="label is-small">Status</label>
<div class="control">
<div class="select">
<select :value="orderIdFilter" @change="onOrderIdFilterChange">
<option value="*">Alle</option>
<option value="is null">Ikke udført</option>
<option value="not null">Udført</option>
</select>
</div>
</div>
</div>
</div>
<div class="column is-narrow">
<div class="field mb-0">
<label class="label is-small">Afdeling</label>
<div class="control">
<div class="select">
<select :value="departmentFilter" @change="onDepartmentFilterChange">
<option value="*">Alle</option>
<option v-for="department in departments" :key="department.id" :value="department.id">{{ department.name }}</option>
</select>
</div>
</div>
</div>
</div>
<div class="column is-narrow">
<div class="field mb-0">
<label class="label is-small">Version</label>
<div class="control">
<div class="select">
<select @change="showLegacyOrderBookingsPortal(); resetVersionToNew()" v-model="versionSelector">
<option value="new">Ny</option>
<option value="legacy">Gammel</option>
</select>
</div>
</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 class="column is-auto-fill my-3" v-if="!isSmall"/>
<!-- Only show the bookings for today (Switch, if the route is /user) -->
<div class="column is-narrow my-3" v-if="router.currentRoute.value.path.startsWith('/user')" :class="{ 'has-text-right': !isSmall }">
<label class="label is-small">{{ isSmall ? 'I dag' : 'Vis kun dagens bookinger' }}</label>
<div class="field">
<input id="today" type="checkbox" class="switch is-rounded" @change="(event) => { switchOnlyToday(event); }" :class="{ 'is-link': onlyTodayFilter !== '*' }" :checked="onlyTodayFilter !== '*'" />
<label for="today"></label>
</div>
</div>
<!-- Create a new booking, if the route is /user -->
<div class="column is-narrow my-3" v-if="router.currentRoute.value.path.startsWith('/user')" :class="{ 'has-text-right': !isSmall }">
<button
class="button is-link"
@click="SessionUser.functions.redirectTo.external('/user/bookings/new', true)"
:style="{ 'color': Colors.buttons.textColor, 'background-color': Colors.buttons.backgroundColor }"
>
Ny booking
</button>
</div>
<!-- Only show the pending bookings (Switch, if the route is /admin) -->
<div class="column is-narrow my-3" v-if="router.currentRoute.value.path.startsWith('/admin')" :class="{ 'has-text-right': !isSmall }">
<label class="label">{{ isSmall ? 'Afventende' : 'Vis kun dagens afventende' }}</label>
<div class="field">
<input id="today" type="checkbox" class="switch is-rounded" @change="(event) => { switchOnlyPendingAndOnlyToday(event); }" :class="{ 'is-link': onlyTodayFilter !== '*' && orderIdFilter === 'is null' }" :checked="onlyTodayFilter !== '*' && orderIdFilter === 'is null'" />
<label for="today"></label>
</div>
</div>
</div>
</div>
<div>
<table class="table is-fullwidth is-hoverable is-striped"
style="table-layout: fixed;">
@@ -420,14 +198,9 @@ const resetVersionToNew = () => {
<th>Ydelser</th>
<th><!-- Actions --></th>
</tr>
<template v-if="list.length === 0 && !isLoading">
<template v-if="(objects || []).length === 0">
<tr>
<template v-if="isFilterApplied()">
<td :colspan="getColspan()" class="has-text-centered">Ingen bookinger fundet med de valgte filtre.</td>
</template>
<template v-else>
<td :colspan="getColspan()" class="has-text-centered">Ingen bookinger fundet.</td>
</template>
<td :colspan="getColspan()" class="has-text-centered">Ingen bookinger fundet.</td>
</tr>
</template>
<template v-else>
@@ -596,145 +369,14 @@ const resetVersionToNew = () => {
</tr>
</template>
</template>
<!-- Show all row if there is any filtering applied -->
<tr v-if="isFilterApplied()">
<td :colspan="getColspan()" class="has-text-centered">
<button class="button is-small is-light" @click="showAllBookings()">
<span>
Vis alle bookinger
</span>
</button>
</td>
</tr>
</table>
<PaginationNavigation :currentPage="metaCurrentPage" :totalPages="Math.ceil(metaTotalItems / metaItemsPerPage)" :loadFunction="loadList" :setPage="setPage" :isLoading="isLoading" />
</div>
</template>
<template #mobile>
<div class="columns is-multiline is-mobile">
<!-- Search -->
<div class="column">
<input
class="input"
type="text"
placeholder="Søg..."
v-model="metaSearch"
@input="search($event.target.value)"
/>
</div>
<!-- Reload button -->
<div class="column is-narrow">
<LoadButtonWhileAwait class="is-dark is-fullwidth" :isLoading="isLoading" :loadFunction="loadList" icon="fas fa-sync-alt">Reload</LoadButtonWhileAwait>
</div>
<!-- Filters -->
<div class="column is-full">
<PaginationDisplay :metaItemsPerPage="metaItemsPerPage" :loadFunction="loadList" :isLoading="isLoading" :setMetaItemsPerPage="setMetaItemsPerPage" :allowSmallScreen="true">
<template #paginationColumns>
<!-- Version selector (align with desktop) -->
<div class="column is-narrow">
<div class="field mb-0">
<label class="label is-small">Version</label>
<div class="control">
<div class="select is-small">
<select @change="showLegacyOrderBookingsPortal(); resetVersionToNew()" v-model="versionSelector">
<option value="new">Ny</option>
<option value="legacy">Gammel</option>
</select>
</div>
</div>
</div>
</div>
<!-- Department filter -->
<div class="column is-narrow">
<div class="field mb-0">
<label class="label is-small">Afdeling</label>
<div class="control">
<div class="select is-small">
<select :value="departmentFilter" @change="onDepartmentFilterChange">
<option value="*">Alle</option>
<option v-for="department in departments" :key="department.id" :value="department.id">{{ department.name }}</option>
</select>
</div>
</div>
</div>
</div>
<!-- Status filter -->
<div class="column is-narrow">
<div class="field mb-0">
<label class="label is-small">Status</label>
<div class="control">
<div class="select is-small">
<select :value="orderIdFilter" @change="onOrderIdFilterChange">
<option value="*">Alle</option>
<option value="is null">Ikke udført</option>
<option value="not null">Udført</option>
</select>
</div>
</div>
</div>
</div>
<!-- Only today filter -->
<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 is-small">
<select :value="onlyTodayFilter" @change="onOnlyTodayFilterChange">
<option value="*">Alle</option>
<option :value="new Date().toISOString().split('T')[0]">Ja</option>
</select>
</div>
</div>
</div>
</div>
<!-- Mobile switches are handled below -->
<div class="column is-auto-fill is-hidden-desktop" v-if="router.currentRoute.value.path.startsWith('/user')" :class="{ 'has-text-right': !isSmall }">
<label class="label is-small">{{ isSmall ? 'I dag' : 'Vis kun dagens bookinger' }}</label>
<div class="field">
<input id="today-mobile" type="checkbox" class="switch is-rounded is-small" @change="(event) => { switchOnlyToday(event); }" :class="{ 'is-link': onlyTodayFilter !== '*' }" :checked="onlyTodayFilter !== '*'" />
<label for="today-mobile" class="label is-small"></label>
</div>
</div>
<div class="column is-auto-fill is-hidden-desktop" v-if="router.currentRoute.value.path.startsWith('/admin')" :class="{ 'has-text-right': !isSmall }">
<label class="label is-small">{{ isSmall ? 'Afventende' : 'Vis kun dagens afventende' }}</label>
<div class="field">
<input id="today-pending-mobile" type="checkbox" class="switch is-rounded is-small" @change="(event) => { switchOnlyPendingAndOnlyToday(event); }" :class="{ 'is-link': onlyTodayFilter !== '*' && orderIdFilter === 'is null' }" :checked="onlyTodayFilter !== '*' && orderIdFilter === 'is null'" />
<label for="today-pending-mobile" class="label is-small"></label>
</div>
</div>
<div class="column is-auto-fill is-hidden-desktop" v-if="router.currentRoute.value.path.startsWith('/user')" :class="{ 'has-text-right': !isSmall }">
<button
class="button is-link is-fullwidth"
@click="SessionUser.functions.redirectTo.external('/user/bookings/new', true)"
:style="{ 'color': Colors.buttons.textColor, 'background-color': Colors.buttons.backgroundColor }"
>
Ny booking
</button>
</div>
</template>
</PaginationDisplay>
</div>
<!-- Toggles (align with desktop behavior) -->
<!-- Only show the bookings for today (Switch, if the route is /user) -->
<div class="column is-narrow my-3 is-hidden-touch" v-if="router.currentRoute.value.path.startsWith('/user')" :class="{ 'has-text-right': !isSmall }">
<label class="label">{{ isSmall ? 'I dag' : 'Vis kun dagens bookinger' }}</label>
<div class="field">
<input id="today-mobile" type="checkbox" class="switch is-rounded" @change="(event) => { switchOnlyToday(event); }" :class="{ 'is-link': onlyTodayFilter !== '*' }" :checked="onlyTodayFilter !== '*'" />
<label for="today-mobile"></label>
</div>
</div>
<!-- Only show the pending bookings (Switch, if the route is /admin) -->
<div class="column is-narrow my-3 is-hidden-touch" v-if="router.currentRoute.value.path.startsWith('/admin')" :class="{ 'has-text-right': !isSmall }">
<label class="label">{{ isSmall ? 'Afventende' : 'Vis kun dagens afventende' }}</label>
<div class="field">
<input id="today-pending-mobile" type="checkbox" class="switch is-rounded" @change="(event) => { switchOnlyPendingAndOnlyToday(event); }" :class="{ 'is-link': onlyTodayFilter !== '*' && orderIdFilter === 'is null' }" :checked="onlyTodayFilter !== '*' && orderIdFilter === 'is null'" />
<label for="today-pending-mobile"></label>
</div>
</div>
<!-- Bookings -->
<div class="column is-full">
<div v-if="list.length === 0 && !isLoading">
<div v-if="(objects || []).length === 0">
<p class="has-text-centered">Ingen bookinger fundet.</p>
</div>
<template v-else>
@@ -790,17 +432,9 @@ const resetVersionToNew = () => {
<!-- Check, when completed -->
<i class="fas fa-check-circle has-text-success" v-else></i>
</span>
<small v-if="!departmentFilter || departmentFilter === '*'">
<small>
{{ 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 -->
@@ -868,16 +502,6 @@ const resetVersionToNew = () => {
</template>
</div>
</template>
<!-- Show all row if there is any filtering applied -->
<div class="column is-full" v-if="isFilterApplied()">
<div class="has-text-centered">
<button class="button is-small is-light" @click="showAllBookings()">
<span>
Vis alle bookinger
</span>
</button>
</div>
</div>
</div>
</div>
</template>