(function () {
	const doc = document.documentElement;
	const app = window.appConfig || {};
	function readStoredTheme() {
		try {
			return localStorage.getItem("theme");
		} catch (e) {
			return null;
		}
	}

	function refreshChartTheme() {
		const mode = doc.classList.contains("dark") ? "dark" : "light";
		document.querySelectorAll("[data-chart]").forEach((chartEl) => {
			const chart = chartEl && chartEl._apexChart ? chartEl._apexChart : null;
			if (!chart || typeof chart.updateOptions !== "function") return;
			chart.updateOptions(
				{
					theme: { mode },
					grid: { borderColor: "hsl(var(--border))" },
				},
				false,
				false,
				false,
			);
		});
	}

	function syncThemeToggles(mode) {
		const isDark = mode === "dark";
		document.querySelectorAll("[data-toggle-theme]").forEach((toggle) => {
			toggle.setAttribute("aria-pressed", isDark ? "true" : "false");
			toggle.setAttribute(
				"aria-label",
				isDark ? "Switch to light mode" : "Switch to dark mode",
			);
			const label = toggle.querySelector("[data-theme-label]");
			if (label) {
				label.textContent = isDark ? "Light Mode" : "Dark Mode";
			}
		});
	}

	const savedTheme = readStoredTheme();
	const initialTheme =
		savedTheme ||
		(doc.classList.contains("dark")
			? "dark"
			: app.theme === "dark"
				? "dark"
				: "light");

	function setTheme(mode, persist = true) {
		const normalized = mode === "dark" ? "dark" : "light";
		doc.classList.toggle("dark", normalized === "dark");
		doc.setAttribute("data-theme", normalized);
		doc.style.colorScheme = normalized;
		syncThemeToggles(normalized);
		refreshChartTheme();

		if (persist) {
			try {
				localStorage.setItem("theme", normalized);
			} catch (e) {
				// Ignore storage errors.
			}
		}
	}

	// Apply saved/initial theme ASAP to avoid flash (no extra write)
	setTheme(initialTheme, false);

	const erpDomReadyState = {
		domContentLoaded: document.readyState !== "loading",
		load: document.readyState === "complete",
	};
	const originalDocumentAddEventListener = document.addEventListener.bind(document);
	const originalWindowAddEventListener = window.addEventListener.bind(window);
	let uiBootstrapPending = 0;

	function beginUiBootstrap() {
		uiBootstrapPending += 1;
	}

	function endUiBootstrap() {
		if (uiBootstrapPending > 0) {
			uiBootstrapPending -= 1;
		}
	}

	function scheduleImmediateEventCallback(target, type, listener, options) {
		window.setTimeout(() => {
			if (uiBootstrapPending > 0) {
				scheduleImmediateEventCallback(target, type, listener, options);
				return;
			}
			try {
				const event = new Event(type);
				if (typeof listener === "function") {
					listener.call(target, event);
				} else if (listener && typeof listener.handleEvent === "function") {
					listener.handleEvent(event);
				}
			} catch (error) {
				console.warn(`Immediate ${type} listener failed`, error);
			}
		}, 0);
	}

	document.addEventListener = function (type, listener, options) {
		if (type === "DOMContentLoaded" && erpDomReadyState.domContentLoaded) {
			scheduleImmediateEventCallback(document, type, listener, options);
			return;
		}
		return originalDocumentAddEventListener(type, listener, options);
	};

	window.addEventListener = function (type, listener, options) {
		if (type === "load" && erpDomReadyState.load) {
			scheduleImmediateEventCallback(window, type, listener, options);
			return;
		}
		return originalWindowAddEventListener(type, listener, options);
	};

	originalDocumentAddEventListener("DOMContentLoaded", () => {
		erpDomReadyState.domContentLoaded = true;
	}, { once: true });
	originalWindowAddEventListener("load", () => {
		erpDomReadyState.load = true;
	}, { once: true });

	function bindThemeToggle() {
		const toggles = document.querySelectorAll("[data-toggle-theme]");
		if (!toggles.length) return;

		toggles.forEach((toggle) => {
			if (toggle.dataset.themeBound === "1") return;
			toggle.dataset.themeBound = "1";
			toggle.addEventListener("click", () => {
				const next = doc.classList.contains("dark") ? "light" : "dark";
				setTheme(next);
				updateIcons();
			});
		});
	}

	const legacyLucideIconMap = {
		"fa-edit": "square-pen",
		"fa-pencil": "pencil",
		"fa-pencil-square": "square-pen",
		"fa-pencil-square-o": "square-pen",
		"fa-trash": "trash-2",
		"fa-trash-o": "trash-2",
		"fa-times": "x",
		"fa-close": "x",
		"fa-times-circle": "x-circle",
		"fa-times-circle-o": "x-circle",
		"fa-plus": "plus",
		"fa-plus-circle": "plus-circle",
		"fa-plus-square-o": "square-plus",
		"fa-minus": "minus",
		"fa-check": "check",
		"fa-check-circle": "check-circle",
		"fa-check-circle-o": "check-circle",
		"fa-circle-o": "circle",
		"fa-dot-circle-o": "circle-dot",
		"fa-question-circle": "circle-help",
		"fa-warning": "triangle-alert",
		"fa-envelope": "mail",
		"fa-envelope-o": "mail",
		"fa-send": "send",
		"fa-paperclip": "paperclip",
		"fa-reply": "reply",
		"fa-chain": "link",
		"fa-unlink": "unlink",
		"fa-user": "user",
		"fa-users": "users",
		"fa-user-plus": "user-plus",
		"fa-user-secret": "user-round-cog",
		"fa-sign-out": "log-out",
		"fa-filter": "filter",
		"fa-search": "search",
		"fa-cog": "settings",
		"fa-cogs": "settings",
		"fa-calendar": "calendar",
		"fa-calendar-o": "calendar",
		"fa-calendar-plus-o": "calendar-plus",
		"fa-clock-o": "clock-3",
		"fa-ticket": "ticket",
		"fa-tasks": "list-todo",
		"fa-list-alt": "list",
		"fa-list-ul": "list",
		"list-ul": "list",
		"fa-bars": "menu",
		"fa-folder-open-o": "folder-open",
		"fa-file-text": "file-text",
		"fa-file-text-o": "file-text",
		"fa-file-pdf-o": "file-text",
		"fa-file-video-o": "file-video",
		"fa-file-excel-o": "file-spreadsheet",
		"fa-building-o": "building-2",
		"fa-shopping-cart": "shopping-cart",
		"fa-truck": "truck",
		"fa-credit-card": "credit-card",
		"fa-money": "wallet",
		"fa-usd": "badge-dollar-sign",
		"fa-retweet": "repeat",
		"fa-copy": "copy",
		"fa-barcode": "scan-barcode",
		"fa-bug": "bug",
		"cube": "box",
		"cubes": "boxes",
		"fa-rocket": "rocket",
		"fa-trophy": "trophy",
		"fa-suitcase": "briefcase",
		"fa-plane": "plane",
		"fa-laptop": "laptop",
		"fa-thumb-tack": "pin",
		"fa-map-marker": "map-pin",
		"fa-print": "printer",
		"fa-eye": "eye",
		"fa-eye-slash": "eye-off",
		"fa-download": "download",
		"fa-upload": "upload",
		"fa-hand-o-right": "hand",
		"fa-leaf": "leaf",
		"fa-server": "server",
		"fa-database": "database",
		"fa-sitemap": "workflow",
		"fa-coffee": "coffee",
		"fa-bullhorn": "megaphone",
		"fa-briefcase": "briefcase",
		"fa-globe": "globe",
		"fa-language": "languages",
		"fa-camera-retro": "camera",
		"camera-retro": "camera",
		"fa-code": "code",
		"fa-shield": "shield",
		"fa-paste": "clipboard",
		"paste": "clipboard-paste",
		"fa-tags": "tags",
		"fa-align-left": "align-left",
		"fa-list-ul": "list",
		"list-ul": "list",
		"fa-arrow-down": "arrow-down",
		"fa-arrow-circle-up": "arrow-up-circle",
		"fa-dashboard": "layout-dashboard",
		"fa-street-view": "map",
		"fa-crosshairs": "crosshair",
		"fa-paw": "paw-print",
		"fa-empire": "building",
		"fa-envira": "leaf",
		"fa-group": "users",
		"fa-plug": "plug",
		"fa-bitbucket": "folder-git-2",
		"fa-layers": "layers-3",
		"caret-right": "chevron-right",
		"caret-left": "chevron-left",
		"caret-up": "chevron-up",
		"caret-down": "chevron-down",
		"circle-o-notch": "loader-circle",
		"window-maximize": "square",
		"window-close": "x",
		"sort": "arrow-up-down",
		"sort-desc": "chevron-down",
		"sort-asc": "chevron-up",
		"fa-random": "shuffle",
		"random": "shuffle",
		"fa-dot-circle": "circle-dot",
		"fa-dot-circle-o": "circle-dot",
		"dot-circle": "circle-dot",
		"times": "x",
	};

	function resolveLegacyLucideIcon(name) {
		if (!name) return null;
		const normalized = String(name).trim().toLowerCase();
		if (legacyLucideIconMap[normalized]) {
			return legacyLucideIconMap[normalized];
		}
		if (normalized.indexOf("fa-") === 0) {
			const stripped = normalized.replace(/^fa-/, "");
			if (legacyLucideIconMap[stripped]) {
				return legacyLucideIconMap[stripped];
			}
			return stripped;
		}
		return null;
	}

	function normalizeLegacyIcons(root = document) {
		if (!root || !root.querySelectorAll) return;

		root.querySelectorAll("i, span").forEach((el) => {
			let iconName = null;
			const dataLucide = el.getAttribute("data-lucide");
			if (dataLucide) {
				iconName = resolveLegacyLucideIcon(dataLucide);
			}

			if (!iconName && el.classList && el.classList.contains("fa")) {
				for (const className of Array.from(el.classList)) {
					if (className.indexOf("fa-") !== 0) continue;
					if (/^fa-(fw|lg|\dx|spin|pulse|border|inverse|rotate-\d+|flip-(horizontal|vertical)|stack(-\dx)?|ul|li)$/.test(className)) {
						continue;
					}
					iconName = resolveLegacyLucideIcon(className);
					if (iconName) break;
				}
			}

			if (!iconName) return;

			el.setAttribute("data-lucide", iconName);
			if (el.classList) {
				Array.from(el.classList).forEach((className) => {
					if (className === "fa" || className.indexOf("fa-") === 0) {
						el.classList.remove(className);
					}
				});
			}
		});
	}

	function ensureLucideBridge() {
		if (!window.lucide || typeof window.lucide.createIcons !== "function") return;
		if (window.lucide.__erpLegacyPatched === true) return;
		const originalCreateIcons = window.lucide.createIcons.bind(window.lucide);
		window.lucide.createIcons = function (...args) {
			normalizeLegacyIcons(document);
			return originalCreateIcons(...args);
		};
		window.lucide.__erpLegacyPatched = true;
	}

	function updateIcons(root = document) {
		ensureLucideBridge();
		normalizeLegacyIcons(root);
		if (window.lucide) {
			window.lucide.createIcons();
		}
	}

	window.normalizeLegacyIcons = normalizeLegacyIcons;

		const lazyScriptPromises = {};
		const LAZY_SCRIPT_TIMEOUT_MS = 8000;
		const PAGE_BOOT_TIMEOUT_MS = 3500;

		function withTimeout(promise, timeoutMs, label) {
			let timer = null;
			const timeout = new Promise((resolve) => {
				timer = window.setTimeout(() => {
					console.warn(`${label || "Operation"} timed out`);
					resolve(false);
				}, timeoutMs);
			});
			return Promise.race([promise, timeout]).finally(() => {
				if (timer) {
					window.clearTimeout(timer);
				}
			});
		}

	function scriptLooksReady(src) {
		if (!src) return false;
		if (src.indexOf("selectpicker/selectpicker.js") !== -1) {
			return !!(window.jQuery && window.jQuery.fn && window.jQuery.fn.selectpicker);
		}
		if (src.indexOf("datetimepicker/datetimepicker.js") !== -1) {
			return typeof window.initDatePickers === "function";
		}
		if (src.indexOf("editor/editor.js") !== -1) {
			return typeof window.initEditors === "function";
		}
		if (src.indexOf("validation/validation.js") !== -1) {
			return !!window.FormValidator;
		}
		if (src.indexOf("uploader/uploader.js") !== -1) {
			return typeof window.initUploader === "function";
		}
		if (src.indexOf("tags/tags.js") !== -1) {
			return typeof window.initTags === "function";
		}
		if (src.indexOf("colorpicker/colorpicker.js") !== -1) {
			return typeof window.initColorPickers === "function";
		}
		if (src.indexOf("kanban/kanban.js") !== -1) {
			return typeof window.initKanban === "function";
		}
		return false;
	}

	function loadScriptOnce(src) {
		if (!src) return Promise.resolve(false);
		if (lazyScriptPromises[src]) return lazyScriptPromises[src];
		const existing = document.querySelector(`script[src="${src}"]`);
		if (existing) {
			if (scriptLooksReady(src) || existing.dataset.erpLoaded === "1") {
				lazyScriptPromises[src] = Promise.resolve(true);
				return lazyScriptPromises[src];
			}
			lazyScriptPromises[src] = withTimeout(new Promise((resolve, reject) => {
				const finish = () => {
					existing.dataset.erpLoaded = "1";
					resolve(true);
				};
				existing.addEventListener("load", finish, { once: true });
				existing.addEventListener("error", () => reject(new Error(`Failed to load ${src}`)), { once: true });
				window.setTimeout(() => {
					if (scriptLooksReady(src)) {
						finish();
					}
				}, 0);
			}), LAZY_SCRIPT_TIMEOUT_MS, `Script load ${src}`).catch(() => false);
			return lazyScriptPromises[src];
		}
		lazyScriptPromises[src] = withTimeout(new Promise((resolve, reject) => {
			const script = document.createElement("script");
			script.src = src;
			script.defer = true;
			script.onload = () => {
				script.dataset.erpLoaded = "1";
				resolve(true);
			};
			script.onerror = () => reject(new Error(`Failed to load ${src}`));
			document.head.appendChild(script);
		}), LAZY_SCRIPT_TIMEOUT_MS, `Script load ${src}`).catch(() => false);
		return lazyScriptPromises[src];
	}

	function ensureDataTableAssets() {
		if (window.jQuery && window.jQuery.fn && window.jQuery.fn.DataTable && window.erpInitDatatables) {
			return Promise.resolve(true);
		}
		const assets = window.erpDataTableCoreAssets || {};
		const scripts = [
			assets.jqueryDataTables,
			assets.responsive,
			assets.buttons,
			assets.print,
			assets.colVis,
			assets.erpDataTables,
		].filter(Boolean);
		let chain = Promise.resolve();
		scripts.forEach((src) => {
			chain = chain.then(() => loadScriptOnce(src));
		});
		return chain.then(() => true);
	}

	function hasFeature(root, selector) {
		if (!root) return false;
		if (root.matches && root.matches(selector)) return true;
		return !!(root.querySelector && root.querySelector(selector));
	}

	function ensureFeaturePlugins(root = document) {
		const assets = window.erpLazyAssets || {};
		const tasks = [];
		if (hasFeature(root, "select.selectpicker, select.tw-selectpicker")) {
			tasks.push(loadScriptOnce(assets.selectpicker).then(() => {
				if (window.jQuery && window.jQuery.fn && window.jQuery.fn.selectpicker) {
					window.jQuery(root).find("select.selectpicker, select.tw-selectpicker").selectpicker();
				}
				if (window.refreshSelectpicker) {
					window.refreshSelectpicker("select.tw-selectpicker");
				}
			}));
		}
		if (hasFeature(root, "input.ts-datepicker, input.datetimepicker")) {
			tasks.push(loadScriptOnce(assets.datetimepicker).then(() => {
				if (window.initDatePickers) window.initDatePickers();
			}));
		}
		if (hasFeature(root, "input.tw-tags")) {
			tasks.push(loadScriptOnce(assets.tags).then(() => {
				if (window.initTags) window.initTags(root);
			}));
		}
		if (hasFeature(root, ".tw-dropzone, .tw-simple-upload, .photo-uploader-container")) {
			tasks.push(loadScriptOnce(assets.uploader).then(() => {
				if (window.initUploader) window.initUploader();
			}));
		}
		if (hasFeature(root, "input.tw-colorpicker")) {
			tasks.push(loadScriptOnce(assets.colorpicker).then(() => {
				if (window.initColorPickers) window.initColorPickers(root);
			}));
		}
		if (hasFeature(root, "textarea.tw-editor")) {
			tasks.push(loadScriptOnce(assets.editor).then(() => {
				if (window.initEditors) window.initEditors(root);
			}));
		}
		if (hasFeature(root, "form[data-parsley-validate], form[data-validate], form[data-comment-form], form[data-comment-reply-form]")) {
			tasks.push(loadScriptOnce(assets.validation).then(() => {
				if (window.FormValidator && typeof window.FormValidator.init === "function") {
					window.FormValidator.init();
				}
			}));
		}
		if (hasFeature(root, "[data-kanban]")) {
			tasks.push(loadScriptOnce(assets.kanban).then(() => {
				if (window.initKanban) {
					window.initKanban(root);
				}
			}));
		}
		if (hasFeature(root, "table.DataTables, #DataTables")) {
			tasks.push(ensureDataTableAssets().then(() => {
				if (window.erpInitDatatables) {
					window.erpInitDatatables(root);
				}
			}));
		}
		return Promise.all(tasks).then(() => undefined);
	}

	window.erpEnsureFeaturePlugins = ensureFeaturePlugins;

	let uiRefreshFrame = null;
	function refreshInjectedUi(root = document) {
		const safeRun = (label, fn) => {
			try {
				fn();
			} catch (error) {
				console.warn(`UI refresh hook failed: ${label}`, error);
			}
		};
		beginUiBootstrap();
		safeRun("updateIcons", () => updateIcons(root));
		const bootPromise = ensureFeaturePlugins(root)
			.then(() => {
				safeRun("updateIcons-after-plugins", () => updateIcons(root));
			})
			.catch((error) => {
				console.warn("Feature plugin refresh failed", error);
			})
			.finally(() => {
				endUiBootstrap();
			});
		safeRun("initLookup", () => {
			if (window.initLookup) {
				window.initLookup(root);
			}
		});
		safeRun("initGroupedLookup", () => {
			if (window.initGroupedLookup) {
				window.initGroupedLookup(root);
			}
		});
		safeRun("initReminderModal", () => {
			if (window.initReminderModal) {
				window.initReminderModal(root);
			}
		});
		safeRun("initBatchPaymentModal", () => {
			if (window.initBatchPaymentModal) {
				window.initBatchPaymentModal(root);
			}
		});
		safeRun("initInvoiceSidebar", () => {
			if (window.initInvoiceSidebar) {
				window.initInvoiceSidebar(root);
			}
		});
		safeRun("initPurchaseSidebar", () => {
			if (window.initPurchaseSidebar) {
				window.initPurchaseSidebar(root);
			}
		});
		safeRun("initAutosizeTextareas", () => {
			if (window.initAutosizeTextareas) {
				window.initAutosizeTextareas(root);
			}
		});
		safeRun("initSalesItemsEditor", () => {
			if (window.initSalesItemsEditor) {
				window.initSalesItemsEditor(root);
			}
		});
		safeRun("initKanban", () => {
			if (window.initKanban) {
				window.initKanban(root);
			}
		});
		safeRun("erpInitCharts", () => {
			if (window.erpInitCharts) {
				window.erpInitCharts(root);
			}
		});
		safeRun("initLeaveApplicationForms", () => initLeaveApplicationForms(root));
		return bootPromise;
	}

	function queueUiRefresh(root = document) {
		if (uiRefreshFrame) {
			cancelAnimationFrame(uiRefreshFrame);
		}
		uiRefreshFrame = requestAnimationFrame(() => {
			refreshInjectedUi(root);
			uiRefreshFrame = null;
		});
	}

	window.refreshAppUi = refreshInjectedUi;

	function parseAjaxJsonResponse(payload) {
		if (!payload) return null;
		if (typeof payload === "object") return payload;
		if (typeof payload !== "string") return null;
		try {
			return JSON.parse(payload);
		} catch (e) {
			return null;
		}
	}

	function showAjaxToast(type, message) {
		if (!message) return;
		const normalizedType = type || "success";
		if (window.toastr && typeof window.toastr[normalizedType] === "function") {
			window.toastr[normalizedType](message);
			return;
		}
		const fallbackType = normalizedType === "error" ? "error" : "success";
		let container = document.getElementById("ajax-toast-fallback");
		if (!container) {
			container = document.createElement("div");
			container.id = "ajax-toast-fallback";
			container.className =
				"fixed right-4 top-4 z-[10000] flex w-full max-w-sm flex-col gap-2";
			document.body.appendChild(container);
		}

		const toast = document.createElement("div");
		toast.className =
			"rounded-xl border px-4 py-3 text-sm shadow-lg transition-all duration-200 " +
			(fallbackType === "error"
				? "border-rose-200 bg-rose-50 text-rose-700"
				: "border-emerald-200 bg-emerald-50 text-emerald-700");
		toast.textContent = message;
		toast.style.opacity = "0";
		toast.style.transform = "translateY(-8px)";
		container.appendChild(toast);

		requestAnimationFrame(() => {
			toast.style.opacity = "1";
			toast.style.transform = "translateY(0)";
		});

		window.setTimeout(() => {
			toast.style.opacity = "0";
			toast.style.transform = "translateY(-8px)";
			window.setTimeout(() => {
				toast.remove();
				if (!container.children.length) {
					container.remove();
				}
			}, 220);
		}, 2600);
	}

	function applyAjaxPatches(patches) {
		if (!Array.isArray(patches)) return false;
		let patched = false;
		patches.forEach((item) => {
			if (!item || !item.selector || typeof item.html !== "string") return;
			const target = document.querySelector(item.selector);
			if (!target) return;
			if (item.mode === "replace") {
				target.outerHTML = item.html;
			} else {
				target.innerHTML = item.html;
			}
			patched = true;
		});
		if (patched) {
			refreshInjectedUi(document);
		}
		return patched;
	}

	function refreshAjaxDeleteUi(trigger) {
		const fadeSelector = trigger?.dataset?.fadeOutOnSuccess;
		const fadeTarget = fadeSelector
			? document.querySelector(fadeSelector)
			: trigger?.closest("tr, .custom-row, .rounded-xl, .rounded-2xl, [data-removable]");

		if (fadeTarget) {
			fadeTarget.style.transition = "opacity 160ms ease";
			fadeTarget.style.opacity = "0";
			window.setTimeout(() => {
				if (fadeTarget.parentNode) {
					fadeTarget.parentNode.removeChild(fadeTarget);
				}
			}, 180);
		}

		if (
			trigger &&
			typeof trigger.closest === "function" &&
			trigger.closest("table.DataTables") &&
			typeof window.reload_table === "function"
		) {
			window.reload_table();
		}
	}

	function bindAjaxDeleteLinks() {
		document.addEventListener("click", async (event) => {
			let trigger = event.target;
			if (trigger && trigger.nodeType !== 1) {
				trigger = trigger.parentElement;
			}
			trigger =
				trigger && typeof trigger.closest === "function"
					? trigger.closest('.js-ajax-delete, [data-act="ajax-request"], [data-confirm-message], [data-confirm]')
					: null;
			if (!trigger) return;

			const actionUrl =
				trigger.getAttribute("data-action-url") || trigger.getAttribute("href");
			if (!actionUrl) return;

			const confirmMessage =
				trigger.getAttribute("data-confirm-message") ||
				trigger.getAttribute("data-confirm");
			if (confirmMessage) {
				event.preventDefault();
				event.stopPropagation();
				if (typeof event.stopImmediatePropagation === "function") {
					event.stopImmediatePropagation();
				}
				if (typeof window.erpConfirmDialog === "function") {
					const actionUrlLower = String(actionUrl || "").toLowerCase();
					const titleLower = String(trigger.getAttribute("title") || "").toLowerCase();
					const looksLikeDelete =
						trigger.classList.contains("js-ajax-delete") ||
						trigger.classList.contains("deleteBtn") ||
						titleLower === String(window.confirm_delete || "delete").toLowerCase() ||
						actionUrlLower.includes("/delete") ||
						actionUrlLower.includes("delete_");
					const confirmAction =
						trigger.getAttribute("data-confirm-action") ||
						(looksLikeDelete ? "delete" : "confirm");
					const confirmTitle =
						trigger.getAttribute("data-confirm-title") ||
						(confirmAction === "delete"
							? window.confirm_delete || "Delete"
							: window.confirm_action || "Confirm");
					const confirmAcceptLabel =
						trigger.getAttribute("data-confirm-accept-label") ||
						(confirmAction === "delete"
							? window.confirm_delete || "Delete"
							: window.confirm_action || "Confirm");
					const confirmed = await window.erpConfirmDialog(confirmMessage, {
						title: confirmTitle,
						acceptLabel: confirmAcceptLabel,
						cancelLabel: window.confirm_cancel || "Cancel",
						action: confirmAction,
						trigger,
					});
						if (!confirmed) {
							return;
						}
					} else if (!window.confirm(confirmMessage)) {
						return;
					}
					if (trigger.getAttribute("data-modal-url")) {
						document.dispatchEvent(
							new CustomEvent("erp:open-modal", {
								detail: { trigger },
							})
						);
						return;
					}
					if (
						!trigger.classList.contains("js-ajax-delete") &&
						trigger.getAttribute("data-act") !== "ajax-request"
					) {
					window.location.href = actionUrl;
					return;
				}
			} else {
				event.preventDefault();
			}

			if (trigger.dataset.ajaxBusy === "1") return;
			trigger.dataset.ajaxBusy = "1";

			try {
				const response = await fetch(actionUrl, {
					method: "GET",
					headers: {
						"X-Requested-With": "XMLHttpRequest",
						Accept: "application/json, text/plain, */*",
					},
				});
				const responseText = await response.text();
				const result = parseAjaxJsonResponse(responseText);

				if (!result) {
					throw new Error("Invalid JSON response");
				}

				const status = result.status || result.type || "success";
				const message = result.message || result.msg || "";
				showAjaxToast(status, message);

				if (status === "success") {
					const redirectUrl =
						trigger.getAttribute("data-redirect-url") ||
						result.redirect_url ||
						result.redirectUrl ||
						"";
					if (redirectUrl) {
						window.setTimeout(() => {
							window.location.href = redirectUrl;
						}, 180);
						return;
					}
					const patched = applyAjaxPatches(result.patch);
					if (!patched) {
						refreshAjaxDeleteUi(trigger);
					}
					if (
						result.reload_page === true ||
						trigger.getAttribute("data-reload-page") === "1"
					) {
						window.setTimeout(() => {
							window.location.reload();
						}, 250);
					}
				}
			} catch (error) {
				showAjaxToast("error", "There was a problem with AJAX");
				console.error("AJAX delete failed", error);
			} finally {
				delete trigger.dataset.ajaxBusy;
			}
		});
	}

	function bindSidebarToggle() {
		const trigger = document.getElementById("sidebar-trigger");
		const backdrop = document.getElementById("sidebar-backdrop");
		const sidebar = document.getElementById("sidebar");
		const collapseTriggers = document.querySelectorAll("[data-sidebar-collapse]");
		const closeTriggers = document.querySelectorAll("[data-sidebar-close]");
		const openTriggers = document.querySelectorAll("[data-sidebar-open]");
		const storageKey = (app && app.sidebarStorageKey) || "layout.sidebar.collapsed";
		if (!backdrop || !sidebar) return;

		let collapsed = false;

		const syncProfileVisibility = () => {
			const desktopCollapsed = collapsed && window.innerWidth >= 768;
			document.querySelectorAll(".sidebar-profile-meta").forEach((el) => {
				el.classList.toggle("hidden", desktopCollapsed);
			});
			document.querySelectorAll(".sidebar-profile-chevron").forEach((el) => {
				el.classList.toggle("hidden", desktopCollapsed);
			});
			document.querySelectorAll(".sidebar-profile-actions").forEach((el) => {
				el.classList.toggle("hidden", false);
			});
			document.querySelectorAll(".sidebar-action-text").forEach((el) => {
				el.classList.toggle("hidden", desktopCollapsed);
			});
		};

		const syncCollapseIcons = () => {
			document.querySelectorAll("[data-sidebar-collapse-icon]").forEach((iconEl) => {
				iconEl.setAttribute(
					"data-lucide",
					collapsed ? "panel-left-open" : "panel-left-close",
				);
			});
			updateIcons();
		};

		const persistCollapsed = (value) => {
			try {
				localStorage.setItem(storageKey, value ? "1" : "0");
			} catch (e) {
				// Ignore storage errors
			}
		};

		const readCollapsed = () => {
			try {
				return localStorage.getItem(storageKey) === "1";
			} catch (e) {
				return false;
			}
		};

		const applyCollapsed = (value, persist = true) => {
			collapsed = !!value;
			document.body.classList.toggle("sidebar-collapsed", collapsed);
			if (persist) persistCollapsed(collapsed);
			syncProfileVisibility();
			syncCollapseIcons();
		};

		const syncMobileSidebarState = (isOpen = false) => {
			if (window.innerWidth >= 768) {
				sidebar.classList.remove("-translate-x-full");
				sidebar.style.transform = "";
				sidebar.style.zIndex = "";
				backdrop.classList.add("hidden");
				document.body.classList.remove("overflow-hidden", "sidebar-mobile-open");
				return;
			}
			document.body.classList.toggle("sidebar-mobile-open", !!isOpen);
			if (isOpen) {
				sidebar.classList.remove("-translate-x-full");
				sidebar.style.transform = "translateX(0)";
				sidebar.style.zIndex = "60";
				backdrop.classList.remove("hidden");
				document.body.classList.add("overflow-hidden");
			} else {
				sidebar.classList.add("-translate-x-full");
				sidebar.style.transform = "translateX(-100%)";
				sidebar.style.zIndex = "60";
				backdrop.classList.add("hidden");
				document.body.classList.remove("overflow-hidden");
			}
		};

		const openSidebar = () => {
			syncMobileSidebarState(true);
		};

		const closeSidebar = () => {
			if (window.innerWidth >= 768) return;
			syncMobileSidebarState(false);
		};

		const toggleCollapsed = () => {
			if (window.innerWidth < 768) {
				openSidebar();
				return;
			}
			applyCollapsed(!collapsed, true);
		};

		window.setSidebarCollapsed = (value, persist = true) => {
			applyCollapsed(!!value, !!persist);
		};
		window.toggleSidebarCollapsed = () => toggleCollapsed();
		window.setSidebarMode = (mode) => {
			if (mode === "collapsed") applyCollapsed(true, true);
			if (mode === "expanded") applyCollapsed(false, true);
		};
		window.__erpSidebarApi = {
			closeSidebar,
			openSidebar,
			syncProfileVisibility,
		};

		if (trigger) {
			if (trigger.dataset.sidebarBound !== "1") {
				trigger.dataset.sidebarBound = "1";
				trigger.addEventListener("click", (e) => {
					e.preventDefault();
					openSidebar();
				});
			}
		}
		openTriggers.forEach((btn) => {
			if (btn === trigger || btn.dataset.sidebarBound === "1") return;
			btn.dataset.sidebarBound = "1";
			btn.addEventListener("click", (e) => {
				e.preventDefault();
				openSidebar();
			});
		});
		collapseTriggers.forEach((btn) => {
			if (btn.dataset.sidebarBound === "1") return;
			btn.dataset.sidebarBound = "1";
			btn.addEventListener("click", (e) => {
				e.preventDefault();
				toggleCollapsed();
			});
		});
		closeTriggers.forEach((btn) => {
			if (btn.dataset.sidebarBound === "1") return;
			btn.dataset.sidebarBound = "1";
			btn.addEventListener("click", (e) => {
				e.preventDefault();
				closeSidebar();
			});
		});

		if (backdrop && backdrop.dataset.sidebarBound !== "1") {
			backdrop.dataset.sidebarBound = "1";
			backdrop.addEventListener("click", () => closeSidebar());
		}

		if (window.__erpSidebarGlobalsBound !== true) {
			window.__erpSidebarGlobalsBound = true;
			document.addEventListener("keydown", (e) => {
				if (e.key === "Escape" && window.__erpSidebarApi) {
					window.__erpSidebarApi.closeSidebar();
				}
			});
			document.addEventListener("click", (e) => {
				let el = e.target;
				if (el && el.nodeType !== 1) el = el.parentElement;
				const navLink = el && typeof el.closest === "function" ? el.closest("#sidebar a[href]") : null;
				if (!navLink || !window.__erpSidebarApi) return;
				window.__erpSidebarApi.closeSidebar();
			});
			window.addEventListener("resize", () => {
				const activeSidebar = document.getElementById("sidebar");
				const activeBackdrop = document.getElementById("sidebar-backdrop");
				if (window.innerWidth >= 768) {
					if (activeSidebar) activeSidebar.classList.remove("-translate-x-full");
					if (activeBackdrop) activeBackdrop.classList.add("hidden");
					document.body.classList.remove("overflow-hidden");
					document.body.classList.remove("sidebar-mobile-open");
				} else {
					if (activeSidebar) activeSidebar.classList.add("-translate-x-full");
					if (activeBackdrop) activeBackdrop.classList.add("hidden");
					document.body.classList.remove("overflow-hidden");
				}
				if (window.__erpSidebarApi) {
					window.__erpSidebarApi.syncProfileVisibility();
				}
			});
		}

		if (window.innerWidth < 768) {
			syncMobileSidebarState(false);
		}
		applyCollapsed(readCollapsed(), false);
	}

	function bindSidebarProfileMenu() {
		const toggle = document.getElementById("sidebar-profile-toggle");
		const menu = document.getElementById("sidebar-profile-menu");
		if (!toggle || !menu) return;

		const closeMenu = () => {
			menu.classList.add("hidden");
			toggle.setAttribute("aria-expanded", "false");
		};
		const openMenu = () => {
			menu.classList.remove("hidden");
			toggle.setAttribute("aria-expanded", "true");
		};
		window.__erpSidebarProfileApi = {
			closeMenu,
			toggle,
			menu,
		};

		if (toggle.dataset.profileMenuBound !== "1") {
			toggle.dataset.profileMenuBound = "1";
			toggle.addEventListener("click", (e) => {
				e.preventDefault();
				if (menu.classList.contains("hidden")) {
					openMenu();
				} else {
					closeMenu();
				}
			});
		}

		if (window.__erpSidebarProfileGlobalsBound !== true) {
			window.__erpSidebarProfileGlobalsBound = true;
			document.addEventListener("click", (e) => {
				const api = window.__erpSidebarProfileApi;
				if (!api) return;
				if (api.toggle.contains(e.target) || api.menu.contains(e.target)) return;
				api.closeMenu();
			});

			document.addEventListener("keydown", (e) => {
				if (e.key === "Escape" && window.__erpSidebarProfileApi) {
					window.__erpSidebarProfileApi.closeMenu();
				}
			});
		}
	}

	function bindSidebarSearch() {
		const sidebar = document.getElementById("sidebar");
		const nav = document.getElementById("nav-container");
		const input = sidebar ? sidebar.querySelector("[data-sidebar-search-input]") : null;
		if (!sidebar || !nav || !input) return;

		const getDirectSubmenu = (item) => {
			if (!item || !item.children) return null;
			return Array.from(item.children).find((child) => {
				return child && child.nodeType === 1 && child.classList.contains("sidebar-submenu");
			}) || null;
		};

		const getDirectLabel = (item) => {
			if (!item) return "";
			const link = Array.from(item.children || []).find((child) => {
				return child && child.nodeType === 1 && child.classList.contains("sidebar-nav-link");
			});
			if (!link) return "";
			const textNode = link.querySelector(".sidebar-nav-text");
			return ((textNode ? textNode.textContent : link.textContent) || "").trim().toLowerCase();
		};

		const topLevelItems = () =>
			Array.from(nav.querySelectorAll("[data-sidebar-item]")).filter((item) => item.parentElement === nav);

		const childItems = (submenu) =>
			Array.from(submenu ? submenu.children : []).filter((child) => {
				return child && child.nodeType === 1 && child.hasAttribute("data-sidebar-item");
			});

		const restoreDefaultState = () => {
			nav.querySelectorAll("[data-sidebar-item]").forEach((item) => {
				item.classList.remove("hidden");
			});
			nav.querySelectorAll("[data-submenu]").forEach((submenu) => {
				const shouldHide = submenu.dataset.defaultHidden !== "0";
				submenu.classList.toggle("hidden", shouldHide);
			});
		};

		const filterItem = (item, query) => {
			const label = getDirectLabel(item);
			const submenu = getDirectSubmenu(item);
			let childVisible = false;

			if (submenu) {
				childItems(submenu).forEach((child) => {
					if (filterItem(child, query)) {
						childVisible = true;
					}
				});
			}

			const selfVisible = !query || label.includes(query);
			const visible = selfVisible || childVisible;
			item.classList.toggle("hidden", !visible);

			if (submenu) {
				const defaultHidden = submenu.dataset.defaultHidden !== "0";
				const shouldShowChildren = !!query && childVisible;
				const shouldHideSubmenu = !query ? defaultHidden : !shouldShowChildren;
				submenu.classList.toggle("hidden", shouldHideSubmenu);
			}

			return visible;
		};

		const applyFilter = () => {
			const query = input.value.trim().toLowerCase();
			if (!query) {
				restoreDefaultState();
				return;
			}
			topLevelItems().forEach((item) => {
				filterItem(item, query);
			});
		};

		if (input.dataset.sidebarSearchBound !== "1") {
			input.dataset.sidebarSearchBound = "1";
			input.addEventListener("input", applyFilter);
			input.addEventListener("search", applyFilter);
			input.addEventListener("keydown", (event) => {
				if (event.key === "Escape") {
					input.value = "";
					applyFilter();
				}
			});
		}

		applyFilter();
	}

	function bindInstantNavigation() {
		const cache = new Map();
		const datatablePrefetchCache = new Map();
		let inflightRequest = null;
		let navBusy = false;
		let topbarTimer = null;
		let prefetchIdleHandle = null;

		function getTopbar() {
			return document.getElementById("app-topbar-preload");
		}

		function setTopbarProgress(progress, visible = true) {
			const bar = getTopbar();
			if (!bar) return;
			const normalized = Math.max(0, Math.min(1, progress || 0));
			bar.style.transform = "scaleX(" + normalized + ")";
			bar.style.opacity = visible ? "1" : "0";
			bar.setAttribute("aria-hidden", visible ? "false" : "true");
		}

		function startTopbar() {
			const bar = getTopbar();
			if (!bar) return;
			if (topbarTimer) {
				clearInterval(topbarTimer);
			}
			bar.classList.add("is-loading");
			setTopbarProgress(0.08, true);
			let current = 0.08;
			topbarTimer = window.setInterval(() => {
				current = Math.min(0.94, current + (current < 0.35 ? 0.14 : current < 0.7 ? 0.05 : 0.015));
				setTopbarProgress(current, true);
			}, 90);
		}

		function finishTopbar() {
			const bar = getTopbar();
			if (!bar) return;
			if (topbarTimer) {
				clearInterval(topbarTimer);
				topbarTimer = null;
			}
			setTopbarProgress(1, true);
			window.setTimeout(() => {
				bar.classList.remove("is-loading");
				setTopbarProgress(0, false);
			}, 220);
		}

		const topbarApi = {
			pending: 0,
			start() {
				this.pending += 1;
				if (this.pending === 1) {
					startTopbar();
				}
			},
			finish() {
				if (this.pending > 0) {
					this.pending -= 1;
				}
				if (this.pending === 0) {
					finishTopbar();
				}
			},
			pulse(progress) {
				setTopbarProgress(progress, true);
			},
		};
		window.erpTopbar = topbarApi;

		function isEligibleLink(link) {
			if (!link || !link.href) return false;
			if (link.closest && link.closest("#sidebar")) return false;
			if (link.target && link.target !== "_self") return false;
			if (link.hasAttribute("download")) return false;
			if (link.getAttribute("data-no-instant-nav") === "1") return false;
			if (link.getAttribute("data-toggle") === "modal") return false;
			if (link.getAttribute("data-act") === "ajax-modal") return false;
			if (link.getAttribute("data-act") === "ajax-request") return false;
			const href = link.getAttribute("href") || "";
			if (!href || href === "#" || href.indexOf("javascript:") === 0) return false;
			const url = new URL(link.href, window.location.origin);
			if (url.origin !== window.location.origin) return false;
			if (/\/change_status(\/|$)/.test(url.pathname)) return false;
			if (/\/admin\/dashboard\/change_report(\/|$)/.test(url.pathname)) return false;
			if (!/^\/(admin|client)\//.test(url.pathname)) return false;
			return true;
		}

		function getShellPartsFromHtml(htmlText) {
			const parser = new DOMParser();
			const doc = parser.parseFromString(htmlText, "text/html");
			return {
				title: doc.title || document.title,
				sidebar: doc.querySelector("#app-sidebar-shell"),
				header: doc.querySelector("#app-header-shell"),
				content: doc.querySelector("#app-content"),
			};
		}

		function extractDatatableUrls(htmlText) {
			const urls = [];
			if (!htmlText) return urls;
			const regex = /\blist\s*=\s*base_url\s*\+\s*"([^"]+)"/g;
			let match;
			while ((match = regex.exec(htmlText)) !== null) {
				if (match[1]) {
					urls.push(window.base_url + match[1]);
				}
			}
			return Array.from(new Set(urls));
		}

		function defaultDatatableRequestPayload() {
			return {
				draw: 1,
				start: 0,
				length: window.tables_pagination_limit || 10,
				search: { value: "", regex: false },
			};
		}

		function prefetchDatatablePayloads(htmlText) {
			const urls = extractDatatableUrls(htmlText);
			if (!urls.length) return Promise.resolve();
			const tasks = urls.map((url) => {
				if (datatablePrefetchCache.has(url)) {
					return datatablePrefetchCache.get(url);
				}
				const params = new URLSearchParams();
				const payload = defaultDatatableRequestPayload();
				Object.keys(payload).forEach((key) => {
					if (key === "search") {
						params.append("search[value]", payload.search.value);
						params.append("search[regex]", String(payload.search.regex));
					} else {
						params.append(key, String(payload[key]));
					}
				});
				const csrfConfig = window.erpCsrf || {};
				const csrfCookieName = csrfConfig.cookieName || "csrf_cookie";
				const csrfTokenName = csrfConfig.tokenName || "csrf_token";
				const csrf = typeof getCookie === "function" ? getCookie(csrfCookieName) : null;
				if (csrf) {
					params.append(csrfTokenName, csrf);
				}
				const promise = fetch(url, {
					method: "POST",
					headers: {
						"X-Requested-With": "XMLHttpRequest",
						"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
						"Accept": "application/json",
					},
					body: params.toString(),
				}).then((response) => {
					if (!response.ok) {
						throw new Error("datatable prefetch failed");
					}
					return response.json();
				}).then((json) => {
					if (window.erpWarmDatatableCache) {
						window.erpWarmDatatableCache(url, payload, json, "DataTables");
					}
					return json;
				}).catch(() => null);
				datatablePrefetchCache.set(url, promise);
				return promise;
			});
			return Promise.all(tasks).then(() => undefined);
		}

		function setBusyState(isBusy) {
			const content = document.getElementById("app-content");
			navBusy = !!isBusy;
			document.documentElement.classList.toggle("app-nav-busy", navBusy);
			if (navBusy) {
				topbarApi.start();
			} else {
				topbarApi.finish();
			}
			if (content) {
				content.classList.toggle("cursor-progress", navBusy);
			}
		}

		function teardownCurrentPage() {
			document.querySelectorAll('[data-selectpicker-floating="true"]').forEach((el) => {
				el.remove();
			});
			document.querySelectorAll("select.tw-selectpicker").forEach((select) => {
				if (select.dataset && select.dataset.enhanced === "true") {
					delete select.dataset.enhanced;
				}
				select.classList.remove("hidden");
				select.style.display = "";
				select.style.visibility = "";
				const next = select.nextElementSibling;
				if (next && next.dataset && next.dataset.selectpickerWrapper === "true") {
					next.remove();
				}
			});
			if (window.jQuery && window.jQuery.fn && window.jQuery.fn.DataTable) {
				window.jQuery(".DataTables").each(function() {
					if (window.jQuery.fn.dataTable.isDataTable(this)) {
						window.jQuery(this).DataTable().destroy(true);
					}
				});
			}
			window.list = "";
			window.bulk_url = "";
			window.erpDatatablePreload = {};
			document.querySelectorAll("[data-chart]").forEach((el) => {
				if (el && el._apexChart && typeof el._apexChart.destroy === "function") {
					try {
						el._apexChart.destroy();
					} catch (e) {
						// Ignore chart cleanup failures.
					}
				}
			});
		}

		function executeScriptsFrom(container) {
			if (!container) return Promise.resolve();
			const scripts = Array.from(container.querySelectorAll("script"));
			let chain = Promise.resolve();
			scripts.forEach((oldScript) => {
				chain = chain.then(() => {
					const flashMessageId = oldScript.getAttribute("data-erp-flash-message");
					if (flashMessageId) {
						const key = "erp-flash-message:" + flashMessageId;
						window.erpShownFlashMessages = window.erpShownFlashMessages || {};
						if (window.erpShownFlashMessages[flashMessageId]) {
							return Promise.resolve();
						}
						try {
							if (window.sessionStorage && window.sessionStorage.getItem(key)) {
								window.erpShownFlashMessages[flashMessageId] = true;
								return Promise.resolve();
							}
						} catch (error) {
							// Ignore storage access failures and let the inline guard handle it.
						}
					}
					if (oldScript.src) {
						const src = oldScript.getAttribute("src");
						if (document.querySelector('script[src="' + src + '"]')) {
							return Promise.resolve();
						}
						return new Promise((resolve, reject) => {
							const script = document.createElement("script");
							script.src = src;
							script.async = false;
							script.onload = resolve;
							script.onerror = reject;
							document.body.appendChild(script);
						});
					}
					try {
						const script = document.createElement("script");
						if (oldScript.type) {
							script.type = oldScript.type;
						}
						script.text = oldScript.textContent || "";
						document.body.appendChild(script);
						if (script.parentNode) {
							script.parentNode.removeChild(script);
						}
					} catch (error) {
						console.warn("Inline script execution failed", error);
					}
					return Promise.resolve();
				});
			});
			return chain;
		}

		function applyFetchedPage(parts) {
			if (!parts || !parts.sidebar || !parts.header || !parts.content) {
				throw new Error("Missing app shell parts");
			}
			const currentSidebar = document.getElementById("app-sidebar-shell");
			const currentHeader = document.getElementById("app-header-shell");
			const currentContent = document.getElementById("app-content");
			if (!currentSidebar || !currentHeader || !currentContent) {
				throw new Error("Current shell not found");
			}

			teardownCurrentPage();
			currentSidebar.replaceWith(parts.sidebar);
			currentHeader.replaceWith(parts.header);
			currentContent.replaceWith(parts.content);
			document.title = parts.title;
			document.body.classList.add("app-ui-loading");

			return Promise.all([
				executeScriptsFrom(parts.header),
				executeScriptsFrom(parts.content),
			])
				.catch((error) => {
					console.warn("One or more page scripts failed to execute", error);
				})
				.then(() => {
					bindThemeToggle();
					bindSidebarToggle();
					bindSidebarProfileMenu();
					bindSubmenus();
					const boot = window.refreshAppUi ? window.refreshAppUi(document) : Promise.resolve();
					withTimeout(Promise.resolve(boot), PAGE_BOOT_TIMEOUT_MS, "Page UI boot").finally(() => {
						window.setTimeout(() => {
							document.body.classList.remove("app-ui-loading");
							if (window.refreshAppUi) {
								window.refreshAppUi(document);
							}
						}, 50);
					});
					window.setTimeout(() => {
						if (window.refreshAppUi) {
							window.refreshAppUi(document);
						}
					}, 150);
					window.scrollTo({ top: 0, behavior: "auto" });
				});
		}

		function fetchPage(url) {
			const key = url.toString();
			if (cache.has(key)) {
				return Promise.resolve(getShellPartsFromHtml(cache.get(key)));
			}
			if (inflightRequest && inflightRequest.url === key) {
				return inflightRequest.promise;
			}
			const controller = new AbortController();
			const promise = fetch(key, {
				headers: {
					"X-Requested-With": "XMLHttpRequest",
					"X-ERP-Instant-Navigation": "1",
				},
				signal: controller.signal,
			}).then((response) => {
				if (!response.ok) {
					throw new Error("Navigation request failed");
				}
				const finalUrl = response.url || key;
				return response.text().then((htmlText) => ({ htmlText, finalUrl }));
			}).then(({ htmlText, finalUrl }) => {
				const parts = getShellPartsFromHtml(htmlText);
				parts.finalUrl = finalUrl;
				prefetchDatatablePayloads(htmlText).catch(() => {});
				const hasFlashMessage = htmlText.indexOf("data-erp-flash-message") !== -1;
				if (!hasFlashMessage) {
					cache.set(key, htmlText);
					if (finalUrl && finalUrl !== key) {
						cache.set(finalUrl, htmlText);
					}
				}
				return parts;
			}).finally(() => {
				if (inflightRequest && inflightRequest.url === key) {
					inflightRequest = null;
				}
			});
			inflightRequest = { url: key, controller, promise };
			return promise;
		}

		function warmLink(link) {
			if (!isEligibleLink(link)) return Promise.resolve();
			const url = new URL(link.href, window.location.origin);
			return fetchPage(url).catch(() => null);
		}

		function scheduleSidebarPrefetch() {
			if (prefetchIdleHandle) {
				if (typeof window.cancelIdleCallback === "function") {
					window.cancelIdleCallback(prefetchIdleHandle);
				} else {
					clearTimeout(prefetchIdleHandle);
				}
			}

			const runner = () => {
				const links = Array.from(document.querySelectorAll("#sidebar a[href]"))
					.filter((link) => isEligibleLink(link))
					.slice(0, 10);
				let chain = Promise.resolve();
				links.forEach((link) => {
					chain = chain.then(() => warmLink(link));
				});
			};

			if (typeof window.requestIdleCallback === "function") {
				prefetchIdleHandle = window.requestIdleCallback(runner, { timeout: 1200 });
			} else {
				prefetchIdleHandle = window.setTimeout(runner, 500);
			}
		}

		function navigateInstant(url, push = true) {
			setBusyState(true);
			return fetchPage(url).then((parts) => {
				return applyFetchedPage(parts).then(() => {
					scheduleSidebarPrefetch();
					if (push) {
						const finalUrl = parts.finalUrl || url.toString();
						window.history.pushState({ instantNav: true, url: finalUrl }, "", finalUrl);
					}
				});
			}).catch(() => {
				window.location.href = url.toString();
			}).finally(() => {
				setBusyState(false);
			});
		}

		document.addEventListener("click", (event) => {
			if (event.defaultPrevented) return;
			if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
			let target = event.target;
			if (target && target.nodeType !== 1) {
				target = target.parentElement;
			}
			const link = target && typeof target.closest === "function" ? target.closest("a[href]") : null;
			if (!isEligibleLink(link)) return;
			event.preventDefault();
			const url = new URL(link.href, window.location.origin);
			if (url.toString() === window.location.href) return;
			navigateInstant(url, true);
		});

		document.addEventListener("mouseenter", (event) => {
			let target = event.target;
			if (target && target.nodeType !== 1) {
				target = target.parentElement;
			}
			const link = target && typeof target.closest === "function" ? target.closest("a[href]") : null;
			if (!isEligibleLink(link)) return;
			const url = new URL(link.href, window.location.origin);
			warmLink(link).catch(() => {});
		}, true);

		document.addEventListener("focusin", (event) => {
			const link = event.target && event.target.closest ? event.target.closest("a[href]") : null;
			if (!isEligibleLink(link)) return;
			warmLink(link).catch(() => {});
		});

		window.addEventListener("popstate", () => {
			const url = new URL(window.location.href);
			navigateInstant(url, false);
		});

		scheduleSidebarPrefetch();
	}

	function bindSubmenus() {
		document.querySelectorAll("[data-toggle-submenu]").forEach((trigger) => {
			if (trigger.dataset.submenuBound === "1") return;
			trigger.dataset.submenuBound = "1";
			trigger.addEventListener("click", (e) => {
				e.preventDefault();
				if (
					document.body.classList.contains("sidebar-collapsed") &&
					window.innerWidth >= 768
				) {
					return;
				}
				const id = trigger.getAttribute("data-toggle-submenu");
				const submenu = document.querySelector(`[data-submenu="${id}"]`);
				if (!submenu) return;
				submenu.classList.toggle("hidden");
				const icon = trigger.querySelector('[data-lucide="chevron-down"]');
				if (icon) icon.classList.toggle("rotate-180");
			});
		});
	}

	// Support inline onclick usage in legacy markup.
	window.toggleSubmenu = function (id) {
		if (!id) return;
		if (
			document.body.classList.contains("sidebar-collapsed") &&
			window.innerWidth >= 768
		) {
			return;
		}
		const submenu = document.querySelector(`[data-submenu="${id}"]`);
		if (!submenu) return;

		const isOpen = !submenu.classList.contains("hidden");

		// Close all other submenus at the same level
		const parent = submenu.parentElement
			? submenu.parentElement.closest("[data-level]")
			: null;
		const level =
			submenu.dataset.level || (parent ? parent.dataset.level : "1");
		const siblings = document.querySelectorAll(
			`[data-submenu][data-level="${level}"]`,
		);
		siblings.forEach((el) => {
			if (el.dataset.submenu !== id) {
				el.classList.add("hidden");
				const trig = document.querySelector(
					`[data-menu-id="${el.dataset.submenu}"]`,
				);
				const ic = trig
					? trig.querySelector('[data-lucide="chevron-down"]')
					: null;
				if (ic) ic.classList.remove("rotate-180");
			}
		});

		// Toggle current
		submenu.classList.toggle("hidden", isOpen);
		const trigger = document.querySelector(`[data-menu-id="${id}"]`);
		const icon = trigger
			? trigger.querySelector('[data-lucide="chevron-down"]')
			: null;
		if (icon) icon.classList.toggle("rotate-180", isOpen === false);
	};

	window.navigateTo = function (route) {
		if (!route) return;
		window.location.href = route;
	};

	let apexChartsLoader = null;

	function ensureApexCharts() {
		if (typeof ApexCharts !== "undefined") {
			return Promise.resolve(ApexCharts);
		}
		if (apexChartsLoader) {
			return apexChartsLoader;
		}

		apexChartsLoader = new Promise((resolve, reject) => {
			const existing = document.querySelector('script[data-apexcharts-loader="1"]');
			if (existing) {
				existing.addEventListener("load", () => resolve(window.ApexCharts), { once: true });
				existing.addEventListener("error", reject, { once: true });
				return;
			}

			const script = document.createElement("script");
			script.src = "https://cdn.jsdelivr.net/npm/apexcharts";
			script.defer = true;
			script.dataset.apexchartsLoader = "1";
			script.onload = () => resolve(window.ApexCharts);
			script.onerror = reject;
			document.head.appendChild(script);
		}).catch((error) => {
			apexChartsLoader = null;
			throw error;
		});

		return apexChartsLoader;
	}

	async function initCharts(root = document) {
		const scope =
			root && typeof root.querySelectorAll === "function" ? root : document;
		const chartNodes = scope.querySelectorAll("[data-chart]");
		if (!chartNodes.length) {
			return;
		}

		try {
			await ensureApexCharts();
		} catch (error) {
			return;
		}

		chartNodes.forEach((el) => {
			if (el.dataset.chartReady === "1") {
				return;
			}
			const type = el.dataset.chart || "area";
			const mobileHeight = parseInt(
				el.dataset.heightSm || el.dataset.height || "320",
				10,
			);
			const desktopHeight = parseInt(el.dataset.height || "320", 10);
			const height = window.innerWidth < 640 ? mobileHeight : desktopHeight;
			let series = [];
			try {
				series = JSON.parse(el.dataset.series || "[]");
			} catch (err) {
				series = [];
			}

			if (!series.length) return;

			const options = {
				series,
				chart: {
					type,
					height,
					toolbar: { show: false },
					background: "transparent",
				},
				dataLabels: { enabled: false },
				stroke: { curve: "smooth", width: 2 },
				xaxis: {
					categories: series[0].categories || [],
					labels: { style: { colors: "hsl(var(--muted-foreground))" } },
					axisBorder: { show: false },
					axisTicks: { show: false },
				},
				yaxis: { show: false },
				grid: {
					show: true,
					borderColor: "hsl(var(--border))",
					strokeDashArray: 4,
				},
				theme: { mode: doc.classList.contains("dark") ? "dark" : "light" },
				colors: [
					"hsl(var(--primary))",
					"#10b981",
					"#f59e0b",
					"#3b82f6",
					"#ef4444",
				],
			};

			const chart = new ApexCharts(el, options);
			chart.render();
			el.dataset.chartReady = "1";
			el._apexChart = chart;
		});
	}
	window.erpInitCharts = initCharts;

	function initTooltips() {
		let tipEl = null;
		const padding = 8;

		function ensureTip() {
			if (tipEl) return tipEl;
			tipEl = document.createElement("div");
			tipEl.className =
				"fixed z-[9999] pointer-events-none rounded-md bg-slate-900 text-white text-xs px-2.5 py-1.5 shadow-lg opacity-0 transition-opacity";
			document.body.appendChild(tipEl);
			return tipEl;
		}

		function showTip(el) {
			const text = el.getAttribute("data-tooltip");
			if (!text) return;
			if (
				el.closest &&
				el.closest("#sidebar") &&
				el.classList.contains("sidebar-nav-link") &&
				!document.body.classList.contains("sidebar-collapsed")
			) {
				return;
			}
			const placement = (
				el.getAttribute("data-placement") || "top"
			).toLowerCase();
			const tip = ensureTip();
			tip.textContent = text;
			tip.style.opacity = "1";
			const rect = el.getBoundingClientRect();
			const tipRect = tip.getBoundingClientRect();
			let top = rect.top - tipRect.height - padding;
			let left = rect.left + rect.width / 2 - tipRect.width / 2;

			if (placement === "bottom") {
				top = rect.bottom + padding;
			} else if (placement === "left") {
				top = rect.top + rect.height / 2 - tipRect.height / 2;
				left = rect.left - tipRect.width - padding;
			} else if (placement === "right") {
				top = rect.top + rect.height / 2 - tipRect.height / 2;
				left = rect.right + padding;
			}

			top = Math.max(8, Math.min(top, window.innerHeight - tipRect.height - 8));
			left = Math.max(8, Math.min(left, window.innerWidth - tipRect.width - 8));
			tip.style.top = `${top}px`;
			tip.style.left = `${left}px`;
		}

		function hideTip() {
			if (tipEl) tipEl.style.opacity = "0";
		}

		document.addEventListener("mouseover", (e) => {
			const el = e.target.closest("[data-tooltip]");
			if (!el) return;
			showTip(el);
		});

		document.addEventListener("mouseout", (e) => {
			if (e.target.closest("[data-tooltip]")) hideTip();
		});

		document.addEventListener("mousedown", hideTip);
		document.addEventListener("focusin", (e) => {
			if (e.target.matches("[data-tooltip]")) showTip(e.target);
			else hideTip();
		});
		document.addEventListener("focusout", hideTip);

		document.addEventListener("scroll", hideTip, true);
		window.addEventListener("resize", hideTip);
	}

	function resetCommentForm(form) {
		form.querySelectorAll("textarea.tw-editor").forEach((el) => {
			el.value = "";
		});
		form.querySelectorAll(".editor-content").forEach((el) => {
			el.innerHTML = "";
		});
		form.querySelectorAll(".upload-row").forEach((row) => row.remove());
		form.querySelectorAll('input[type="file"]').forEach((input) => {
			input.value = "";
		});
		form.querySelectorAll("[data-file-target]").forEach((el) => {
			el.value = "";
		});
		form.querySelectorAll("[data-file-remove]").forEach((el) => {
			el.classList.add("hidden");
		});
		form.querySelectorAll("[data-photo-upload]").forEach((wrap) => {
			const img = wrap.querySelector("[data-photo-img]");
			const placeholder = wrap.querySelector("[data-photo-placeholder]");
			const btnText = wrap.querySelector("[data-photo-btn-text]");
			const removeBtn = wrap.querySelector("[data-photo-remove]");
			if (img) {
				img.src = "";
				img.classList.add("hidden");
			}
			if (placeholder) placeholder.classList.remove("hidden");
			if (btnText) btnText.textContent = "Select Image";
			if (removeBtn) removeBtn.classList.add("hidden");
		});
	}

	async function submitForm(form) {
		const method = (form.getAttribute("method") || "post").toUpperCase();
		const res = await fetch(form.getAttribute("action") || "", {
			method,
			body: new FormData(form),
			headers: { "X-Requested-With": "XMLHttpRequest" },
		});
		const text = await res.text();
		try {
			return JSON.parse(text);
		} catch (err) {
			return null;
		}
	}

	async function submitCommentForm(form) {
		const submitBtn =
			form.querySelector("[data-comment-submit]") ||
			form.querySelector("#file-save-button") ||
			form.querySelector('button[type="submit"]');
		const originalLabel = submitBtn ? submitBtn.innerHTML : "";
		if (submitBtn) {
			submitBtn.disabled = true;
			submitBtn.innerHTML = submitBtn.dataset.loadingText || "Please wait...";
		}

		try {
			const payload = await submitForm(form);

			if (!payload || !payload.status) {
				if (window.toaster) {
					window.toaster.error("Could not save. Please try again.");
				}
				return;
			}

			if (payload.status === "success") {
				const list =
					form.closest("#chat-box")?.querySelector("[data-comment-list]") ||
					form.parentElement;
				if (payload.data && list) {
					list.insertAdjacentHTML("afterbegin", payload.data);
				}
				resetCommentForm(form);
				initReplyForms();
				updateIcons();
				if (window.toaster) {
					window.toaster.success(payload.message || "Saved.");
				}
				return;
			}

			if (window.toaster) {
				window.toaster.error(payload.message || "Failed to save.");
			}
		} finally {
			if (submitBtn) {
				submitBtn.disabled = false;
				submitBtn.innerHTML = originalLabel || "Submit";
			}
		}
	}

	async function submitReplyForm(form) {
		const submitBtn = form.querySelector('button[type="submit"]');
		const originalLabel = submitBtn ? submitBtn.innerHTML : null;
		try {
			if (submitBtn) {
				submitBtn.disabled = true;
				submitBtn.innerHTML = "Please wait...";
			}
			const payload = await submitForm(form);
			if (!payload || !payload.status) {
				if (window.toaster) {
					window.toaster.error("Could not save. Please try again.");
				}
				return;
			}
			if (payload.status === "success") {
				const replyWrapper = form.closest(".reply__");
				let inserted = null;
				if (replyWrapper && payload.data) {
					replyWrapper.insertAdjacentHTML("beforebegin", payload.data);
					inserted = replyWrapper.previousElementSibling;
				}
				form.querySelectorAll("textarea").forEach((el) => (el.value = ""));
				if (replyWrapper) {
					replyWrapper.classList.add("hidden");
				}
				if (inserted && typeof inserted.scrollIntoView === "function") {
					inserted.scrollIntoView({ behavior: "smooth", block: "center" });
				}
				updateIcons();
				if (window.toaster) {
					window.toaster.success(payload.message || "Saved.");
				}
				return;
			}
			if (window.toaster) {
				window.toaster.error(payload.message || "Failed to save.");
			}
		} finally {
			if (submitBtn) {
				submitBtn.disabled = false;
				submitBtn.innerHTML = originalLabel || "Save";
			}
		}
	}

	function getRestoreDatabaseElements(form) {
		return {
			button: form.querySelector("#restoreDatabaseBtn"),
			progressBox: form.querySelector("#restoreDatabaseProgress"),
			progressText: form.querySelector("#restoreDatabaseProgressText"),
			progressPercent: form.querySelector("#restoreDatabaseProgressPercent"),
			progressBar: form.querySelector("#restoreDatabaseProgressBar"),
			progressDetail: form.querySelector("#restoreDatabaseProgressDetail"),
			fileName: form.querySelector("#restoreDatabaseFileName"),
			clearFile: form.querySelector("#restoreDatabaseClearFile"),
			fileInput: form.querySelector('input[type="file"]'),
		};
	}

	function setRestoreDatabaseProgress(elements, percent, text, detail) {
		const value = Math.max(0, Math.min(100, parseInt(percent || 0, 10)));
		if (elements.progressBox) {
			elements.progressBox.classList.remove("hidden");
		}
		if (elements.progressText) {
			elements.progressText.textContent = text || "Processing restore";
		}
		if (elements.progressPercent) {
			elements.progressPercent.textContent = value + "%";
		}
		if (elements.progressBar) {
			elements.progressBar.style.width = value + "%";
		}
		if (elements.progressDetail) {
			elements.progressDetail.textContent = detail || "";
		}
	}

	function updateRestoreDatabasePreview(elements) {
		if (!elements.fileInput || !elements.fileName || !elements.clearFile) {
			return;
		}
		const file = elements.fileInput.files && elements.fileInput.files[0] ? elements.fileInput.files[0] : null;
		if (file) {
			const sizeKb = Math.max(1, Math.round(file.size / 1024));
			elements.fileName.textContent = `${file.name} (${sizeKb} KB)`;
			elements.clearFile.classList.remove("hidden");
		} else {
			elements.fileName.textContent = "No file selected";
			elements.clearFile.classList.add("hidden");
		}
	}

	function parseRestoreProgress(text, cursor, elements) {
		const chunk = text.slice(cursor.value);
		cursor.value = text.length;
		chunk.split(/\r?\n/).forEach((line) => {
			if (!line.trim()) {
				return;
			}
			try {
				const payload = JSON.parse(line);
				if (typeof payload.progress !== "undefined") {
					setRestoreDatabaseProgress(
						elements,
						payload.progress,
						payload.message,
						payload.details,
					);
				}
				if (payload.status === false && payload.message) {
					setRestoreDatabaseProgress(
						elements,
						payload.progress || 0,
						"Restore failed",
						payload.message,
					);
				}
			} catch (err) {
			}
		});
	}

	function handleRestoreDatabaseFormSubmit(form, event) {
		event.preventDefault();
		event.stopPropagation();

		const elements = getRestoreDatabaseElements(form);
		if (!elements.button) {
			return;
		}

		if (!elements.fileInput || !elements.fileInput.files || !elements.fileInput.files.length) {
			setRestoreDatabaseProgress(elements, 0, "Please choose a backup file", "");
			return;
		}

		const originalLabel = elements.button.innerHTML;
		elements.button.disabled = true;
		elements.button.classList.add("opacity-70", "cursor-not-allowed");
		setRestoreDatabaseProgress(elements, 0, "Uploading backup file", "Preparing restore request");

		const xhr = new XMLHttpRequest();
		const cursor = { value: 0 };
		xhr.open((form.getAttribute("method") || "post").toUpperCase(), form.getAttribute("action") || "", true);
		xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

		xhr.upload.onprogress = (uploadEvent) => {
			if (uploadEvent.lengthComputable) {
				const uploadPercent = Math.round((uploadEvent.loaded / uploadEvent.total) * 100);
				setRestoreDatabaseProgress(
					elements,
					uploadPercent,
					"Uploading backup file",
					`${uploadEvent.loaded} / ${uploadEvent.total} bytes`,
				);
			}
		};

		xhr.onprogress = () => {
			parseRestoreProgress(xhr.responseText || "", cursor, elements);
		};

		xhr.onload = () => {
			parseRestoreProgress(xhr.responseText || "", cursor, elements);
			const lines = (xhr.responseText || "").trim().split(/\r?\n/);
			const finalLine = lines[lines.length - 1];
			try {
				const payload = finalLine ? JSON.parse(finalLine) : null;
				if (payload && payload.status) {
					setRestoreDatabaseProgress(
						elements,
						100,
						payload.message || "Restore completed",
						"Refreshing page...",
					);
					window.setTimeout(() => {
						window.location.href = `${base_url}admin/settings/database_backup`;
					}, 1200);
				} else {
					setRestoreDatabaseProgress(
						elements,
						(payload && payload.progress) || 0,
						"Restore failed",
						(payload && payload.message) || "Unable to restore database.",
					);
				}
			} catch (err) {
				setRestoreDatabaseProgress(elements, 100, "Restore completed", "Refreshing page...");
				window.setTimeout(() => {
					window.location.href = `${base_url}admin/settings/database_backup`;
				}, 1200);
			}
			elements.button.disabled = false;
			elements.button.classList.remove("opacity-70", "cursor-not-allowed");
			elements.button.innerHTML = originalLabel;
		};

		xhr.onerror = () => {
			setRestoreDatabaseProgress(elements, 0, "Restore failed", "The request could not be completed.");
			elements.button.disabled = false;
			elements.button.classList.remove("opacity-70", "cursor-not-allowed");
			elements.button.innerHTML = originalLabel;
		};

		xhr.send(new FormData(form));
	}

	function initCommentForms() {
		document.querySelectorAll("form[data-comment-form]").forEach((form) => {
			if (form.dataset.commentInit === "true") return;
			form.dataset.commentInit = "true";
			form.addEventListener("submit", (e) => {
				e.preventDefault();
				if (
					window.FormValidator &&
					typeof window.FormValidator.validateForm === "function"
				) {
					if (!window.FormValidator.validateForm(form)) return;
				}
				submitCommentForm(form);
			});
		});
	}

	function initReplyForms() {
		document
			.querySelectorAll("form[data-comment-reply-form]")
			.forEach((form) => {
				if (form.dataset.replyInit === "true") return;
				form.dataset.replyInit = "true";
				form.addEventListener("submit", (e) => {
					e.preventDefault();
					if (
						window.FormValidator &&
						typeof window.FormValidator.validateForm === "function"
					) {
						if (!window.FormValidator.validateForm(form)) return;
					}
					submitReplyForm(form);
				});
			});
	}

	function normalizeRelatedSelect(container) {
		if (!container) return;
		container.classList.add("grid", "grid-cols-1", "md:grid-cols-2", "gap-4");
		container
			.querySelectorAll(".col-sm-5, .col-sm-7, .col-sm-4, .col-sm-8")
			.forEach((el) => {
				el.classList.add("w-full");
			});
		container.querySelectorAll("select").forEach((select) => {
			if (select.id === "related_to") {
				select.id = "";
			}
			select.classList.remove("form-control", "selectpicker", "m0");
			select.classList.add(
				"tw-selectpicker",
				"w-full",
				"rounded-lg",
				"border",
				"border-border",
				"bg-input",
				"px-3",
				"py-2",
				"text-sm",
				"focus-visible:outline-none",
				"focus-visible:ring-2",
				"focus-visible:ring-secondary/30",
			);
			if (!select.dataset.placeholder) {
				select.dataset.placeholder = "Nothing selected";
			}
			if (window.refreshSelectpicker) {
				window.refreshSelectpicker(select);
			}
		});
	}

	function debounce(fn, wait) {
		let timeout;
		return function (...args) {
			clearTimeout(timeout);
			timeout = setTimeout(() => fn.apply(this, args), wait);
		};
	}

	function parseLookupDepends(depends, wrap) {
		if (!depends) return [];
		const form = wrap.closest("form") || document;
		return depends
			.split(",")
			.map((token) => token.trim())
			.filter(Boolean)
			.map((token) => {
				let key = token;
				let selector = "";
				if (token.indexOf(":") !== -1) {
					const parts = token.split(":");
					key = parts[0].trim();
					selector = parts.slice(1).join(":").trim();
				}
				let element = null;
				if (selector) {
					element = document.querySelector(selector);
				} else {
					element =
						form.querySelector(`[name="${key}"]`) ||
						document.querySelector(`[name="${key}"]`) ||
						document.getElementById(key);
				}
				return { key, element };
			});
	}

	function formatAmount(value, symbol) {
		const safe = Number.isFinite(value) ? value : 0;
		const formatted = safe.toFixed(2);
		return symbol ? `${symbol}${formatted}` : formatted;
	}

	window.initLookup = function (scope) {
		const root = scope || document;
		const lookups = root.querySelectorAll(
			"[data-lookup], [data-project-lookup]",
		);
		if (!lookups.length) return;

		const recentStorageKey = "erp_lookup_recent_v1";
		const readLookupRecentStore = () => {
			try {
				return JSON.parse(localStorage.getItem(recentStorageKey) || "{}");
			} catch (e) {
				return {};
			}
		};
		const writeLookupRecentStore = (data) => {
			try {
				localStorage.setItem(recentStorageKey, JSON.stringify(data));
			} catch (e) {}
		};
		const getLookupRecentItems = (type) => {
			const data = readLookupRecentStore();
			return Array.isArray(data[type]) ? data[type] : [];
		};
		const pushLookupRecentItem = (type, item) => {
			if (!type || !item || !item.id || !item.text) return;
			const data = readLookupRecentStore();
			const current = Array.isArray(data[type]) ? data[type] : [];
			const next = [item].concat(
				current.filter((entry) => String(entry.id) !== String(item.id)),
			).slice(0, 8);
			data[type] = next;
			writeLookupRecentStore(data);
		};

		lookups.forEach((wrap) => {
			if (wrap.dataset.lookupReady || wrap.dataset.projectLookupReady) return;
			wrap.dataset.lookupReady = "1";

			const input =
				wrap.querySelector("[data-lookup-input]") ||
				wrap.querySelector("[data-project-lookup-input]");
			const hidden =
				wrap.querySelector("[data-lookup-hidden]") ||
				wrap.querySelector("[data-project-lookup-hidden]");
			const results =
				wrap.querySelector("[data-lookup-results]") ||
				wrap.querySelector("[data-project-lookup-results]");
			const status =
				wrap.querySelector("[data-lookup-status]") ||
				wrap.querySelector("[data-project-lookup-status]");
			const clearBtn = wrap.querySelector("[data-lookup-clear]");
			const isMultiple = wrap.dataset.lookupMultiple === "1";
			const hiddenList = wrap.querySelector("[data-lookup-hidden-list]");
			const chipsWrap = wrap.querySelector("[data-lookup-chips]");
			const hiddenName = wrap.dataset.lookupName || "";

			const lookupType =
				wrap.dataset.lookupType ||
				wrap.dataset.projectLookupType ||
				(wrap.dataset.projectLookup !== undefined ? "project" : "");
			const endpoint =
				wrap.dataset.lookupEndpoint ||
				wrap.dataset.projectLookupEndpoint ||
				(lookupType
					? `${window.base_url}admin/common/${lookupType}_lookup`
					: `${window.base_url}admin/common/project_lookup`);
			const limit = parseInt(
				wrap.dataset.lookupLimit || wrap.dataset.projectLookupLimit || "4",
				10,
			);
			const minLength = parseInt(wrap.dataset.lookupMinLength || "0", 10);
			const onChange =
				wrap.dataset.lookupOnchange || wrap.dataset.projectLookupOnchange || "";
			const depends = wrap.dataset.lookupDepends || "";
			let valueMap = {};
			let extraParams = {};
			try {
				extraParams = JSON.parse(wrap.dataset.lookupExtra || "{}");
			} catch (e) {
				extraParams = {};
			}
			try {
				valueMap = JSON.parse(wrap.dataset.lookupValueMap || "{}");
			} catch (e) {
				valueMap = {};
			}
			const stableParamsKey = (params) => {
				const sorted = {};
				Object.keys(params || {})
					.sort()
					.forEach((key) => {
						sorted[key] = params[key];
					});
				return JSON.stringify(sorted);
			};
			
			
			const cache = new Map();

			if (!input || !results) return;

			let lastQuery = null;
			let controller = null;
			let resultsPlaceholder = null;
			let resultsPortaled = false;

			const positionFloatingResults = () => {
				if (!resultsPortaled || results.classList.contains("hidden")) return;
				const rect = input.getBoundingClientRect();
				const gap = 4;
				const viewportPadding = 12;
				const maxHeight = Math.max(
					120,
					window.innerHeight - rect.bottom - viewportPadding - gap,
				);
				results.style.position = "fixed";
				results.style.left = `${rect.left}px`;
				results.style.top = `${rect.bottom + gap}px`;
				results.style.width = `${rect.width}px`;
				results.style.maxHeight = `${Math.min(224, maxHeight)}px`;
				results.style.zIndex = "10050";
			};

			const portalResults = () => {
				if (resultsPortaled) {
					positionFloatingResults();
					return;
				}
				resultsPlaceholder = document.createComment("lookup-results-placeholder");
				results.parentNode.insertBefore(resultsPlaceholder, results);
				document.body.appendChild(results);
				resultsPortaled = true;
				positionFloatingResults();
			};

			const restoreResults = () => {
				if (!resultsPortaled) return;
				if (resultsPlaceholder && resultsPlaceholder.parentNode) {
					resultsPlaceholder.parentNode.insertBefore(results, resultsPlaceholder);
					resultsPlaceholder.remove();
				}
				resultsPlaceholder = null;
				resultsPortaled = false;
				results.style.position = "";
				results.style.left = "";
				results.style.top = "";
				results.style.width = "";
				results.style.maxHeight = "";
				results.style.zIndex = "";
			};

			const normalizeLookupValue = (value) => {
				const idString = String(value);
				return Object.prototype.hasOwnProperty.call(valueMap, idString)
					? String(valueMap[idString])
					: idString;
			};

			const getSelectedIds = () => {
				if (isMultiple) {
					if (!hiddenList) return [];
					return Array.from(
						hiddenList.querySelectorAll("[data-lookup-hidden-item]"),
					).map((node) => String(node.value));
				}
				if (hidden && hidden.value) {
					return [String(hidden.value)];
				}
				return [];
			};

			const updateClearButton = () => {
				if (!clearBtn) return;
				const hasValue = isMultiple
					? getSelectedIds().length > 0 || Boolean(input.value.trim())
					: Boolean(hidden && hidden.value);
				clearBtn.classList.toggle("hidden", !hasValue);
			};

			const setStatus = (text) => {
				if (status) status.classList.remove("hidden");
				if (status) status.textContent = text || "";
			};

			const closeResults = () => {
				results.classList.add("hidden");
				restoreResults();
			};

			const renderResults = (items) => {
				portalResults();
				results.innerHTML = "";
				const selectedIds = new Set(getSelectedIds());
				const visibleItems = items.filter(
					(item) => !selectedIds.has(normalizeLookupValue(item.id)),
				);
				if (!visibleItems.length) {
					results.innerHTML =
						'<div class="px-3 py-2 text-sm text-muted-foreground">No results</div>';
					results.classList.remove("hidden");
					return;
				}
				visibleItems.forEach((item) => {
					const btn = document.createElement("button");
					btn.type = "button";
					btn.className =
						"w-full text-left px-3 py-2 text-sm text-foreground hover:bg-muted";
					btn.dataset.lookupId = item.id;
					btn.dataset.lookupLabel = item.text;
					btn.textContent = item.text;
					results.appendChild(btn);
				});
				results.classList.remove("hidden");
				positionFloatingResults();
			};

			const addMultipleSelection = (id, label) => {
				if (!hiddenList || !chipsWrap || !hiddenName) return;
				const idString = normalizeLookupValue(id);
				if (getSelectedIds().includes(idString)) return;

				const hiddenInput = document.createElement("input");
				hiddenInput.type = "hidden";
				hiddenInput.name = hiddenName;
				hiddenInput.value = idString;
				hiddenInput.setAttribute("data-lookup-hidden-item", "1");
				hiddenList.appendChild(hiddenInput);

				const chip = document.createElement("span");
				chip.className =
					"inline-flex items-center gap-1 rounded-full border border-border bg-background px-3 py-1 text-xs font-medium text-foreground";
				chip.setAttribute("data-lookup-chip", "1");
				chip.setAttribute("data-lookup-chip-id", idString);
				chip.innerHTML =
					`<span>${label}</span>` +
					`<button type="button" class="rounded-full p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground" data-lookup-chip-remove="${idString}">` +
					`<i data-lucide="x" class="h-3 w-3"></i>` +
					`</button>`;
				chipsWrap.appendChild(chip);
				if (window.lucide && typeof window.lucide.createIcons === "function") {
					window.lucide.createIcons({ attrs: { "stroke-width": 1.75 } });
				}
				updateClearButton();
			};

			const removeSelectionById = (id) => {
				const idString = String(id);
				if (isMultiple) {
					if (hiddenList) {
						hiddenList
							.querySelectorAll("[data-lookup-hidden-item]")
							.forEach((node) => {
								if (String(node.value) === idString) node.remove();
							});
					}
					if (chipsWrap) {
						chipsWrap
							.querySelectorAll("[data-lookup-chip]")
							.forEach((node) => {
								if (node.dataset.lookupChipId === idString) node.remove();
							});
					}
				} else if (hidden && String(hidden.value) === idString) {
					hidden.value = "";
					input.value = "";
				}
				updateClearButton();
			};

			const collectDepends = () => {
				const params = {};
				if (!depends) return params;
				parseLookupDepends(depends, wrap).forEach((dep) => {
					if (dep.element && dep.element.value) {
						params[dep.key] = dep.element.value;
					}
				});
				return params;
			};

			const hasMissingDepends = () => {
				if (!depends) return false;
				return parseLookupDepends(depends, wrap).some(
					(dep) => !dep.element || !dep.element.value,
				);
			};

			const collectLookupParams = () => {
				const params = collectDepends();
				Object.assign(params, extraParams);
				return params;
			};

			const lookupRecentKey = (params) => {
				return [
					lookupType || "lookup",
					endpoint || "",
					stableParamsKey(params),
				].join("|");
			};

			const fetchResults = async (term) => {
				const query = term.trim();

				if (query && query.length < minLength) {
					setStatus(`Type at least ${minLength} characters`);
					closeResults();
					return;
				}
				if (hasMissingDepends()) {
					setStatus("Select the required item first");
					closeResults();
					return;
				}
				if (query === lastQuery) return;
				lastQuery = query;
				setStatus("Searching...");
				if (controller) controller.abort();
				controller = new AbortController();
				const params = collectLookupParams();
				const recentKey = lookupRecentKey(params);
				const recentItems =
					query === ""
						? getLookupRecentItems(recentKey).slice(0, limit)
						: [];
				const cacheKey = `${query}|${recentKey}`;
				if (cache.has(cacheKey)) {
					const cachedItems = cache.get(cacheKey);
					const mergedItems =
						query === ""
							? recentItems.concat(
									cachedItems.filter(
										(item) =>
											!recentItems.some(
												(recent) => String(recent.id) === String(item.id),
											),
									),
							  )
							: cachedItems;
					renderResults(mergedItems);
					setStatus(query ? "" : "Recent items");
					return;
				}

				if (query === "" && recentItems.length) {
					renderResults(recentItems);
					setStatus("Recent items");
				}
				const searchParams = new URLSearchParams();

				searchParams.set("q", query);
				searchParams.set("limit", String(limit));
				Object.keys(params).forEach((key) => {
					searchParams.set(key, params[key]);
				});
				const url = `${endpoint}?${searchParams.toString()}`;
				try {
					const res = await fetch(url, {
						headers: { "X-Requested-With": "XMLHttpRequest" },
						signal: controller.signal,
					});
					const data = await res.json();
					const items = Array.isArray(data.results) ? data.results : [];
					cache.set(cacheKey, items);
					const mergedItems =
						query === ""
							? recentItems.concat(
									items.filter(
										(item) =>
											!recentItems.some(
												(recent) => String(recent.id) === String(item.id),
											),
									),
							  )
							: items;
					renderResults(mergedItems);
					setStatus(query ? "" : "Recent items");
				} catch (err) {
					if (err.name !== "AbortError") {
						console.warn("lookup failed", err);
						setStatus("Lookup failed");
					}
				}
			};

			const debouncedFetch = debounce(fetchResults, 250);

			input.addEventListener("input", () => {
				if (!isMultiple && hidden) hidden.value = "";
				updateClearButton();
				debouncedFetch(input.value);
			});

			input.addEventListener("focus", () => {
				lastQuery = null;
				debouncedFetch(input.value);
			});

			window.addEventListener("scroll", positionFloatingResults, true);
			window.addEventListener("resize", positionFloatingResults);

			input.addEventListener("blur", () => {
				setTimeout(() => {
					if (!isMultiple && hidden && !hidden.value) {
						input.value = "";
						setStatus("Select an item from the list");
					} else if (isMultiple) {
						input.value = "";
						setStatus("");
					}
					closeResults();
					updateClearButton();
				}, 150);
			});

			results.addEventListener("click", (e) => {
				const btn = e.target.closest("button[data-lookup-id]");
				if (!btn) return;
				const id = btn.dataset.lookupId;
				const label = btn.dataset.lookupLabel;
				if (isMultiple) {
					addMultipleSelection(id, label);
					input.value = "";
				} else {
					if (hidden) hidden.value = id;
					if (hidden) {
						hidden.dispatchEvent(new Event("change", { bubbles: true }));
					}
					input.value = label || "";
				}
				closeResults();
				setStatus("");
				pushLookupRecentItem(lookupRecentKey(collectLookupParams()), {
					id,
					text: label,
				});
				updateClearButton();
				if (onChange) {
					onChange
						.split(",")
						.map((fn) => fn.trim())
						.filter(Boolean)
						.forEach((fn) => {
							if (typeof window[fn] === "function") {
								window[fn](id, label, wrap);
							}
						});
				}
				const event = new CustomEvent("lookup:change", {
					detail: { id, label, type: lookupType, wrapper: wrap },
				});
				wrap.dispatchEvent(event);
				document.dispatchEvent(event);
			});

			if (chipsWrap) {
				chipsWrap.addEventListener("click", (e) => {
					const removeBtn = e.target.closest("[data-lookup-chip-remove]");
					if (!removeBtn) return;
					removeSelectionById(removeBtn.dataset.lookupChipRemove);
				});
			}

			if (clearBtn) {
				clearBtn.addEventListener("click", () => {
					if (isMultiple) {
						getSelectedIds().forEach((id) => removeSelectionById(id));
						input.value = "";
					} else {
						if (hidden) hidden.value = "";
						input.value = "";
						if (hidden) {
							hidden.dispatchEvent(new Event("change", { bubbles: true }));
						}
					}
					lastQuery = null;
					closeResults();
					setStatus("");
					updateClearButton();
				});
			}

			if (depends) {
				parseLookupDepends(depends, wrap).forEach((dep) => {
					if (!dep.element) return;
					dep.element.addEventListener("change", () => {
						if (!isMultiple && hidden) hidden.value = "";
						if (input) input.value = "";
						if (isMultiple) {
							getSelectedIds().forEach((id) => removeSelectionById(id));
						}
						setStatus("");
						closeResults();
						updateClearButton();
					});
				});
			}

			updateClearButton();

			document.addEventListener("click", (e) => {
				if (!wrap.contains(e.target)) {
					closeResults();
				}
			});
		});
	};

	window.initGroupedLookup = function (scope) {
		const root = scope || document;
		const lookups = root.querySelectorAll("[data-grouped-lookup]");
		if (!lookups.length) return;

		lookups.forEach((wrap) => {
			if (wrap.dataset.groupedLookupReady === "1") return;
			wrap.dataset.groupedLookupReady = "1";

			const rootWrap = wrap.querySelector("[data-grouped-lookup-root]") || wrap;
			const input = wrap.querySelector("[data-grouped-lookup-input]");
			const hidden = wrap.querySelector("[data-grouped-lookup-hidden]");
			const results = wrap.querySelector("[data-grouped-lookup-results]");
			const clearButton = wrap.querySelector("[data-grouped-lookup-clear]");
			const sourceNode = wrap.querySelector("[data-grouped-lookup-source]");

			if (!input || !hidden || !results || !sourceNode) return;

			let groups = [];
			try {
				groups = JSON.parse(sourceNode.textContent || "[]");
			} catch (err) {
				groups = [];
			}

			const escapeHtml = (value) =>
				String(value ?? "")
					.replace(/&/g, "&amp;")
					.replace(/</g, "&lt;")
					.replace(/>/g, "&gt;")
					.replace(/"/g, "&quot;")
					.replace(/'/g, "&#39;");

			const escapeAttr = (value) => escapeHtml(value);

			const setClearVisibility = () => {
				const hasValue = Boolean((hidden.value || "").trim()) || Boolean((input.value || "").trim());
				if (!clearButton) return;
				clearButton.classList.toggle("hidden", !hasValue);
				clearButton.classList.toggle("flex", hasValue);
			};

			const renderResults = (query) => {
				const term = (query || "").trim().toLowerCase();
				const selectedValue = String(hidden.value || "").trim();
				const markup = [];
				const avatarPalette = [
					{
						avatar: "bg-slate-700 text-slate-100 border-slate-600 dark:bg-slate-700 dark:text-slate-100 dark:border-slate-500",
					},
					{
						avatar: "bg-blue-900 text-blue-200 border-blue-800 dark:bg-blue-900 dark:text-blue-200 dark:border-blue-700",
					},
					{
						avatar: "bg-emerald-900 text-emerald-200 border-emerald-800 dark:bg-emerald-900 dark:text-emerald-200 dark:border-emerald-700",
					},
					{
						avatar: "bg-violet-900 text-violet-200 border-violet-800 dark:bg-violet-900 dark:text-violet-200 dark:border-violet-700",
					},
				];
				const pickPalette = (value) => {
					const source = String(value || "");
					let hash = 0;
					for (let i = 0; i < source.length; i += 1) {
						hash = ((hash << 5) - hash) + source.charCodeAt(i);
						hash |= 0;
					}
					return avatarPalette[Math.abs(hash) % avatarPalette.length];
				};

				groups.forEach((group) => {
					const label = String(group.label || "");
					const labelTerm = label.toLowerCase();
					const items = Array.isArray(group.items) ? group.items.filter((item) => {
						if (!term) return true;
						return String(item.text || "").toLowerCase().includes(term)
							|| String(item.name || "").toLowerCase().includes(term)
							|| String(item.meta || "").toLowerCase().includes(term)
							|| labelTerm.includes(term);
					}) : [];

					if (!items.length) return;

					markup.push('<section class="overflow-hidden rounded-[1.1rem] border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-950">');
					markup.push(
						'<header class="flex items-center border-b border-slate-200 bg-slate-100/70 px-4 py-2.5 dark:border-slate-800 dark:bg-slate-900/60">' +
						'<span class="text-[0.72rem] font-semibold uppercase tracking-[0.18em] text-slate-500 dark:text-slate-400">' + escapeHtml(label) + '</span>' +
						'</header>'
					);
					markup.push('<div class="divide-y divide-slate-200 dark:divide-slate-800">');

					items.forEach((item) => {
						const safeText = String(item.text || "");
						const safeDisplay = String(item.display || item.name || safeText);
						const safeMeta = String(item.meta || "");
						const safeId = String(item.id || "");
						const selectedClass = selectedValue !== "" && selectedValue === safeId
							? " bg-slate-100 dark:bg-slate-800/70"
							: "";
						const initials = safeDisplay
							.trim()
							.split(/\s+/)
							.filter(Boolean)
							.slice(0, 2)
							.map((part) => part.charAt(0).toUpperCase())
							.join("") || safeDisplay.charAt(0).toUpperCase() || "?";
						const palette = pickPalette(safeId || safeDisplay);
						const metaMarkup = safeMeta
							? '<span class="mt-0.5 block text-[0.88rem] font-normal text-slate-500 dark:text-slate-400">' + escapeHtml(safeMeta) + '</span>'
							: "";
						markup.push(
							'<button type="button" class="group flex w-full items-center gap-3 px-4 py-2.5 text-left transition hover:bg-slate-100/70 dark:hover:bg-slate-900/70' + selectedClass + '" data-grouped-lookup-option="' + escapeAttr(safeId) + '" data-grouped-lookup-label="' + escapeAttr(safeText) + '">' +
							'<span class="relative flex h-9 w-9 flex-none items-center justify-center rounded-full border text-[0.78rem] font-semibold tracking-[0.08em] ' + palette.avatar + '" aria-hidden="true">' + escapeHtml(initials) + '</span>' +
							'<span class="min-w-0 flex-1">' +
							'<span class="block truncate text-[0.95rem] font-medium leading-5 text-slate-900 dark:text-slate-100">' + escapeHtml(safeDisplay) + '</span>' +
							metaMarkup +
							'</span>' +
							(safeMeta ? '<span class="inline-flex min-w-[5rem] justify-center rounded-md border border-slate-300 bg-slate-100 px-2.5 py-1 text-[0.68rem] font-medium text-slate-500 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400">' + escapeHtml(safeMeta) + '</span>' : '<span class="min-w-[5rem]"></span>') +
							'</button>'
						);
					});

					markup.push("</div></section>");
				});

				results.innerHTML = markup.length
					? markup.join("")
					: '<div class="rounded-[0.85rem] border border-dashed border-slate-300 bg-slate-50 px-4 py-3 text-center text-sm text-slate-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-400">No results found.</div>';
				results.classList.remove("hidden");
			};

			const openResults = () => {
				renderResults(input.value);
			};

			const closeResults = () => {
				results.classList.add("hidden");
			};

			input.addEventListener("focus", openResults);
			input.addEventListener("input", () => {
				if (hidden) hidden.value = "";
				setClearVisibility();
				openResults();
			});

			if (clearButton) {
				clearButton.addEventListener("click", () => {
					input.value = "";
					hidden.value = "";
					setClearVisibility();
					openResults();
				});
			}

			results.addEventListener("click", (event) => {
				const option = event.target.closest("[data-grouped-lookup-option]");
				if (!option) return;
				hidden.value = option.getAttribute("data-grouped-lookup-option") || "";
				input.value = option.getAttribute("data-grouped-lookup-label") || "";
				hidden.dispatchEvent(new Event("change", { bubbles: true }));
				closeResults();
				setClearVisibility();
			});

			document.addEventListener("click", (event) => {
				if (!rootWrap.contains(event.target)) {
					closeResults();
				}
			});

			setClearVisibility();
		});
	};

	window.initBatchPaymentModal = function (scope) {
		const root = scope || document;
		const modals = root.matches?.("[data-batch-payment-modal]")
			? [root]
			: Array.from(root.querySelectorAll("[data-batch-payment-modal]"));
		if (!modals.length) return;

		const formatMoney = (value) => {
			const amount = Number(value || 0);
			if (!Number.isFinite(amount)) {
				return "0.00";
			}
			return amount.toLocaleString(undefined, {
				minimumFractionDigits: 2,
				maximumFractionDigits: 2,
			});
		};

		const replaceModalContent = (currentRoot, htmlText) => {
			const parser = new DOMParser();
			const doc = parser.parseFromString(htmlText, "text/html");
			const nextRoot = doc.querySelector("[data-batch-payment-modal]");
			if (!nextRoot) return;
			currentRoot.replaceWith(nextRoot);
			if (window.initDatePickers) window.initDatePickers();
			if (window.lucide && typeof window.lucide.createIcons === "function") {
				window.lucide.createIcons();
			}
			window.initBatchPaymentModal(nextRoot);
		};

		modals.forEach((modalRoot) => {
			const form = modalRoot.querySelector("[data-batch-payment-form]");
			const draftUrl = form ? form.getAttribute("data-draft-url") : "";
			const pageUrl = form ? form.getAttribute("data-page-url") : "";
			const clientLocked = form && form.getAttribute("data-lock-client") === "1";
			const clientFilter = modalRoot.querySelector("#batchPaymentClientFilter");
			const pageSizeSelect = modalRoot.querySelector("#batchPaymentPageSize");
			const selectedTotal = modalRoot.querySelector("[data-batch-selected-total]");
			const fillVisibleButton = modalRoot.querySelector("[data-batch-fill-visible]");
			const clearVisibleButton = modalRoot.querySelector("[data-batch-clear-visible]");

			const refreshCurrentPageTotal = () => {
				if (!selectedTotal) return;
				let total = 0;
				modalRoot.querySelectorAll("[data-batch-amount]").forEach((input) => {
					total += Number(input.value || 0);
				});
				selectedTotal.textContent = formatMoney(total);
			};
			modalRoot.__batchPaymentRefresh = refreshCurrentPageTotal;

			const saveDraft = () => {
				if (!form || !draftUrl) return Promise.resolve();
				const formData = new FormData(form);
				return fetch(draftUrl, {
					method: "POST",
					body: formData,
					headers: { "X-Requested-With": "XMLHttpRequest" },
					credentials: "same-origin",
				}).catch(() => null);
			};

			const navigate = (url) => {
				if (!url) return;
				saveDraft().then(() =>
					fetch(url, {
						headers: { "X-Requested-With": "XMLHttpRequest" },
						credentials: "same-origin",
					})
						.then((response) => response.text())
						.then((htmlText) => replaceModalContent(modalRoot, htmlText))
						.catch(() => null)
				);
			};

			refreshCurrentPageTotal();

			if (!modalRoot.__batchPaymentBound) {
				if (fillVisibleButton) {
					fillVisibleButton.addEventListener("click", (event) => {
						event.preventDefault();
						modalRoot.querySelectorAll("[data-batch-row]").forEach((row) => {
							const amountInput = row.querySelector("[data-batch-amount]");
							if (!amountInput) return;
							amountInput.value = row.getAttribute("data-due-amount") || "";
							const selectInput = row.querySelector("[data-batch-select]");
							if (selectInput) selectInput.checked = true;
						});
						refreshCurrentPageTotal();
					});
				}
				modalRoot.__batchPaymentFillVisible = () => {
					modalRoot.querySelectorAll("[data-batch-row]").forEach((row) => {
						const amountInput = row.querySelector("[data-batch-amount]");
						if (!amountInput) return;
						amountInput.value = row.getAttribute("data-due-amount") || "";
						const selectInput = row.querySelector("[data-batch-select]");
						if (selectInput) selectInput.checked = true;
					});
					refreshCurrentPageTotal();
				};

				if (clearVisibleButton) {
					clearVisibleButton.addEventListener("click", (event) => {
						event.preventDefault();
						modalRoot.querySelectorAll("[data-batch-amount]").forEach((input) => {
							input.value = "";
						});
						modalRoot.querySelectorAll("[data-batch-select]").forEach((input) => {
							input.checked = false;
						});
						refreshCurrentPageTotal();
					});
				}
				modalRoot.__batchPaymentClearVisible = () => {
					modalRoot.querySelectorAll("[data-batch-amount]").forEach((input) => {
						input.value = "";
					});
					modalRoot.querySelectorAll("[data-batch-select]").forEach((input) => {
						input.checked = false;
					});
					refreshCurrentPageTotal();
				};

				modalRoot.addEventListener("input", (event) => {
					if (event.target.matches("[data-batch-amount]")) {
						refreshCurrentPageTotal();
					}
				});

				modalRoot.addEventListener("click", (event) => {
					const navButton = event.target.closest("[data-batch-nav-url]");
					if (!navButton || navButton.disabled) return;
					event.preventDefault();
					navigate(navButton.getAttribute("data-batch-nav-url"));
				});

				if (clientFilter) {
					clientFilter.addEventListener("change", () => {
						if (clientLocked) return;
						const params = new URLSearchParams();
						if (clientFilter.value) params.set("client_id", clientFilter.value);
						params.set("page", "1");
						params.set("per_page", pageSizeSelect ? pageSizeSelect.value : "25");
						params.set("keep", "1");
						navigate(`${pageUrl}?${params.toString()}`);
					});
				}

				if (pageSizeSelect) {
					pageSizeSelect.addEventListener("change", () => {
						const params = new URLSearchParams();
						if (clientFilter && clientFilter.value) params.set("client_id", clientFilter.value);
						if (clientLocked) params.set("lock_client", "1");
						params.set("page", "1");
						params.set("per_page", pageSizeSelect.value || "25");
						params.set("keep", "1");
						navigate(`${pageUrl}?${params.toString()}`);
					});
				}

				modalRoot.__batchPaymentBound = true;
			}
		});

		if (window.__batchPaymentModalDelegatesBound) return;
		window.__batchPaymentModalDelegatesBound = true;

		const getModalRoot = (target) =>
			target && typeof target.closest === "function"
				? target.closest("[data-batch-payment-modal]")
				: null;

		document.addEventListener("change", (event) => {
			const modalRoot = getModalRoot(event.target);
			if (!modalRoot) return;
			if (event.target.matches("[data-batch-select]")) {
				const row = event.target.closest("[data-batch-row]");
				const amountInput = row ? row.querySelector("[data-batch-amount]") : null;
				if (amountInput) {
					amountInput.value = event.target.checked ? row.getAttribute("data-due-amount") || "" : "";
				}
				if (typeof modalRoot.__batchPaymentRefresh === "function") {
					modalRoot.__batchPaymentRefresh();
				}
			}
			if (event.target.matches("[data-batch-amount]") && typeof modalRoot.__batchPaymentRefresh === "function") {
				modalRoot.__batchPaymentRefresh();
			}
		});

		document.addEventListener("input", (event) => {
			const modalRoot = getModalRoot(event.target);
			if (!modalRoot) return;
			if (event.target.matches("[data-batch-amount]") && typeof modalRoot.__batchPaymentRefresh === "function") {
				modalRoot.__batchPaymentRefresh();
			}
		});

		document.addEventListener("click", (event) => {
			const fillBtn = event.target.closest("[data-batch-fill-visible]");
			if (fillBtn) {
				const modalRoot = getModalRoot(fillBtn);
				if (modalRoot && typeof modalRoot.__batchPaymentFillVisible === "function") {
					event.preventDefault();
					modalRoot.__batchPaymentFillVisible();
				}
				return;
			}

			const clearBtn = event.target.closest("[data-batch-clear-visible]");
			if (clearBtn) {
				const modalRoot = getModalRoot(clearBtn);
				if (modalRoot && typeof modalRoot.__batchPaymentClearVisible === "function") {
					event.preventDefault();
					modalRoot.__batchPaymentClearVisible();
				}
			}
		});
	};

	function setReminderTabState(container, targetTab) {
		if (!container) return;
		const activeClasses = [
			"border-slate-900",
			"bg-slate-900",
			"text-white",
			"dark:border-slate-100",
			"dark:bg-slate-100",
			"dark:text-slate-900",
		];
		const inactiveClasses = [
			"border-slate-200",
			"bg-white",
			"text-slate-600",
			"hover:bg-slate-50",
			"dark:border-slate-700",
			"dark:bg-slate-900",
			"dark:text-slate-300",
			"dark:hover:bg-slate-800",
		];
		const tabButtons = Array.from(container.querySelectorAll("[data-reminder-tab-btn]"));
		const tabPanels = Array.from(container.querySelectorAll("[data-reminder-tab-panel]"));
		if (!tabButtons.length || !tabPanels.length) return;

		tabButtons.forEach((button) => {
			const isActive = button.getAttribute("data-reminder-tab-btn") === targetTab;
			button.classList.remove(...activeClasses, ...inactiveClasses);
			button.classList.add(...(isActive ? activeClasses : inactiveClasses));
		});

		tabPanels.forEach((panel) => {
			panel.classList.toggle("hidden", panel.getAttribute("data-reminder-tab-panel") !== targetTab);
		});
	}

	window.initReminderModal = function (scope) {
		const root = scope || document;
		const containers = root.matches?.(".invoice-modern")
			? [root]
			: Array.from(root.querySelectorAll(".invoice-modern"));
		if (!containers.length) return;

		containers.forEach((container) => {
			const tabPanels = Array.from(container.querySelectorAll("[data-reminder-tab-panel]"));
			if (!tabPanels.length) {
				return;
			}

			const visiblePanel = tabPanels.find((panel) => !panel.classList.contains("hidden"));
			setReminderTabState(container, visiblePanel ? visiblePanel.getAttribute("data-reminder-tab-panel") : "manage");
		});
	};

	if (!window.__erpReminderTabsDelegated) {
		document.addEventListener("click", (event) => {
			const button = event.target.closest("[data-reminder-tab-btn]");
			if (!button) return;
			const container = button.closest(".invoice-modern");
			if (!container) return;
			setReminderTabState(container, button.getAttribute("data-reminder-tab-btn") || "create");
		});
		window.__erpReminderTabsDelegated = true;
	}

	window.initAutosizeTextareas = function (scope) {
		const root = scope || document;
		const textareas = root.matches?.("textarea[data-autosize]")
			? [root]
			: Array.from(root.querySelectorAll("textarea[data-autosize]"));
		textareas.forEach((textarea) => {
			if (textarea.__autosizeBound) {
				textarea.style.height = "auto";
				textarea.style.height = `${textarea.scrollHeight}px`;
				return;
			}
			const resize = () => {
				textarea.style.height = "auto";
				textarea.style.height = `${textarea.scrollHeight}px`;
			};
			textarea.addEventListener("input", resize);
			textarea.__autosizeBound = true;
			resize();
		});
	};

	window.initInvoiceSidebar = function (scope) {
		const root = scope || document;
		const sidebars = root.matches?.("[data-invoice-sidebar]")
			? [root]
			: Array.from(root.querySelectorAll("[data-invoice-sidebar]"));
		sidebars.forEach((sidebar) => {
			sidebar.__invoiceSidebarReady = true;
		});
	};

	function fetchInvoiceSidebar(sidebar, page, search) {
		if (!sidebar) return;
		const endpoint = sidebar.getAttribute("data-sidebar-endpoint");
		if (!endpoint || typeof fetch !== "function") return;

		const url = new URL(endpoint, window.location.origin);
		url.searchParams.set("page", String(page || 1));
		if (search) {
			url.searchParams.set("q", search);
		}

		if (sidebar.__invoiceSidebarAbort) {
			sidebar.__invoiceSidebarAbort.abort();
		}

		const controller = new AbortController();
		sidebar.__invoiceSidebarAbort = controller;
		sidebar.classList.add("opacity-60", "pointer-events-none");

		fetch(url.toString(), {
			credentials: "same-origin",
			headers: {
				"X-Requested-With": "XMLHttpRequest",
			},
			signal: controller.signal,
		})
			.then((response) => response.text())
			.then((html) => {
				const wrapper = document.createElement("div");
				wrapper.innerHTML = html.trim();
				const nextSidebar = wrapper.firstElementChild;
				if (!nextSidebar) return;
				sidebar.replaceWith(nextSidebar);
				if (window.refreshAppUi) {
					window.refreshAppUi(nextSidebar);
				}
			})
			.catch((error) => {
				if (error && error.name === "AbortError") {
					return;
				}
				console.error("Failed to load invoice sidebar", error);
			})
			.finally(() => {
				sidebar.classList.remove("opacity-60", "pointer-events-none");
			});
	}

	if (!window.__erpInvoiceSidebarDelegated) {
		document.addEventListener("click", (event) => {
			const pageButton = event.target.closest("[data-invoice-sidebar-page]");
			if (!pageButton) {
				const clearButton = event.target.closest("[data-invoice-sidebar-clear]");
				if (!clearButton) return;
				const sidebar = clearButton.closest("[data-invoice-sidebar]");
				if (!sidebar) return;
				const searchInput = sidebar.querySelector("[data-invoice-sidebar-search]");
				if (searchInput) {
					searchInput.value = "";
				}
				fetchInvoiceSidebar(sidebar, 1, "");
				return;
			}
			const sidebar = pageButton.closest("[data-invoice-sidebar]");
			if (!sidebar || pageButton.disabled) return;
			const searchInput = sidebar.querySelector("[data-invoice-sidebar-search]");
			const search = searchInput ? searchInput.value.trim() : "";
			const page = parseInt(pageButton.getAttribute("data-invoice-sidebar-page") || "1", 10);
			fetchInvoiceSidebar(sidebar, page, search);
		});

		document.addEventListener("input", (event) => {
			const searchInput = event.target.closest("[data-invoice-sidebar-search]");
			if (!searchInput) return;
			const sidebar = searchInput.closest("[data-invoice-sidebar]");
			if (!sidebar) return;
			if (sidebar.__invoiceSidebarDebounce) {
				clearTimeout(sidebar.__invoiceSidebarDebounce);
			}
			sidebar.__invoiceSidebarDebounce = setTimeout(() => {
				fetchInvoiceSidebar(sidebar, 1, searchInput.value.trim());
			}, 250);
		});

		window.__erpInvoiceSidebarDelegated = true;
	}

	window.initPurchaseSidebar = function (scope) {
		const root = scope || document;
		const sidebars = root.matches?.("[data-purchase-sidebar]")
			? [root]
			: Array.from(root.querySelectorAll("[data-purchase-sidebar]"));
		sidebars.forEach((sidebar) => {
			sidebar.__purchaseSidebarReady = true;
		});
	};

	function fetchPurchaseSidebar(sidebar, page, search) {
		if (!sidebar) return;
		const endpoint = sidebar.getAttribute("data-sidebar-endpoint");
		if (!endpoint || typeof fetch !== "function") return;

		const url = new URL(endpoint, window.location.origin);
		url.searchParams.set("page", String(page || 1));
		if (search) {
			url.searchParams.set("q", search);
		}

		if (sidebar.__purchaseSidebarAbort) {
			sidebar.__purchaseSidebarAbort.abort();
		}

		const controller = new AbortController();
		sidebar.__purchaseSidebarAbort = controller;
		sidebar.classList.add("opacity-60", "pointer-events-none");

		fetch(url.toString(), {
			credentials: "same-origin",
			headers: {
				"X-Requested-With": "XMLHttpRequest",
			},
			signal: controller.signal,
		})
			.then((response) => response.text())
			.then((html) => {
				const wrapper = document.createElement("div");
				wrapper.innerHTML = html.trim();
				const nextSidebar = wrapper.firstElementChild;
				if (!nextSidebar) return;
				sidebar.replaceWith(nextSidebar);
				if (window.refreshAppUi) {
					window.refreshAppUi(nextSidebar);
				}
			})
			.catch((error) => {
				if (error && error.name === "AbortError") {
					return;
				}
				console.error("Failed to load purchase sidebar", error);
			})
			.finally(() => {
				sidebar.classList.remove("opacity-60", "pointer-events-none");
			});
	}

	if (!window.__erpPurchaseSidebarDelegated) {
		document.addEventListener("click", (event) => {
			const pageButton = event.target.closest("[data-purchase-sidebar-page]");
			if (!pageButton) {
				const clearButton = event.target.closest("[data-purchase-sidebar-clear]");
				if (!clearButton) return;
				const sidebar = clearButton.closest("[data-purchase-sidebar]");
				if (!sidebar) return;
				const searchInput = sidebar.querySelector("[data-purchase-sidebar-search]");
				if (searchInput) {
					searchInput.value = "";
				}
				fetchPurchaseSidebar(sidebar, 1, "");
				return;
			}
			const sidebar = pageButton.closest("[data-purchase-sidebar]");
			if (!sidebar || pageButton.disabled) return;
			const searchInput = sidebar.querySelector("[data-purchase-sidebar-search]");
			const search = searchInput ? searchInput.value.trim() : "";
			const page = parseInt(pageButton.getAttribute("data-purchase-sidebar-page") || "1", 10);
			fetchPurchaseSidebar(sidebar, page, search);
		});

		document.addEventListener("input", (event) => {
			const searchInput = event.target.closest("[data-purchase-sidebar-search]");
			if (!searchInput) return;
			const sidebar = searchInput.closest("[data-purchase-sidebar]");
			if (!sidebar) return;
			if (sidebar.__purchaseSidebarDebounce) {
				clearTimeout(sidebar.__purchaseSidebarDebounce);
			}
			sidebar.__purchaseSidebarDebounce = setTimeout(() => {
				fetchPurchaseSidebar(sidebar, 1, searchInput.value.trim());
			}, 250);
		});

		window.__erpPurchaseSidebarDelegated = true;
	}

	window.initSalesItemsEditor = function (scope) {
		const root = scope || document;
		const editors = root.querySelectorAll("[data-sales-items]");
		if (!editors.length) return;

		window.__salesItemsEditors = window.__salesItemsEditors || {};

		editors.forEach((editor) => {
			if (editor.dataset.itemsReady) return;
			editor.dataset.itemsReady = "1";

			const tbody = editor.querySelector("[data-items-body]");
			const template = editor.querySelector("template[data-item-template]");
			const addButton = editor.querySelector("[data-add-item]");
			const discountInput = editor.querySelector("[data-discount-percent]");
			const discountType =
				editor.querySelector("[data-discount-type]") ||
				(editor.closest("form")
					? editor.closest("form").querySelector("[data-discount-type]")
					: null);
			const adjustmentInput = editor.querySelector("[data-adjustment]");
			const subtotalEl = editor.querySelector("[data-subtotal]");
			const discountEl = editor.querySelector("[data-discount-total-display]");
			const taxTotalEl = editor.querySelector("[data-tax-total]");
			const taxBreakdownEl = editor.querySelector("[data-tax-breakdown]");
			const adjustmentRowEl = editor.querySelector("[data-adjustment-row]");
			const adjustmentTotalEl = editor.querySelector("[data-adjustment-total]");
			const totalEl = editor.querySelector("[data-total]");
			const discountTotalInput = editor.querySelector("[data-discount-total]");
			const taxHiddenWrap = editor.querySelector("[data-tax-hidden]");
			const removedItemsWrap = editor.querySelector("[data-removed-items]");
			const currencySymbol = editor.dataset.currencySymbol || "";
			const itemType = editor.dataset.terget || editor.dataset.target || "invoice";
			const searchInput = editor.querySelector(`#${itemType}_item`);
			const searchResults = editor.querySelector("[data-item-search-results]");

			if (!tbody || !template) return;

			let searchAbortController = null;

			const escapeHtml = (value) =>
				String(value == null ? "" : value)
					.replace(/&/g, "&amp;")
					.replace(/</g, "&lt;")
					.replace(/>/g, "&gt;")
					.replace(/"/g, "&quot;")
					.replace(/'/g, "&#039;");

			const closeSearchResults = () => {
				if (!searchResults) return;
				searchResults.classList.add("hidden");
				searchResults.innerHTML = "";
			};

			const getWarehouseField = () => {
				const form = editor.closest("form");
				const scope = form || document;
				return (
					scope.querySelector('[name="warehouse_id"]') ||
					scope.querySelector('[name="warehouseId"]') ||
					null
				);
			};

			const clearWarehouseAlertState = () => {
				const field = getWarehouseField();
				if (!field) return;
				const wrap = field.closest(".form-group, [data-lookup], .relative, .col-sm-6, .col-sm-4") || field.parentElement;
				if (wrap) {
					wrap.classList.remove("ring-2", "ring-rose-300", "dark:ring-rose-700", "rounded-lg");
				}
			};

			const requireWarehouseSelection = () => {
				const field = getWarehouseField();
				if (!field) return true;
				const warehouseId = String(field.value || "").trim();
				if (warehouseId !== "") {
					clearWarehouseAlertState();
					return true;
				}

				const wrap = field.closest(".form-group, [data-lookup], .relative, .col-sm-6, .col-sm-4") || field.parentElement;
				if (wrap) {
					wrap.classList.add("ring-2", "ring-rose-300", "dark:ring-rose-700", "rounded-lg");
				}
				if (searchResults) {
					searchResults.innerHTML =
						'<div class="px-4 py-3 text-sm font-medium text-rose-600 dark:text-rose-400">Please select warehouse first.</div>';
					searchResults.classList.remove("hidden");
				}
				if (window.toastr && typeof window.toastr.warning === "function") {
					window.toastr.warning("Please select warehouse first.");
				}
				return false;
			};

			const getWarehouseId = () => {
				const field = getWarehouseField();
				if (field && String(field.value || "").trim() !== "") {
					return String(field.value).trim();
				}
				return "";
			};

			const buildItemSearchMarkup = (results) => {
				if (!searchResults) return;
				if (!results.length) {
					searchResults.innerHTML =
						'<div class="px-4 py-3 text-sm text-slate-500 dark:text-slate-400">No items found</div>';
					searchResults.classList.remove("hidden");
					return;
				}
				searchResults.innerHTML = results
					.map((entry) => {
						const label = entry && entry.label ? entry.label : "";
						const row = entry && entry.row ? entry.row : {};
						const desc = row && row.item_desc ? String(row.item_desc).replace(/<[^>]*>/g, "").trim() : "";
						const price =
							row && (row.unit_cost !== undefined || row.rate !== undefined)
								? formatAmount(parseFloat(row.unit_cost ?? row.rate ?? 0) || 0, currencySymbol)
								: "";
						return `
							<button type="button" class="flex w-full items-start justify-between gap-4 px-4 py-3 text-left transition hover:bg-slate-50 dark:hover:bg-slate-800/70" data-item-search-payload='${JSON.stringify(entry).replace(/'/g, "&apos;")}'>
								<span class="min-w-0">
									<span class="block truncate text-sm font-medium text-slate-900 dark:text-slate-100">${escapeHtml(label)}</span>
									<span class="mt-0.5 block truncate text-xs text-slate-500 dark:text-slate-400">${escapeHtml(desc || "Saved item")}</span>
								</span>
								<span class="shrink-0 text-xs font-semibold text-slate-500 dark:text-slate-300">${escapeHtml(price)}</span>
							</button>
						`;
					})
					.join("");
				searchResults.classList.remove("hidden");
			};

			const fetchItemSuggestions = debounce(async (term) => {
				if (!searchInput || !searchResults) return;
				const query = String(term || "").trim();
				if (query.length < 2) {
					closeSearchResults();
					return;
				}
				if (!requireWarehouseSelection()) {
					return;
				}
				if (searchAbortController) {
					searchAbortController.abort();
				}
				searchAbortController = new AbortController();
				searchResults.innerHTML =
					'<div class="px-4 py-3 text-sm text-slate-500 dark:text-slate-400">Searching...</div>';
				searchResults.classList.remove("hidden");

				const url =
					`${window.base_url}admin/common/suggestions?term=${encodeURIComponent(query)}&warehouse_id=${encodeURIComponent(getWarehouseId())}`;
				try {
					const response = await fetch(url, {
						headers: { "X-Requested-With": "XMLHttpRequest" },
						signal: searchAbortController.signal,
					});
					const data = await response.json();
					const results = Array.isArray(data)
						? data.filter((entry) => entry && entry.saved_items_id && String(entry.saved_items_id) !== "0" && entry.row)
						: [];
					buildItemSearchMarkup(results);
				} catch (error) {
					if (error.name === "AbortError") return;
					searchResults.innerHTML =
						'<div class="px-4 py-3 text-sm text-rose-500">Unable to load items</div>';
					searchResults.classList.remove("hidden");
				}
			}, 220);

			const updateNames = () => {
				const rows = tbody.querySelectorAll("tr[data-item-row]");
				rows.forEach((row, index) => {
					const idx = row.querySelector("[data-item-index]");
					if (idx) idx.textContent = String(index + 1);
					row.querySelectorAll("[data-field]").forEach((field) => {
						const key = field.dataset.field;
						if (!key) return;
						if (key === "taxname") {
							field.name = `items[${index}][taxname][]`;
						} else {
							field.name = `items[${index}][${key}]`;
						}
					});
					const taxLookup = row.querySelector("[data-tax-lookup]");
					if (taxLookup) {
						taxLookup.dataset.lookupName = `items[${index}][taxname][]`;
						taxLookup
							.querySelectorAll("[data-lookup-hidden-item]")
							.forEach((field) => {
								field.name = `items[${index}][taxname][]`;
							});
					}
				});
			};

			const getRowData = (row) => {
				const qtyInput = row.querySelector('[data-field="quantity"]');
				const rateInput = row.querySelector('[data-field="unit_cost"]');
				const taxSelect = row.querySelector('[data-field="taxname"]');
				const taxLookup = row.querySelector("[data-tax-lookup]");
				const qty = parseFloat(qtyInput ? qtyInput.value : "0") || 0;
				const rate = parseFloat(rateInput ? rateInput.value : "0") || 0;
				const lineTotal = qty * rate;
				const taxes = taxLookup
					? Array.from(taxLookup.querySelectorAll("[data-lookup-hidden-item]")).map((opt) => opt.value)
					: taxSelect
						? Array.from(taxSelect.selectedOptions).map((opt) => opt.value)
						: [];
				return { qty, rate, lineTotal, taxes };
			};

			const bindTaxLookupRow = (row) => {
				const taxLookup = row.querySelector("[data-tax-lookup]");
				if (!taxLookup || taxLookup.dataset.salesTaxBound === "1") return;
				taxLookup.dataset.salesTaxBound = "1";
				taxLookup.addEventListener("lookup:change", () => {
					updateRowTotals(row);
					updateTotals();
				});
				taxLookup.addEventListener("click", (e) => {
					if (!e.target.closest("[data-lookup-chip-remove], [data-lookup-clear]")) return;
					setTimeout(() => {
						updateRowTotals(row);
						updateTotals();
					}, 0);
				});
			};

			const updateRowTotals = (row) => {
				const data = getRowData(row);
				const totalCell = row.querySelector("[data-line-total]");
				const taxCell = row.querySelector("[data-line-tax]");
				let taxAmount = 0;
				data.taxes.forEach((tax) => {
					const rate = parseFloat(String(tax).split("|")[1]) || 0;
					taxAmount += (data.lineTotal * rate) / 100;
				});
				if (totalCell) totalCell.textContent = formatAmount(data.lineTotal, currencySymbol);
				if (taxCell) taxCell.textContent = taxAmount ? formatAmount(taxAmount, currencySymbol) : "-";
			};

			const updateTotals = () => {
				let subtotal = 0;
				const taxTotals = new Map();
				tbody.querySelectorAll("tr[data-item-row]").forEach((row) => {
					const data = getRowData(row);
					subtotal += data.lineTotal;
					data.taxes.forEach((tax) => {
						const rate = parseFloat(String(tax).split("|")[1]) || 0;
						const amount = (data.lineTotal * rate) / 100;
						const prev = taxTotals.get(tax) || 0;
						taxTotals.set(tax, prev + amount);
					});
				});

				let taxTotal = 0;
				if (taxHiddenWrap) {
					taxHiddenWrap.innerHTML = "";
				}
				taxTotals.forEach((amount, tax) => {
					taxTotal += amount;
					if (!taxHiddenWrap) return;
					const nameInput = document.createElement("input");
					nameInput.type = "hidden";
					nameInput.name = "total_tax_name[]";
					nameInput.value = tax;
					const valueInput = document.createElement("input");
					valueInput.type = "hidden";
					valueInput.name = "total_tax[]";
					valueInput.value = amount.toFixed(2);
					taxHiddenWrap.appendChild(nameInput);
					taxHiddenWrap.appendChild(valueInput);
				});

				if (taxBreakdownEl) {
					if (!taxTotals.size) {
						taxBreakdownEl.innerHTML = "";
					} else {
						taxBreakdownEl.innerHTML = Array.from(taxTotals.entries())
							.map(([tax, amount]) => {
								const parts = String(tax).split("|");
								const taxName = parts[0] || tax;
								const taxRate = parts[1] ? `(${parts[1]}%)` : "";
								return `
									<div class="flex items-center justify-between gap-3 rounded-xl border border-slate-200 bg-white px-3 py-2 text-[13px] text-slate-500 dark:border-slate-800 dark:bg-slate-950/50 dark:text-slate-400">
										<span class="min-w-0 truncate"><span class="font-medium text-slate-700 dark:text-slate-200">${escapeHtml(taxName)}</span> <span class="text-slate-400 dark:text-slate-500">${escapeHtml(taxRate)}</span></span>
										<span class="shrink-0 font-semibold text-slate-900 dark:text-slate-100">${escapeHtml(formatAmount(amount, currencySymbol))}</span>
									</div>
								`;
							})
							.join("");
					}
				}

				const discountPercent = parseFloat(discountInput ? discountInput.value : "0") || 0;
				const adjustment = parseFloat(adjustmentInput ? adjustmentInput.value : "0") || 0;
				const discountMode = discountType ? discountType.value : "percent";
				const discountBase = subtotal;
				const discountTotal = discountMode === "fixed"
					? Math.min(discountPercent, discountBase)
					: (discountBase * discountPercent) / 100;
				const grandTotal = subtotal - discountTotal + taxTotal + adjustment;

				if (subtotalEl) subtotalEl.textContent = formatAmount(subtotal, currencySymbol);
				if (discountEl) discountEl.textContent = formatAmount(discountTotal, currencySymbol);
				if (taxTotalEl) taxTotalEl.textContent = formatAmount(taxTotal, currencySymbol);
				if (adjustmentRowEl && adjustmentTotalEl) {
					if (Math.abs(adjustment) > 0.000001) {
						adjustmentRowEl.classList.remove("hidden");
						adjustmentRowEl.classList.add("flex");
						adjustmentTotalEl.textContent = formatAmount(adjustment, currencySymbol);
					} else {
						adjustmentRowEl.classList.add("hidden");
						adjustmentRowEl.classList.remove("flex");
						adjustmentTotalEl.textContent = formatAmount(0, currencySymbol);
					}
				}
				if (totalEl) totalEl.textContent = formatAmount(grandTotal, currencySymbol);
				if (discountTotalInput) discountTotalInput.value = discountTotal.toFixed(2);
			};

			const addRow = (data) => {
				const fragment = template.content.cloneNode(true);
				const row = fragment.querySelector("tr");
				if (!row) return null;
				if (data) {
					Object.keys(data).forEach((key) => {
						const field = row.querySelector(`[data-field="${key}"]`);
						if (!field) return;
						if (field.tagName === "SELECT" && field.multiple && Array.isArray(data[key])) {
							Array.from(field.options).forEach((opt) => {
								opt.selected = data[key].includes(opt.value);
							});
							return;
						}
						field.value = data[key];
					});
				}
				tbody.appendChild(fragment);
				if (window.initLookup) {
					window.initLookup(row);
				}
				bindTaxLookupRow(row);
				updateNames();
				updateRowTotals(row);
				if (window.lucide && typeof window.lucide.createIcons === "function") {
					window.lucide.createIcons({
						attrs: {
							"stroke-width": 1.75,
						},
					});
				}
				return row;
			};

			const findRowBySavedItemId = (savedItemId) => {
				const targetId = String(savedItemId == null ? "" : savedItemId).trim();
				if (!targetId || targetId === "0") return null;
				const rows = tbody.querySelectorAll("tr[data-item-row]");
				for (const row of rows) {
					const savedField = row.querySelector('[data-field="saved_items_id"]');
					if (!savedField) continue;
					if (String(savedField.value == null ? "" : savedField.value).trim() === targetId) {
						return row;
					}
				}
				return null;
			};

			const removeRow = (row) => {
				const itemsIdField = row.querySelector('[data-field="items_id"]');
				const itemsId = itemsIdField ? itemsIdField.value : "";
				if (itemsId && removedItemsWrap) {
					const input = document.createElement("input");
					input.type = "hidden";
					input.name = "removed_items[]";
					input.value = itemsId;
					removedItemsWrap.appendChild(input);
				}
				row.remove();
				updateNames();
				updateTotals();
			};

			const updateQuantityToggleStyles = () => {
				editor.querySelectorAll('input[name="show_quantity_as"]').forEach((radio) => {
					const pill = radio.nextElementSibling;
					if (!pill) return;
					pill.classList.toggle("bg-slate-900", radio.checked);
					pill.classList.toggle("text-white", radio.checked);
					pill.classList.toggle("shadow-sm", radio.checked);
					pill.classList.toggle("dark:bg-indigo-500", radio.checked);
					pill.classList.toggle("text-slate-600", !radio.checked);
					pill.classList.toggle("dark:text-slate-300", !radio.checked);
				});
			};

			const updateDiscountModeState = () => {
				if (!discountInput) return;
				const discountLabel = editor.querySelector("[data-discount-label]");
				const discountAddon = editor.querySelector("[data-discount-addon]");
				const isFixed = discountType && discountType.value === "fixed";
				if (discountLabel) {
					discountLabel.textContent = isFixed ? "Discount Amount" : "Discount (%)";
				}
				if (discountAddon) {
					discountAddon.textContent = isFixed ? currencySymbol : "%";
				}
				discountInput.min = "0";
				if (isFixed) {
					discountInput.removeAttribute("max");
				} else {
					discountInput.max = "100";
				}
			};

			tbody.addEventListener("input", (e) => {
				const row = e.target.closest("tr[data-item-row]");
				if (!row) return;
				updateRowTotals(row);
				updateTotals();
			});

			tbody.addEventListener("change", (e) => {
				const row = e.target.closest("tr[data-item-row]");
				if (!row) return;
				updateRowTotals(row);
				updateTotals();
			});

			tbody.addEventListener("click", (e) => {
				const btn = e.target.closest("[data-remove-item]");
				if (!btn) return;
				const row = btn.closest("tr[data-item-row]");
				if (!row) return;
				removeRow(row);
			});

			if (addButton) {
				addButton.addEventListener("click", () => {
					addRow();
					updateTotals();
				});
			}

			if (discountInput) {
				discountInput.addEventListener("input", updateTotals);
			}
			if (discountType) {
				discountType.addEventListener("change", () => {
					updateDiscountModeState();
					updateTotals();
				});
			}
			if (adjustmentInput) {
				adjustmentInput.addEventListener("input", updateTotals);
			}

			if (searchInput && searchResults) {
				searchInput.addEventListener("input", () => {
					fetchItemSuggestions(searchInput.value);
				});
				searchInput.addEventListener("focus", () => {
					if (!requireWarehouseSelection()) {
						return;
					}
					if (searchInput.value.trim().length >= 2) {
						fetchItemSuggestions(searchInput.value);
					}
				});
				searchResults.addEventListener("click", (e) => {
					const option = e.target.closest("[data-item-search-payload]");
					if (!option) return;
					try {
						const entry = JSON.parse(option.dataset.itemSearchPayload.replace(/&apos;/g, "'"));
						const row = entry && entry.row ? entry.row : {};
						addRow({
							item_name: row.item_name || "",
							item_desc: row.item_desc || "",
							hsn_code: row.hsn_code || "",
							unit: row.unit || row.unit_type || "1",
							quantity: row.qty || row.quantity || 1,
							unit_cost: row.rate || row.unit_cost || 0,
							taxname: Array.isArray(row.taxname) ? row.taxname : [],
							saved_items_id: row.saved_items_id || entry.saved_items_id || 0,
							items_id: "",
						});
						updateTotals();
						searchInput.value = "";
						closeSearchResults();
					} catch (err) {
						console.warn("item search parse failed", err);
					}
				});
				document.addEventListener("click", (e) => {
					if (!editor.contains(e.target)) {
						closeSearchResults();
					}
				});

				const warehouseField = getWarehouseField();
				if (warehouseField) {
					warehouseField.addEventListener("change", () => {
						clearWarehouseAlertState();
					});
				}
			}

			editor.querySelectorAll('input[name="show_quantity_as"]').forEach((radio) => {
				radio.addEventListener("change", () => {
					const label = editor.querySelector("[data-qty-label]");
					if (!label) return;
					const qtyLabel = label.dataset.qtyLabelQty || "Qty";
					const hoursLabel = label.dataset.qtyLabelHours || "Hours";
					const qtyHoursLabel = label.dataset.qtyLabelQtyHours || "Qty/Hours";
					label.textContent =
						radio.value === "hours"
							? hoursLabel
							: radio.value === "qty_hours"
								? qtyHoursLabel
								: qtyLabel;
					updateQuantityToggleStyles();
				});
			});
			const initialQty = editor.querySelector('input[name="show_quantity_as"]:checked');
			if (initialQty) {
				initialQty.dispatchEvent(new Event("change"));
			}
			updateDiscountModeState();
			updateQuantityToggleStyles();

			tbody.querySelectorAll("tr[data-item-row]").forEach((row) => {
				bindTaxLookupRow(row);
				updateRowTotals(row);
			});
			updateNames();
			updateTotals();

			window.__salesItemsEditors[itemType] = {
				addItem(data) {
					const incomingSavedItemId = data && data.saved_items_id ? data.saved_items_id : 0;
					if (itemType === "pos" && incomingSavedItemId) {
						const existingRow = findRowBySavedItemId(incomingSavedItemId);
						if (existingRow) {
							const qtyField = existingRow.querySelector('[data-field="quantity"]');
							const qtyToAdd = parseFloat(data && data.quantity != null ? data.quantity : "1") || 1;
							if (qtyField) {
								const currentQty = parseFloat(qtyField.value || "0") || 0;
								qtyField.value = String(currentQty + qtyToAdd);
							}
							updateRowTotals(existingRow);
							updateTotals();
							return;
						}
					}

					addRow({
						item_name: data.item_name || "",
						item_desc: data.item_desc || "",
						hsn_code: data.hsn_code || "",
						unit: data.unit || "1",
						quantity: data.quantity || 1,
						unit_cost: data.unit_cost || 0,
						taxname: Array.isArray(data.taxname) ? data.taxname : [],
						saved_items_id: data.saved_items_id || 0,
						items_id: data.items_id || "",
					});
					updateTotals();
				},
			};

			const previousManualHandler = window.addManualSalesItem;
			window.addManualSalesItem = function(data, targetType) {
				const target = targetType || itemType;
				const modernEditor =
					window.__salesItemsEditors && window.__salesItemsEditors[target]
						? window.__salesItemsEditors[target]
						: null;
				if (modernEditor && data) {
					modernEditor.addItem(data);
					return true;
				}
				if (typeof previousManualHandler === "function") {
					return previousManualHandler(data, targetType);
				}
				return false;
			};
		});
	};

	window.ins_data = async function (url) {
		if (!url) return;
		try {
			const res = await fetch(url, {
				headers: { "X-Requested-With": "XMLHttpRequest" },
			});
			const data = await res.json();
			if (data && typeof data === "object") {
				if (data.invoice_state_report_div) {
					const container = document.getElementById("invoice_state_report_div");
					if (container) {
						container.innerHTML = data.invoice_state_report_div;
					}
					return;
				}
				Object.keys(data).forEach((key) => {
					const el = document.getElementById(key);
					if (!el) return;
					if (key.endsWith("_state_report_div") || key.endsWith("_report_div")) {
						el.innerHTML = data[key];
						return;
					}
					el.textContent = data[key];
				});
			}
		} catch (err) {
			console.warn("ins_data failed", err);
		}
	};

	window.initProjectLookup = function (scope) {
		if (window.initLookup) {
			window.initLookup(scope);
		}
	};

	function initLeaveApplicationForms(root = document) {
		if (!root) return;

		const forms = [];
		if (
			typeof root.matches === "function" &&
			root.matches('[data-leave-application-form="true"]')
		) {
			forms.push(root);
		}
		if (typeof root.querySelectorAll === "function") {
			root
				.querySelectorAll('[data-leave-application-form="true"]')
				.forEach((form) => forms.push(form));
		}

		if (!forms.length) return;

		const normalizeText = (text) => String(text || "").replace(/\s+/g, " ").trim();
		const escapeHtml = (text) =>
			String(text || "")
				.replace(/&/g, "&amp;")
				.replace(/</g, "&lt;")
				.replace(/>/g, "&gt;")
				.replace(/"/g, "&quot;")
				.replace(/'/g, "&#039;");

		const renderSummaryCard = (title, rows) => {
			const items = rows
				.map((row) => {
					const totalClass = row.isTotal
						? 'inline-flex items-center rounded-lg border border-border bg-background px-2.5 py-1 text-sm font-semibold text-foreground'
						: "text-sm font-semibold text-muted-foreground";
					const meta = row.isTotal
						? ""
						: '<p class="mt-1 text-[11px] font-medium uppercase tracking-[0.12em] text-muted-foreground">Taken / Quota</p>';
					const bar = row.isTotal
						? ""
						: '<div class="mt-2 h-[3px] rounded-full bg-background"></div>';
					return (
						`<div class="py-3.5${row.isTotal ? " border-t border-border" : ""}">` +
						'<div class="flex items-center justify-between gap-4">' +
						'<div class="min-w-0">' +
						`<p class="text-sm font-semibold text-foreground">${escapeHtml(row.label)}</p>` +
						meta +
						"</div>" +
						`<span class="${totalClass}">${escapeHtml(row.value)}</span>` +
						"</div>" +
						bar +
						"</div>"
					);
				})
				.join("");

			const badgeValue = rows.length ? rows[rows.length - 1].value : "0/0";
			return (
				'<div class="overflow-hidden rounded-2xl border border-border bg-card shadow-sm">' +
				'<div class="border-b border-border px-4 py-3.5">' +
				'<div class="flex items-start justify-between gap-3">' +
				'<div class="min-w-0">' +
				'<p class="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">Leave Management</p>' +
				`<h3 class="mt-1 text-base font-semibold tracking-tight text-foreground">${escapeHtml(title)}</h3>` +
				"</div>" +
				`<span class="inline-flex items-center rounded-lg bg-primary/15 px-2.5 py-1 text-xs font-semibold text-primary">${escapeHtml(badgeValue)}</span>` +
				"</div>" +
				"</div>" +
				`<div class="px-4 py-1.5">${items}</div>` +
				"</div>"
			);
		};

		const buildSummaryFromHtml = (html) => {
			const doc = new DOMParser().parseFromString(html, "text/html");
			const titleNode = doc.querySelector(".panel-title strong, .panel-title");
			const rowNodes = Array.from(doc.querySelectorAll("table tbody tr"));
			if (!rowNodes.length) {
				return "";
			}

			const rows = rowNodes.map((row, index) => {
				const cells = row.querySelectorAll("td");
				return {
					label: normalizeText(cells[0] ? cells[0].textContent : ""),
					value: normalizeText(cells[1] ? cells[1].textContent : ""),
					isTotal: index === rowNodes.length - 1,
				};
			});

			return renderSummaryCard(
				normalizeText(
					titleNode ? titleNode.textContent : "My Leave Details",
				),
				rows,
			);
		};

		const getCurrentLeaveForm = () => {
			const visibleForms = Array.from(
				document.querySelectorAll('[data-leave-application-form="true"]'),
			).filter((form) => form.offsetParent !== null);
			if (visibleForms.length) {
				return visibleForms[visibleForms.length - 1];
			}
			return document.querySelector('[data-leave-application-form="true"]');
		};

		forms.forEach((form) => {
			if (form.dataset.leaveApplicationBound === "1") {
				return;
			}
			form.dataset.leaveApplicationBound = "1";

			const summaryContainer = form.querySelector("#leave_user_details");
			const validationNode = form.querySelector("#leave_validation_message");
			const dateFieldIds = [
				"single_day_start_date",
				"multiple_days_start_date",
				"multiple_days_end_date",
				"hours_start_date",
			];

			const getField = (id) => form.querySelector(`#${id}`);

			const setValidationMessage = (message) => {
				if (!validationNode) return;
				const text = normalizeText(message);
				validationNode.textContent = text;
				validationNode.classList.toggle("hidden", !text);
			};

			const getSelectedLeaveType = () => {
				const selected = form.querySelector('input[name="leave_type"]:checked');
				return selected ? selected.value : "single_day";
			};

			const setSectionState = (section, visible) => {
				if (!section) return;
				section.hidden = !visible;
				section.setAttribute("aria-hidden", visible ? "false" : "true");
				section.classList.toggle("hidden", !visible);
				section.querySelectorAll("input, select, textarea").forEach((field) => {
					if (field.type === "hidden") {
						return;
					}
					if (field.dataset.requiredInit === undefined) {
						field.dataset.requiredInit = field.required ? "1" : "0";
					}
					field.disabled = !visible;
					field.required = visible && field.dataset.requiredInit === "1";
				});
			};

			const syncLeaveTypeTabs = () => {
				const activeMode = getSelectedLeaveType();
				form.querySelectorAll("[data-leave-type-tab]").forEach((tab) => {
					const isActive = tab.getAttribute("data-leave-type-tab") === activeMode;
					tab.classList.toggle("bg-card", isActive);
					tab.classList.toggle("border-border", isActive);
					tab.classList.toggle("text-foreground", isActive);
					tab.classList.toggle("shadow-sm", isActive);
					tab.classList.toggle("text-muted-foreground", !isActive);
				});
			};

			const syncLeaveTypeMode = () => {
				const mode = getSelectedLeaveType();
				syncLeaveTypeTabs();
				setSectionState(getField("single_day_section"), mode === "single_day");
				setSectionState(
					getField("multiple_days_section"),
					mode === "multiple_days",
				);
				setSectionState(getField("hours_section"), mode === "hours");
				setValidationMessage("");
			};

			const getCurrentUserId = () => {
				const userField = getField("users_id") || getField("user_id");
				return userField ? userField.value : "";
			};

			const getCurrentLeaveCategoryId = () => {
				const categoryField = getField("leave_category");
				return categoryField ? categoryField.value : "";
			};

			const getLeaveDates = () => {
				const mode = getSelectedLeaveType();
				if (mode === "multiple_days") {
					return {
						start: getField("multiple_days_start_date")
							? getField("multiple_days_start_date").value
							: "",
						end: getField("multiple_days_end_date")
							? getField("multiple_days_end_date").value
							: "",
					};
				}
				if (mode === "hours") {
					return {
						start: getField("hours_start_date")
							? getField("hours_start_date").value
							: "",
						end: "",
					};
				}
				return {
					start: getField("single_day_start_date")
						? getField("single_day_start_date").value
						: "",
					end: "",
				};
			};

			const resetDateFields = () => {
				dateFieldIds.forEach((fieldId) => {
					const field = getField(fieldId);
					if (field) {
						field.value = "";
					}
				});
				setValidationMessage("");
			};

			const loadLeaveDetails = async (userId) => {
				const selectedUserId = userId || getCurrentUserId();
				if (!summaryContainer || !selectedUserId) {
					return;
				}
				try {
					const res = await fetch(
						`${window.base_url}admin/common/get_leave_details/${encodeURIComponent(selectedUserId)}`,
						{
							headers: { "X-Requested-With": "XMLHttpRequest" },
						},
					);
					const html = await res.text();
					const normalized = buildSummaryFromHtml(html);
					if (normalized) {
						summaryContainer.innerHTML = normalized;
					}
				} catch (err) {
					console.warn("leave details load failed", err);
				}
			};

			const checkAvailableLeave = async () => {
				const userId = getCurrentUserId();
				const leaveCategoryId = getCurrentLeaveCategoryId();
				const dates = getLeaveDates();
				const endDate = dates.end ? dates.end : "null";

				if (!userId || !leaveCategoryId || !dates.start) {
					setValidationMessage("");
					return;
				}

				try {
					const res = await fetch(
						`${window.base_url}admin/common/check_available_leave/${encodeURIComponent(userId)}/${encodeURIComponent(dates.start)}/${encodeURIComponent(endDate)}/${encodeURIComponent(leaveCategoryId)}`,
						{
							headers: { "X-Requested-With": "XMLHttpRequest" },
						},
					);
					const message = await res.text();
					setValidationMessage(message);
				} catch (err) {
					setValidationMessage("");
				}
			};

			form.querySelectorAll('input[name="leave_type"]').forEach((radio) => {
				radio.addEventListener("change", syncLeaveTypeMode);
			});

			const categoryField = getField("leave_category");
			if (categoryField) {
				categoryField.addEventListener("change", () => {
					resetDateFields();
					checkAvailableLeave();
				});
			}

			const userField = getField("users_id");
			if (userField) {
				userField.addEventListener("change", () => {
					resetDateFields();
					loadLeaveDetails(userField.value);
					checkAvailableLeave();
				});
			}

			const multipleStartDate = getField("multiple_days_start_date");
			if (multipleStartDate) {
				multipleStartDate.addEventListener("change", () => {
					const multipleEndDate = getField("multiple_days_end_date");
					if (multipleEndDate) {
						multipleEndDate.value = "";
					}
					checkAvailableLeave();
				});
			}

			[
				"single_day_start_date",
				"multiple_days_end_date",
				"hours_start_date",
				"hours_value",
			].forEach((fieldId) => {
				const field = getField(fieldId);
				if (!field) return;
				field.addEventListener("change", () => {
					checkAvailableLeave();
				});
			});

			form.__leaveApplicationApi = {
				loadLeaveDetails,
				checkAvailableLeave,
				syncLeaveTypeMode,
			};

			syncLeaveTypeMode();
			loadLeaveDetails(getCurrentUserId());
		});

		window.get_leave_details = function (userId) {
			const activeForm = getCurrentLeaveForm();
			if (
				activeForm &&
				activeForm.__leaveApplicationApi &&
				typeof activeForm.__leaveApplicationApi.loadLeaveDetails === "function"
			) {
				activeForm.__leaveApplicationApi.loadLeaveDetails(userId);
			}
		};

		window.check_available_leave = function () {
			const activeForm = getCurrentLeaveForm();
			if (
				activeForm &&
				activeForm.__leaveApplicationApi &&
				typeof activeForm.__leaveApplicationApi.checkAvailableLeave ===
					"function"
			) {
				activeForm.__leaveApplicationApi.checkAvailableLeave();
			}
		};
	}

	window.initLeaveApplicationForms = initLeaveApplicationForms;

	window.get_related_moduleName = async function (val, proposal, group) {
		const container = document.getElementById("related_to");
		if (!container) return;
		if (!val || val === "0") {
			container.innerHTML = "";
			return;
		}
		const url = `${window.base_url}admin/common/get_related_moduleName_by_value/${val}`;
		try {
			const res = await fetch(url, {
				headers: { "X-Requested-With": "XMLHttpRequest" },
			});
			const html = await res.text();
			container.innerHTML = html;
			normalizeRelatedSelect(container);
			if (window.initLookup) {
				window.initLookup(container);
			}
		} catch (err) {
			console.warn("related module load failed", err);
		}
	};

	window.get_milestone_by_id = async function (projectId) {
		const select =
			document.getElementById("milestone") ||
			document.querySelector('select[name="milestones_id"]');
		if (!select) return;
		if (!projectId) {
			select.innerHTML = `<option value="">None</option>`;
			if (window.refreshSelectpicker) window.refreshSelectpicker(select);
			return;
		}
		const url = `${window.base_url}admin/common/get_milestone_by_project_id/${projectId}`;
		try {
			const res = await fetch(url, {
				headers: { "X-Requested-With": "XMLHttpRequest" },
			});
			const html = await res.text();
			select.innerHTML = html || `<option value=\"\">None</option>`;
			if (window.refreshSelectpicker) window.refreshSelectpicker(select);
		} catch (err) {
			console.warn("milestone load failed", err);
		}
	};

	function findBulkTable(node) {
		if (!node || !node.closest) {
			if (node && node.querySelectorAll) {
				return Array.from(node.querySelectorAll('table.bulk_table, table.DataTables, table#DataTables')).find((table) => {
					return table.classList.contains('bulk_table') ||
						table.querySelector('thead input#select_all, thead input[data-select-all]');
				}) || null;
			}
			return null;
		}

		const table = node.closest('table');
		if (table) {
			const looksBulk = table.classList.contains('bulk_table') ||
				table.querySelector('thead input#select_all, thead input[data-select-all]');
			if (looksBulk) {
				return table;
			}
		}

		const wrapper = node.closest('.dataTables_wrapper');
		if (wrapper) {
			return wrapper.querySelector('table.bulk_table, table.DataTables, table#DataTables');
		}

		return table;
	}

	function getBulkRowCheckboxes(table) {
		if (!table) return [];
		return Array.from(
			table.querySelectorAll(
				'tbody input[type="checkbox"]:not(#select_all):not(.select_one):not([data-bulk-ignore]):not(:disabled)',
			),
		);
	}

	function getBulkSelectAllCheckbox(table) {
		if (!table) return null;
		return table.querySelector('thead input#select_all, thead input[data-select-all]');
	}

	function getBulkDeleteButtons(scope) {
		const root = scope && scope.closest
			? (scope.closest('.erp-table-shell, .erp-panel, .dataTables_wrapper, section, .erp-page') || document)
			: document;
		return Array.from(
			root.querySelectorAll(
				'.dt-bulk-delete, [data-bulk-delete], [id$="BulkDelete"], [id*="BulkDelete"], #bulk_delete',
			),
		);
	}

	function isBulkTableCheckbox(el) {
		if (!el || !el.closest) return false;
		const table = findBulkTable(el);
		if (!table) return false;
		return !!el.closest('table.bulk_table, table.DataTables, table#DataTables');
	}

	function setBulkDeleteVisibility(buttons, visible) {
		buttons.forEach((button) => {
			if (!button) return;
			// button.classList.toggle('hidden', !visible);
			// button.classList.toggle('!block', visible);
			button.setAttribute('aria-hidden', visible ? 'false' : 'true');
			button.hidden = !visible;
			button.style.display = visible ? 'inline-flex !important' : 'none !important';
		});
	}

	function syncBulkTableSelection(tableOrNode) {
		if (!tableOrNode) return;

		if (tableOrNode === document || (tableOrNode.querySelectorAll && tableOrNode.tagName !== 'TABLE')) {
			Array.from(tableOrNode.querySelectorAll('table.bulk_table, table.DataTables, table#DataTables'))
				.filter((table) => table.classList.contains('bulk_table') || table.querySelector('thead input#select_all, thead input[data-select-all]'))
				.forEach((table) => {
					syncBulkTableSelection(table);
				});
			return;
		}

		const table = tableOrNode.tagName === 'TABLE' ? tableOrNode : findBulkTable(tableOrNode);
		if (!table) return;

		const selectAll = getBulkSelectAllCheckbox(table);
		const rowChecks = getBulkRowCheckboxes(table);
		const checkedRows = rowChecks.filter((checkbox) => checkbox.checked);
		const hasChecked = checkedRows.length > 0;
		const allChecked = rowChecks.length > 0 && checkedRows.length === rowChecks.length;
		const bulkButtons = getBulkDeleteButtons(table);

		if (selectAll) {
			selectAll.checked = allChecked;
			selectAll.indeterminate = hasChecked && !allChecked;
			selectAll.setAttribute('aria-checked', selectAll.indeterminate ? 'mixed' : String(allChecked));
		}

		setBulkDeleteVisibility(bulkButtons, hasChecked);
	}

	// select_one checkbox behavior
	document.addEventListener("change", (e) => {
		const el = e.target;
		const restoreForm = el.closest && el.closest("#restoreDatabaseForm");
		if (restoreForm && el.matches && el.matches('input[type="file"]')) {
			updateRestoreDatabasePreview(getRestoreDatabaseElements(restoreForm));
			return;
		}
		if (el.matches && el.matches('table.bulk_table input[type="checkbox"], table.DataTables input[type="checkbox"], #DataTables input[type="checkbox"]')) {
			syncBulkTableSelection(el);
		}
		if (el.matches && el.matches('input[type="checkbox"]#select_all, input[type="checkbox"][data-select-all]')) {
			const table = findBulkTable(el);
			if (table) {
				getBulkRowCheckboxes(table).forEach((checkbox) => {
					checkbox.checked = el.checked;
				});
				syncBulkTableSelection(table);
			}
		}
		if (el.matches('input[type="checkbox"].select_one')) {
			const name = el.getAttribute("name");
			if (!name) return;
			if (el.checked) {
				document
					.querySelectorAll(`input[type="checkbox"].select_one[name="${name}"]`)
					.forEach((checkbox) => {
						if (checkbox !== el) {
							checkbox.checked = false;
						}
					});
			}
		}
	});

	document.addEventListener(
		"click",
		(e) => {
			const checkbox = e.target.closest && e.target.closest('input[type="checkbox"]#select_all, input[type="checkbox"][data-select-all], table.bulk_table input[type="checkbox"], table.DataTables input[type="checkbox"], #DataTables input[type="checkbox"]');
			if (!checkbox || !isBulkTableCheckbox(checkbox)) return;
			e.stopPropagation();
		},
		true,
	);

	document.addEventListener(
		"click",
		(e) => {
			const clearBtn = e.target.closest && e.target.closest("#restoreDatabaseClearFile");
			if (!clearBtn) return;
			const form = clearBtn.closest("#restoreDatabaseForm");
			if (!form) return;
			const elements = getRestoreDatabaseElements(form);
			if (elements.fileInput) {
				elements.fileInput.value = "";
			}
			updateRestoreDatabasePreview(elements);
		},
		true,
	);

	document.addEventListener(
		"submit",
		(e) => {
			const restoreForm = e.target.closest && e.target.closest("#restoreDatabaseForm");
			if (!restoreForm) return;
			handleRestoreDatabaseFormSubmit(restoreForm, e);
		},
		true,
	);

	document.addEventListener(
		"submit",
		async (e) => {
			const form = e.target.closest && e.target.closest("[data-project-member-form]");
			if (!form) return;
			e.preventDefault();
			e.stopPropagation();

			const submitButton = form.querySelector('button[type="submit"]');
			const originalLabel = submitButton ? submitButton.innerHTML : "";
			if (submitButton) {
				submitButton.disabled = true;
				submitButton.innerHTML = "Please wait...";
			}

			try {
				const response = await fetch(form.getAttribute("action") || "", {
					method: (form.getAttribute("method") || "POST").toUpperCase(),
					body: new FormData(form),
					headers: {
						"X-Requested-With": "XMLHttpRequest",
						Accept: "application/json",
					},
				});
				const payload = await response.json();

				if (!payload || payload.status !== "success") {
					if (window.toaster) {
						window.toaster.error((payload && payload.message) || "Could not update project members.");
					}
					return;
				}

				let patched = false;
				if (payload.project_id && payload.assigned_html) {
					const currentCell = document.querySelector(
						`[data-project-member-cell-content="${payload.project_id}"]`,
					);
					if (currentCell) {
						const td = currentCell.closest("td");
						if (td) {
							td.innerHTML = payload.assigned_html;
							patched = true;
						}
					}
				}

				if (window.toaster) {
					window.toaster.success(payload.message || "Updated.");
				}

				const closeButton = form.querySelector("[data-modal-close]");
				if (closeButton) {
					closeButton.click();
				}

				updateIcons(document);
				if (!patched) {
					window.setTimeout(() => {
						window.location.reload();
					}, 250);
				}
			} catch (error) {
				if (window.toaster) {
					window.toaster.error("Could not update project members.");
				}
			} finally {
				if (submitButton) {
					submitButton.disabled = false;
					submitButton.innerHTML = originalLabel || "Update";
				}
			}
		},
		true,
	);

	if (!window.erpHolidayColorPickerBound) {
		window.erpHolidayColorPickerBound = true;

		function syncHolidayColor(source, value) {
			const form = source && source.closest ? source.closest("form") : null;
			if (!form || !value) return;

			const hidden = form.querySelector("[data-holiday-color-value]");
			const picker = form.querySelector("[data-holiday-color-picker]");
			const palette = form.querySelector("[data-holiday-color-palette]");

			if (hidden) {
				hidden.value = value;
			}
			if (picker && picker.value !== value) {
				picker.value = value;
			}
			if (palette) {
				palette.querySelectorAll("[data-holiday-color-option]").forEach((option) => {
					const isActive = option.getAttribute("data-holiday-color-option") === value;
					option.classList.toggle("ring-2", isActive);
					option.classList.toggle("ring-indigo-500", isActive);
				});
			}
		}

		document.addEventListener("click", (event) => {
			const option = event.target.closest && event.target.closest("[data-holiday-color-option]");
			if (!option) return;
			event.preventDefault();
			syncHolidayColor(option, option.getAttribute("data-holiday-color-option"));
		});

		document.addEventListener("input", (event) => {
			const picker = event.target.closest && event.target.closest("[data-holiday-color-picker]");
			if (!picker) return;
			syncHolidayColor(picker, picker.value);
		});
	}

	function getCookieValue(name) {
		if (!name || !document.cookie) return "";
		const cookies = document.cookie.split("; ");
		for (const cookie of cookies) {
			const separator = cookie.indexOf("=");
			const key = separator >= 0 ? cookie.slice(0, separator) : cookie;
			if (key === name) {
				return decodeURIComponent(separator >= 0 ? cookie.slice(separator + 1) : "");
			}
		}
		return "";
	}

	function getCsrfValue() {
		const config = window.erpCsrf || {};
		const cookieName = (config.cookiePrefix || "") + (config.cookieName || "");
		return getCookieValue(cookieName) || getCookieValue(config.cookieName || "");
	}

	function isInternalForm(form) {
		const action = form.getAttribute("action") || window.location.href;
		try {
			return new URL(action, window.location.href).origin === window.location.origin;
		} catch (error) {
			return true;
		}
	}

	function ensureFormCsrf(form) {
		const config = window.erpCsrf || {};
		const tokenName = config.tokenName || "csrf_token";
		if (!form || !tokenName) return;
		const method = (form.getAttribute("method") || "get").toLowerCase();
		if (method === "get" || !isInternalForm(form)) return;

		let input = form.querySelector('input[name="' + tokenName.replace(/"/g, '\\"') + '"]');
		if (!input) {
			input = document.createElement("input");
			input.type = "hidden";
			input.name = tokenName;
			form.prepend(input);
		}
		input.value = getCsrfValue();
	}

	function ensureFormsCsrf(root = document) {
		if (!window.erpCsrf) return;
		root.querySelectorAll("form").forEach(ensureFormCsrf);
	}

	function bindCsrfProtection() {
		if (!window.erpCsrf || window.erpCsrf.bound === true) return;
		window.erpCsrf.bound = true;

		ensureFormsCsrf(document);

		document.addEventListener("submit", (event) => {
			ensureFormCsrf(event.target);
		}, true);

		if (window.MutationObserver) {
			const csrfObserver = new MutationObserver((mutations) => {
				for (const mutation of mutations) {
					for (const node of mutation.addedNodes) {
						if (!node || node.nodeType !== 1) continue;
						if (node.matches && node.matches("form")) {
							ensureFormCsrf(node);
						}
						if (node.querySelectorAll) {
							ensureFormsCsrf(node);
						}
					}
				}
			});
			csrfObserver.observe(document.body, { childList: true, subtree: true });
		}

		if (window.jQuery && typeof window.jQuery.ajaxPrefilter === "function") {
			window.jQuery.ajaxPrefilter((options) => {
				const method = (options.type || options.method || "GET").toUpperCase();
				if (method === "GET") return;

				try {
					if (options.url && new URL(options.url, window.location.href).origin !== window.location.origin) {
						return;
					}
				} catch (error) {
					// Relative or malformed URLs are handled by the app routing layer.
				}

				const tokenName = window.erpCsrf.tokenName || "csrf_token";
				const tokenValue = getCsrfValue();
				if (!tokenValue) return;

				if (typeof FormData !== "undefined" && options.data instanceof FormData) {
					if (!options.data.has(tokenName)) {
						options.data.append(tokenName, tokenValue);
					}
					return;
				}

				if (!options.data) {
					options.data = {};
				}

				if (typeof options.data === "string") {
					if (options.data.indexOf(encodeURIComponent(tokenName) + "=") === -1 && options.data.indexOf(tokenName + "=") === -1) {
						options.data += (options.data ? "&" : "") + encodeURIComponent(tokenName) + "=" + encodeURIComponent(tokenValue);
					}
					return;
				}

				if (typeof options.data === "object" && !(tokenName in options.data)) {
					options.data[tokenName] = tokenValue;
				}
			});
		}

		if (typeof window.fetch === "function" && window.fetch.__erpCsrfPatched !== true) {
			const originalFetch = window.fetch.bind(window);
			const csrfFetch = function(input, init) {
				const requestInit = init ? Object.assign({}, init) : {};
				const method = (requestInit.method || (input && input.method) || "GET").toUpperCase();
				if (method === "GET" || method === "HEAD") {
					return originalFetch(input, init);
				}

				const targetUrl = typeof input === "string" || input instanceof URL
					? String(input)
					: input && input.url
						? input.url
						: window.location.href;
				try {
					if (new URL(targetUrl, window.location.href).origin !== window.location.origin) {
						return originalFetch(input, init);
					}
				} catch (error) {
					// Relative or malformed URLs are handled by the app routing layer.
				}

				const tokenName = window.erpCsrf.tokenName || "csrf_token";
				const tokenValue = getCsrfValue();
				if (!tokenValue) {
					return originalFetch(input, init);
				}

				if (!requestInit.body) {
					const body = new URLSearchParams();
					body.append(tokenName, tokenValue);
					requestInit.body = body;
				} else if (typeof FormData !== "undefined" && requestInit.body instanceof FormData) {
					if (!requestInit.body.has(tokenName)) {
						requestInit.body.append(tokenName, tokenValue);
					}
				} else if (requestInit.body instanceof URLSearchParams) {
					if (!requestInit.body.has(tokenName)) {
						requestInit.body.append(tokenName, tokenValue);
					}
				} else if (typeof requestInit.body === "string") {
					const contentType = requestInit.headers && typeof requestInit.headers.get === "function"
						? requestInit.headers.get("Content-Type")
						: requestInit.headers && (requestInit.headers["Content-Type"] || requestInit.headers["content-type"]);
					if ((!contentType || String(contentType).indexOf("application/json") === -1) && requestInit.body.indexOf(encodeURIComponent(tokenName) + "=") === -1 && requestInit.body.indexOf(tokenName + "=") === -1) {
						requestInit.body += (requestInit.body ? "&" : "") + encodeURIComponent(tokenName) + "=" + encodeURIComponent(tokenValue);
					}
				}

				return originalFetch(input, requestInit);
			};
			csrfFetch.__erpCsrfPatched = true;
			window.fetch = csrfFetch;
		}
	}

	function init() {
		document.body.classList.add("app-ui-loading");
		bindCsrfProtection();
		bindThemeToggle();
		bindAjaxDeleteLinks();
		bindSidebarToggle();
		bindSidebarSearch();
		bindSidebarProfileMenu();
		bindInstantNavigation();
		bindSubmenus();
		updateIcons();
		initCharts();
		initTooltips();
		initCommentForms();
		initReplyForms();
		syncBulkTableSelection(document);
		if (window.jQuery && typeof window.jQuery.fn !== "undefined") {
			window.jQuery(document).on("draw.dt", "table.bulk_table, table.DataTables, table#DataTables", function() {
				syncBulkTableSelection(this);
			});
		}
		if (window.initLookup) {
			window.initLookup(document);
		}
		if (window.initGroupedLookup) {
			window.initGroupedLookup(document);
		}
		if (window.initSalesItemsEditor) {
			window.initSalesItemsEditor(document);
		}
		initLeaveApplicationForms(document);
		Promise.resolve(refreshInjectedUi(document)).finally(() => {
			window.setTimeout(() => {
				document.body.classList.remove("app-ui-loading");
			}, 50);
		});
		window.setTimeout(() => {
			refreshInjectedUi(document);
		}, 200);

		document.addEventListener("shown.bs.modal", (e) => {
			queueUiRefresh(e.target || document);
		});
		document.addEventListener("loaded.bs.modal", (e) => {
			queueUiRefresh(e.target || document);
		});
		document.addEventListener("app:modal-loaded", (e) => {
			queueUiRefresh((e.detail && e.detail.bodyEl) || document);
		});

		if (window.jQuery) {
			window.jQuery(document).ajaxComplete(function () {
				queueUiRefresh(document);
			});
		}

		if (window.MutationObserver) {
			const observer = new MutationObserver((mutations) => {
				for (const mutation of mutations) {
					for (const node of mutation.addedNodes) {
						if (
							node &&
							node.nodeType === 1 &&
							(typeof node.matches === "function" &&
								(node.matches("[data-lucide]") ||
									node.querySelector?.("[data-lucide], [data-lookup], [data-grouped-lookup], [data-chart]")))
						) {
							queueUiRefresh(node);
							return;
						}
					}
				}
			});
			observer.observe(document.body, { childList: true, subtree: true });
		}

		if (!window.erpInitCreditApplyForms) {
			window.erpInitCreditApplyForms = function(scope) {
				const rootScope = scope || document;

				function formatMoney(value, symbol) {
					const amount = Number(value) || 0;
					const parts = amount.toFixed(2).split('.');
					parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
					return (symbol || '$') + parts.join('.');
				}

				function updateCreditSummary(root) {
					if (!root) return;
					const invoiceDue = Number(root.getAttribute('data-invoice-due') || 0);
					const symbol = root.getAttribute('data-currency-symbol') || '$';
					let totalApplied = 0;
					let hasExceeded = false;
					let hasPositiveAmount = false;
					const inputs = root.querySelectorAll('.amount-credit-field');
					const submitButton = root.querySelector('[data-credit-submit]');

					inputs.forEach((input) => {
						const remaining = Number(input.getAttribute('data-credit-remaining') || 0);
						const rawValue = Number(input.value || 0);
						const value = Math.min(rawValue, remaining);
						const card = input.closest('[data-credit-amount-card]');
						const row = card ? card.closest('[data-credit-row]') : null;
						const error = card ? card.querySelector('[data-credit-error]') : null;
						const errorText = card ? card.querySelector('[data-credit-error-text]') : null;
						const summaryNote = row ? row.querySelector('[data-summary-note]') : null;
						const summaryWarning = row ? row.querySelector('[data-summary-warning]') : null;
						const exceeded = rawValue > remaining;

						if (exceeded) {
							hasExceeded = true;
						}
						if (value > 0) {
							hasPositiveAmount = true;
						}
						totalApplied += value;

						if (card) {
							card.classList.toggle('border-rose-300', exceeded);
							card.classList.toggle('ring-4', exceeded);
							card.classList.toggle('ring-rose-100', exceeded);
							card.classList.toggle('bg-rose-50/40', exceeded);
						}
						input.classList.toggle('text-rose-700', exceeded);
						input.classList.toggle('placeholder:text-rose-300', exceeded);
						input.setAttribute('aria-invalid', exceeded ? 'true' : 'false');

						if (error) {
							error.classList.toggle('hidden', !exceeded);
						}
						if (errorText) {
							errorText.textContent = 'Amount exceeds available credit.';
						}
						if (summaryNote) {
							summaryNote.classList.toggle('hidden', exceeded || value <= 0);
						}
						if (summaryWarning) {
							summaryWarning.classList.toggle('hidden', !exceeded);
						}
					});

					const applied = Math.min(totalApplied, invoiceDue);
					const remainingBalance = Math.max(invoiceDue - applied, 0);
					const appliedEl = root.querySelector('[data-summary-applied]');
					const remainingEl = root.querySelector('[data-summary-remaining]');
					const noteValues = root.querySelectorAll('[data-summary-note-value]');

					if (appliedEl) {
						appliedEl.textContent = '-' + formatMoney(applied, symbol);
					}
					if (remainingEl) {
						remainingEl.textContent = formatMoney(remainingBalance, symbol);
					}
					noteValues.forEach((noteValue) => {
						noteValue.textContent = formatMoney(remainingBalance, symbol);
					});

					if (submitButton) {
						submitButton.disabled = hasExceeded || !hasPositiveAmount;
						submitButton.classList.toggle('opacity-60', hasExceeded || !hasPositiveAmount);
						submitButton.classList.toggle('cursor-not-allowed', hasExceeded || !hasPositiveAmount);
					}
				}

				function updateActionSwitches(scope) {
					const nodes = (scope || document).querySelectorAll('[data-credit-switch]');
					nodes.forEach((toggle) => {
						const row = toggle.closest('[data-credit-action-row]');
						const shell = toggle.parentElement;
						const track = shell ? shell.querySelector('[data-credit-switch-track]') : null;
						const thumb = shell ? shell.querySelector('[data-credit-switch-thumb]') : null;
						const checked = !!toggle.checked;
						if (row) {
							row.classList.toggle('border-primary/30', checked);
							row.classList.toggle('bg-primary/5', checked);
							row.classList.toggle('ring-1', checked);
							row.classList.toggle('ring-primary/10', checked);
							row.classList.toggle('dark:bg-primary/10', checked);
							row.classList.toggle('dark:border-primary/30', checked);
							row.classList.toggle('dark:ring-primary/20', checked);
						}
						if (track) {
							track.classList.toggle('bg-slate-300', !checked);
							track.classList.toggle('dark:bg-slate-600', !checked);
							track.classList.toggle('bg-primary/90', checked);
							track.classList.toggle('dark:bg-primary', checked);
						}
						if (thumb) {
							thumb.classList.toggle('translate-x-0', !checked);
							thumb.classList.toggle('sm:translate-x-0', !checked);
							thumb.classList.toggle('translate-x-4', checked);
							thumb.classList.toggle('sm:translate-x-5', checked);
						}
					});
				}

				rootScope.querySelectorAll('[data-credit-apply-root]').forEach((root) => {
					if (root.getAttribute('data-credit-apply-bound') === '1') {
						updateCreditSummary(root);
						return;
					}
					root.setAttribute('data-credit-apply-bound', '1');
					updateCreditSummary(root);
				});

				if (!window.erpCreditApplyDelegatedBound) {
					window.erpCreditApplyDelegatedBound = true;

					const handleAmountEvent = function(event) {
						if (!event.target.classList || !event.target.classList.contains('amount-credit-field')) return;
						const root = event.target.closest('[data-credit-apply-root]');
						updateCreditSummary(root);
					};

					const syncPostmarkRows = function() {
						const masterToggle = document.querySelector('[data-credit-toggle="postmark"]');
						if (!masterToggle) return;
						document.querySelectorAll('.postmark_config').forEach((node) => {
							node.classList.toggle('hidden', !masterToggle.checked);
						});
					};

					document.addEventListener('input', handleAmountEvent);
					document.addEventListener('change', handleAmountEvent);
					document.addEventListener('keyup', handleAmountEvent);

					document.addEventListener('click', function(event) {
						const maxButton = event.target.closest('[data-apply-max]');
						if (!maxButton) return;
						event.preventDefault();
						const root = maxButton.closest('[data-credit-apply-root]');
						if (!root) return;
						const input = root.querySelector('.amount-credit-field');
						if (!input) return;
						input.value = input.getAttribute('data-credit-remaining') || '0';
						input.dispatchEvent(new Event('input', { bubbles: true }));
						input.dispatchEvent(new Event('change', { bubbles: true }));
					});

					document.addEventListener('click', function(event) {
						const row = event.target.closest('[data-credit-action-row]');
						if (!row) return;
						if (event.target && (event.target.matches('input') || event.target.closest('input'))) return;
						const input = row.querySelector('[data-credit-switch]');
						if (!input) return;
						event.preventDefault();
						input.checked = !input.checked;
						input.dispatchEvent(new Event('change', { bubbles: true }));
					});

					document.addEventListener('change', function(event) {
						if (event.target && event.target.matches('[data-credit-switch]')) {
							updateActionSwitches(event.target.closest('[data-credit-apply-root]') || document);
						}
						const toggle = event.target.closest('[data-credit-toggle="postmark"]');
						if (!toggle) return;
						syncPostmarkRows();
					});

					updateActionSwitches(document);
					syncPostmarkRows();
				}
			};

			window.erpInitCreditApplyForms(document);
			document.addEventListener('app:modal-loaded', (e) => {
				window.erpInitCreditApplyForms((e.detail && e.detail.bodyEl) || document);
			});
		}
	}

	if (!window.erpSalaryTemplatePrintBound) {
		window.erpSalaryTemplatePrintBound = true;
		window.printSalaryTemplateDetails = window.printSalaryTemplateDetails || function(trigger, printableArea) {
			const source = trigger && trigger.closest ? trigger.closest('.modal-content, .modal, body') : document;
			const textarea = source && source.querySelector ? source.querySelector('[data-salary-template-print-html]') : document.querySelector('[data-salary-template-print-html]');
			const fallback = document.getElementById(printableArea || 'printableArea');
			const printHtml = (textarea ? textarea.value : '') || window.salaryTemplatePrintHtml || (fallback ? fallback.outerHTML : '');

			if (!printHtml) {
				return false;
			}

			const printWindow = window.open('', 'PRINT', 'height=900,width=1200');
			if (!printWindow) {
				if (typeof window.erpNativePrint === 'function') {
					window.erpNativePrint();
				} else if (typeof window.print === 'function') {
					window.print();
				}
				return false;
			}

			printWindow.document.open();
			printWindow.document.write(printHtml);
			printWindow.document.close();

			let printed = false;
			const doPrint = function() {
				if (printed || printWindow.closed) {
					return;
				}
				printed = true;
				printWindow.focus();
				if (typeof printWindow.print === 'function') {
					printWindow.print();
				}
				printWindow.close();
			};

			printWindow.onload = function() {
				printWindow.setTimeout(doPrint, 350);
			};
			printWindow.setTimeout(doPrint, 1000);
			return false;
		};

		document.addEventListener('click', function(event) {
			const trigger = event.target.closest ? event.target.closest('[data-salary-template-print]') : null;
			if (!trigger) {
				return;
			}
			event.preventDefault();
			window.printSalaryTemplateDetails(trigger, trigger.getAttribute('data-print-target') || 'printableArea');
		});
	}

	if (!window.erpEmployeeSalaryPrintBound) {
		window.erpEmployeeSalaryPrintBound = true;
		window.printEmployeeSalaryDetails = window.printEmployeeSalaryDetails || function(trigger, printableArea) {
			const source = trigger && trigger.closest ? trigger.closest('.modal-content, .modal, body') : document;
			const textarea = source && source.querySelector ? source.querySelector('[data-employee-salary-print-html]') : document.querySelector('[data-employee-salary-print-html]');
			const fallback = document.getElementById(printableArea || 'printableArea');
			const printHtml = (textarea ? textarea.value : '') || window.employeeSalaryPrintHtml || (fallback ? fallback.outerHTML : '');

			if (!printHtml) {
				return false;
			}

			const printWindow = window.open('', 'PRINT', 'height=900,width=1200');
			if (!printWindow) {
				if (typeof window.erpNativePrint === 'function') {
					window.erpNativePrint();
				} else if (typeof window.print === 'function') {
					window.print();
				}
				return false;
			}

			printWindow.document.open();
			printWindow.document.write(printHtml);
			printWindow.document.close();

			let printed = false;
			const doPrint = function() {
				if (printed || printWindow.closed) {
					return;
				}
				printed = true;
				printWindow.focus();
				if (typeof printWindow.print === 'function') {
					printWindow.print();
				}
				printWindow.close();
			};

			printWindow.onload = function() {
				printWindow.setTimeout(doPrint, 350);
			};
			printWindow.setTimeout(doPrint, 1000);
			return false;
		};

		document.addEventListener('click', function(event) {
			const trigger = event.target.closest ? event.target.closest('[data-employee-salary-print]') : null;
			if (!trigger) {
				return;
			}
			event.preventDefault();
			window.printEmployeeSalaryDetails(trigger, trigger.getAttribute('data-print-target') || 'printableArea');
		});
	}

	if (!window.erpPayrollPaymentPrintBound) {
		window.erpPayrollPaymentPrintBound = true;

		function collectPrintHeadHtml() {
			return Array.from(document.querySelectorAll('link[rel="stylesheet"], style'))
				.map((node) => node.outerHTML)
				.join("\n");
		}

		function waitForPayrollPrintAssets(printWindow) {
			const printDocument = printWindow && printWindow.document ? printWindow.document : null;
			if (!printDocument) {
				return Promise.resolve();
			}

			const imagePromises = Array.from(printDocument.images || []).map((image) => {
				if (image.complete) {
					return Promise.resolve();
				}
				return new Promise((resolve) => {
					image.addEventListener("load", resolve, { once: true });
					image.addEventListener("error", resolve, { once: true });
				});
			});

			if (printDocument.readyState === "complete") {
				return Promise.all(imagePromises);
			}

			return new Promise((resolve) => {
				printWindow.addEventListener("load", resolve, { once: true });
				printWindow.setTimeout(resolve, 1200);
			}).then(() => Promise.all(imagePromises));
		}

		window.printPayrollPaymentDetails = window.printPayrollPaymentDetails || function(trigger, printableArea) {
			const source = trigger && trigger.closest ? trigger.closest("#" + (printableArea || "printableArea")) : null;
			const fallback = source || document.getElementById(printableArea || "printableArea");

			if (!fallback) {
				return false;
			}

			let printRoot = document.getElementById("erp-payroll-print-root");
			if (printRoot) {
				printRoot.remove();
			}

			let printStyle = document.getElementById("erp-payroll-print-style");
			if (!printStyle) {
				printStyle = document.createElement("style");
				printStyle.id = "erp-payroll-print-style";
				printStyle.textContent = [
					"@media print {",
					"@page { margin: 14mm; }",
					"html, body { background: #fff !important; }",
					"body.erp-payroll-printing > *:not(#erp-payroll-print-root) { display: none !important; }",
					"#erp-payroll-print-root { display: block !important; width: 100% !important; color: inherit !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }",
					"#erp-payroll-print-root .hidden-print, #erp-payroll-print-root [data-payroll-payment-print] { display: none !important; }",
					"#erp-payroll-print-root .show_print { display: block !important; }",
					"#erp-payroll-print-root a { color: inherit !important; text-decoration: none !important; }",
					"}"
				].join("");
				document.head.appendChild(printStyle);
			}

			printRoot = document.createElement("div");
			printRoot.id = "erp-payroll-print-root";
			printRoot.style.display = "none";
			printRoot.appendChild(fallback.cloneNode(true));
			document.body.appendChild(printRoot);
			document.body.classList.add("erp-payroll-printing");

			const cleanup = function() {
				document.body.classList.remove("erp-payroll-printing");
				const root = document.getElementById("erp-payroll-print-root");
				if (root) {
					root.remove();
				}
				window.removeEventListener("afterprint", cleanup);
			};

			window.addEventListener("afterprint", cleanup);
			window.setTimeout(function() {
				try {
					window.print();
				} catch (error) {
					cleanup();
					return;
				}
				window.setTimeout(cleanup, 15000);
			}, 100);
			return false;
		};

		document.addEventListener("click", function(event) {
			const trigger = event.target.closest ? event.target.closest("[data-payroll-payment-print]") : null;
			if (!trigger) {
				return;
			}
			event.preventDefault();
			window.printPayrollPaymentDetails(trigger, trigger.getAttribute("data-print-target") || "printableArea");
		});
	}

	if (document.readyState === "loading") {
		document.addEventListener("DOMContentLoaded", init);
	} else {
		init();
	}
})();
