Files
pleno-vue/src/components/models/navigation/items/NavigationMenuItemsAdmin.vue
T

549 lines
16 KiB
Vue

<script lang="ts">
import { NavigationItemProps } from "@/components/models/navigation/NavigationItem.vue";
import { defineComponent, ref, computed, watch } from "vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import {
departments_cache,
getDepartmentName,
} from "@/components/session/token/SessionUser/Objects/economicDepartments.vue";
import i18n from "@/i18n";
import {
ensureDraftTransactionCustomerLoaded,
getDraftTransactionCustomerNumber,
} from "@/composables/useDraftTransactionCustomer.js";
import { fetchDepartmentOrderBookingCounts } from "@/components/models/navigation/items/adminBookingCount.js";
import { fetchDepartmentDraftCount } from "@/components/models/navigation/items/adminDraftCount.js";
import { hasExplicitBookingCountPermission } from "@/components/models/navigation/items/bookingCountGuards.js";
import { NAVIGATION_COUNT_REFRESH_EVENT } from "@/components/models/navigation/items/navigationCountEvents.js";
import { isAccessibleVisibleNamedDepartment } from "@/services/departmentVisibility.js";
const t = (key: string) => i18n.global.t(key);
const ADMIN_DEPARTMENT_SELECTION_CLASS = "js-admin-department-selection";
const routeContext = ref({
path: window.location.pathname || "",
params: {} as Record<string, unknown>,
});
const syncAdminNavigationRoute = (route: any) => {
routeContext.value = {
path: String(route?.path ?? window.location.pathname ?? ""),
params: (route?.params ?? {}) as Record<string, unknown>,
};
};
const department_id = computed(() => {
const currentRoute = routeContext.value;
const fromParams = Number.parseInt(String(currentRoute?.params?.departmentId ?? ""), 10);
if (Number.isInteger(fromParams) && fromParams > 0) {
return fromParams;
}
const fromPath = Number.parseInt(String(currentRoute?.path?.match(/^\/admin\/(\d+)(?:\/|$)/)?.[1] ?? ""), 10);
if (Number.isInteger(fromPath) && fromPath > 0) {
return fromPath;
}
return undefined;
});
const firstToUpperCase = (str: string) => {
if (!str) return "";
return str.charAt(0).toUpperCase() + str.slice(1);
};
const department_booking_counts = ref({
past: 0,
current: 0,
future: 0,
});
const department_booking_counts_loading = ref(false);
const department_draft_count = ref(0);
const department_draft_count_loading = ref(false);
const draftTransactionCustomerNumber = computed(() => getDraftTransactionCustomerNumber());
const canUseAdminNavigationCountsValue = computed(() => canUseAdminNavigationCounts());
let bookingCountRequestId = 0;
let draftCountRequestId = 0;
let bookingCountFetchInFlight = false;
let draftCountFetchInFlight = false;
let bookingCountRefreshQueued = false;
let draftCountRefreshQueued = false;
let queuedBookingLoadingIndicator = false;
let queuedDraftLoadingIndicator = false;
const getDepartmentIdNumber = () => Number.parseInt(String(department_id.value), 10);
const canUseAdminNavigationCounts = () => SessionUser.canAccessAdmin();
const canUseAdminBookingNavigationCounts = () =>
canUseAdminNavigationCounts() && hasExplicitBookingCountPermission(SessionUser);
const getDepartmentById = (id: number) => {
return departments_cache.value?.find((department: any) => Number(department?.id) === Number(id)) || null;
};
const hasValidDepartmentId = computed(() => {
const id = getDepartmentIdNumber();
return Number.isInteger(id) && id > 0;
});
const getBookingsLoadingBadge = () => ({
type: "info" as const,
text: "",
condition: true,
classes: ["desktop-buefy-badge-loading"],
testId: "desktop-buefy-nav-bookings-badge-loading",
tooltip: "Indlæser bookinger",
tooltipTestId: "desktop-buefy-nav-bookings-badge-loading-tooltip",
});
const getDepartmentBookingBadges = () => {
if (department_booking_counts_loading.value) {
return [getBookingsLoadingBadge()];
}
return [
{
type: "danger" as const,
text: department_booking_counts.value.past.toString(),
condition: department_booking_counts.value.past > 0,
testId: "desktop-buefy-nav-bookings-badge-overdue",
tooltip: "Tidligere",
tooltipTestId: "desktop-buefy-nav-bookings-badge-overdue-tooltip",
},
{
type: "info" as const,
text: department_booking_counts.value.current.toString(),
condition: department_booking_counts.value.current > 0,
testId: "desktop-buefy-nav-bookings-badge",
tooltip: "I dag",
tooltipTestId: "desktop-buefy-nav-bookings-badge-tooltip",
},
{
type: "info" as const,
text: department_booking_counts.value.future.toString(),
condition: department_booking_counts.value.future > 0,
classes: ["desktop-buefy-bookings-badge-future"],
testId: "desktop-buefy-nav-bookings-badge-future",
tooltip: "Fremtidige",
tooltipTestId: "desktop-buefy-nav-bookings-badge-future-tooltip",
},
];
};
const getDraftsLoadingBadge = () => ({
type: "info" as const,
text: "",
condition: true,
classes: ["desktop-buefy-badge-loading"],
testId: "desktop-buefy-nav-drafts-badge-loading",
tooltip: "Indlæser kladder",
tooltipTestId: "desktop-buefy-nav-drafts-badge-loading-tooltip",
});
const getDepartmentDraftBadge = () => {
if (department_draft_count_loading.value) {
return getDraftsLoadingBadge();
}
return {
type: "info" as const,
text: department_draft_count.value.toString(),
condition: department_draft_count.value > 0,
testId: "desktop-buefy-nav-drafts-badge",
tooltip: t("nav.drafts"),
tooltipTestId: "desktop-buefy-nav-drafts-badge-tooltip",
};
};
const fetchDepartmentBookingCount = async ({ showLoadingIndicator = false } = {}) => {
if (!canUseAdminBookingNavigationCounts()) {
department_booking_counts.value = {
past: 0,
current: 0,
future: 0,
};
department_booking_counts_loading.value = false;
bookingCountFetchInFlight = false;
bookingCountRefreshQueued = false;
queuedBookingLoadingIndicator = false;
return;
}
if (!hasValidDepartmentId.value) {
department_booking_counts.value = {
past: 0,
current: 0,
future: 0,
};
department_booking_counts_loading.value = false;
bookingCountFetchInFlight = false;
bookingCountRefreshQueued = false;
queuedBookingLoadingIndicator = false;
return;
}
if (bookingCountFetchInFlight) {
bookingCountRefreshQueued = true;
queuedBookingLoadingIndicator = queuedBookingLoadingIndicator || showLoadingIndicator;
return;
}
if (showLoadingIndicator) {
department_booking_counts_loading.value = true;
department_booking_counts.value = {
past: 0,
current: 0,
future: 0,
};
}
bookingCountFetchInFlight = true;
const requestId = ++bookingCountRequestId;
const id = getDepartmentIdNumber();
let didApply = false;
try {
const nextBookingCounts = await fetchDepartmentOrderBookingCounts({
departmentId: id,
});
if (requestId !== bookingCountRequestId || id !== getDepartmentIdNumber()) {
return;
}
department_booking_counts.value = nextBookingCounts;
department_booking_counts_loading.value = false;
didApply = true;
} finally {
bookingCountFetchInFlight = false;
const shouldRerun = bookingCountRefreshQueued;
const shouldShowLoadingIndicator = queuedBookingLoadingIndicator;
bookingCountRefreshQueued = false;
queuedBookingLoadingIndicator = false;
if (shouldRerun) {
void fetchDepartmentBookingCount({
showLoadingIndicator: shouldShowLoadingIndicator,
});
return;
}
if (!didApply) {
department_booking_counts_loading.value = false;
}
}
};
const fetchCurrentDepartmentDraftCount = async ({ showLoadingIndicator = false } = {}) => {
if (!hasValidDepartmentId.value || draftTransactionCustomerNumber.value === null) {
department_draft_count.value = 0;
department_draft_count_loading.value = false;
draftCountFetchInFlight = false;
draftCountRefreshQueued = false;
queuedDraftLoadingIndicator = false;
return;
}
if (draftCountFetchInFlight) {
draftCountRefreshQueued = true;
queuedDraftLoadingIndicator = queuedDraftLoadingIndicator || showLoadingIndicator;
return;
}
if (showLoadingIndicator) {
department_draft_count_loading.value = true;
department_draft_count.value = 0;
}
draftCountFetchInFlight = true;
const requestId = ++draftCountRequestId;
const currentDepartmentId = getDepartmentIdNumber();
const currentCustomerNumber = draftTransactionCustomerNumber.value;
let didApply = false;
try {
const nextDraftCount = await fetchDepartmentDraftCount({
departmentId: currentDepartmentId,
customerNumber: currentCustomerNumber,
});
if (
requestId !== draftCountRequestId ||
currentDepartmentId !== getDepartmentIdNumber() ||
currentCustomerNumber !== draftTransactionCustomerNumber.value
) {
return;
}
department_draft_count.value = nextDraftCount;
department_draft_count_loading.value = false;
didApply = true;
} finally {
draftCountFetchInFlight = false;
const shouldRerun = draftCountRefreshQueued;
const shouldShowLoadingIndicator = queuedDraftLoadingIndicator;
draftCountRefreshQueued = false;
queuedDraftLoadingIndicator = false;
if (shouldRerun) {
void fetchCurrentDepartmentDraftCount({
showLoadingIndicator: shouldShowLoadingIndicator,
});
return;
}
if (!didApply) {
department_draft_count_loading.value = false;
}
}
};
const refreshNavigationCounts = ({ showBookingLoadingIndicator = false, showDraftLoadingIndicator = false } = {}) => {
if (!canUseAdminNavigationCounts()) {
bookingCountRequestId += 1;
draftCountRequestId += 1;
department_booking_counts.value = {
past: 0,
current: 0,
future: 0,
};
department_booking_counts_loading.value = false;
department_draft_count.value = 0;
department_draft_count_loading.value = false;
bookingCountFetchInFlight = false;
draftCountFetchInFlight = false;
bookingCountRefreshQueued = false;
draftCountRefreshQueued = false;
queuedBookingLoadingIndicator = false;
queuedDraftLoadingIndicator = false;
return;
}
if (!hasValidDepartmentId.value) {
department_booking_counts.value = {
past: 0,
current: 0,
future: 0,
};
department_booking_counts_loading.value = false;
department_draft_count.value = 0;
department_draft_count_loading.value = false;
bookingCountFetchInFlight = false;
draftCountFetchInFlight = false;
bookingCountRefreshQueued = false;
draftCountRefreshQueued = false;
queuedBookingLoadingIndicator = false;
queuedDraftLoadingIndicator = false;
return;
}
void fetchDepartmentBookingCount({
showLoadingIndicator: showBookingLoadingIndicator,
});
void fetchCurrentDepartmentDraftCount({
showLoadingIndicator: showDraftLoadingIndicator,
});
};
const isDepartmentSet = computed(() => {
return department_id.value !== "default" && parseInt(department_id.value) > 0;
});
watch(
[department_id, canUseAdminNavigationCountsValue],
() => {
refreshNavigationCounts({
showBookingLoadingIndicator: true,
showDraftLoadingIndicator: true,
});
},
{ immediate: true }
);
watch(draftTransactionCustomerNumber, () => {
void fetchCurrentDepartmentDraftCount({
showLoadingIndicator: true,
});
});
void ensureDraftTransactionCustomerLoaded();
if (typeof window !== "undefined") {
window.addEventListener(NAVIGATION_COUNT_REFRESH_EVENT, refreshNavigationCounts);
}
// Fetch the booking count every 5 seconds when department context is valid
setInterval(() => {
if (canUseAdminNavigationCounts() && hasValidDepartmentId.value) {
refreshNavigationCounts();
}
}, 5000);
const items = computed<NavigationItemProps[]>(() => [
{
label: t("nav.overview"),
to: `/admin`,
permissions: ["admin"],
},
// Select available department options
{
label: t("nav.select_department"),
to: `/admin/departments`,
type: "category",
hidden: isDepartmentSet.value,
classes: [ADMIN_DEPARTMENT_SELECTION_CLASS],
permissions: ["admin", "list_departments"],
children: SessionUser.functions
.getAccessibleDepartments()
.map((departmentId: any) => {
const label = getDepartmentName(departmentId);
const department = getDepartmentById(departmentId);
if (!isAccessibleVisibleNamedDepartment(department, SessionUser.canAccessAssignedDepartment)) {
return null;
}
return {
label,
to: `/admin/${department.id}`,
type: "department",
permissions: ["admin", "view_department"],
order_priority: department?.order_priority ?? department?.priority_order ?? null,
priority_order: department?.priority_order ?? department?.order_priority ?? null,
};
})
.filter(Boolean) as NavigationItemProps[],
},
// Kassesystem
{
label: t("nav.pos_system"),
type: "category",
hidden: !isDepartmentSet.value,
permissions: ["admin", "add_order"],
children: [
{
label: t("nav.create_transaction"),
to: `/admin/${department_id.value}/modules/pos`,
type: "department",
permissions: ["admin", "add_order"],
},
{
label: t("nav.wash_log"),
to: `/admin/${department_id.value}/modules/pos/orders`,
type: "department",
permissions: ["admin", "list_orders"],
},
{
label: t("nav.drafts"),
to: `/admin/${department_id.value}/modules/pos/drafts`,
type: "department",
permissions: ["admin", "list_orders"],
hidden: draftTransactionCustomerNumber.value === null,
badge: getDepartmentDraftBadge(),
},
],
},
// Tidsbookinger
/**
{
label: 'Tidsbookinger',
type: 'category',
permissions: ['admin', 'department_timebookings_entries_get'],
children: [
{
label: 'Opret tidsbooking',
to: `/admin/${department_id.value}/modules/time-bookings/new`,
type: 'department',
permissions: ['admin', 'department_timebookings_create'],
},
{
label: 'Tidsbookinger',
to: `/admin/${department_id.value}/modules/time-bookings`,
type: 'department',
permissions: ['admin', 'department_timebookings_entries_get'],
},
{
label: 'Åbningstider',
to: `/admin/${department_id.value}/modules/time-bookings/opening-hours`,
type: 'department',
permissions: ['admin', 'department_timebookings_opening_hours_get'],
},
{
label: 'Vasketyper',
to: `/admin/${department_id.value}/modules/time-bookings/types`,
type: 'department',
permissions: ['admin', 'department_timebookings_types_get'],
},
],
}, */
// Bookinger
{
label: t("nav.bookings"),
to: `/admin/${department_id.value}/modules/bookings`,
type: "department",
permissions: ["admin", "list_bookings"],
hidden: !isDepartmentSet.value,
badges: getDepartmentBookingBadges(),
},
// Dagsopgørelse
{
label: t("nav.daily_report"),
to: `/admin/${department_id.value}/modules/daily-report`,
type: "department",
permissions: ["admin", "list_department_daily_reports"],
hidden: !isDepartmentSet.value,
},
// Selvvask
{
label: t("nav.self_wash"),
to: `/admin/${department_id.value}/modules/wash-lanes`,
type: "department",
permissions: ["admin", "list_department_wash_lanes"],
hidden: !isDepartmentSet.value,
},
// Notifikationer
{
label: t("nav.notifications"),
to: `/admin/${department_id.value}/modules/notifications`,
type: "department",
permissions: ["admin", "department_notification_sms_get"],
hidden: !isDepartmentSet.value,
},
// Intranet
/**
{
label: 'Intranet',
to: `/admin/${department_id.value}/modules/intranet`,
type: 'department',
permissions: ['admin', 'list_intranet_posts'],
}*/
]);
const isDepartment = computed(() => department_id.value !== "default" && parseInt(department_id.value) > 0);
const parsedItems = () => {
// If the current route is not department-specific, filter out department items
if (!isDepartment.value) {
return items.value.filter((item) => item.type !== "department");
}
// Otherwise, return all items
return items.value;
};
export default defineComponent({
name: "NavigationMenuItemsAdmin",
setup() {
return {
items,
parsedItems,
};
},
});
// Named exports if needed
export { ADMIN_DEPARTMENT_SELECTION_CLASS, syncAdminNavigationRoute, items, parsedItems };
</script>
<template>
<!-- Add a template section even if empty -->
</template>