clqms-be/app/Views/layout/main_layout.php

223 lines
8.1 KiB
PHP
Raw Normal View History

<!DOCTYPE html>
<html lang="en" data-theme="corporate">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= esc($pageTitle ?? 'CLQMS') ?> - CLQMS</title>
<!-- TailwindCSS 4 + DaisyUI 5 CDN -->
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<!-- FontAwesome -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/js/all.min.js"></script>
<!-- Alpine.js -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<style>
[x-cloak] { display: none !important; }
/* Smooth theme transition */
* {
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}
/* Custom scrollbar - light theme optimized */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: #f1f5f9; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
/* Dark theme scrollbar */
[data-theme="business"] ::-webkit-scrollbar-track { background: rgba(0,0,0,0.1); }
[data-theme="business"] ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); }
[data-theme="business"] ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.3); }
/* Sidebar transition */
.sidebar-transition { transition: width 0.3s ease, transform 0.3s ease; }
/* Menu active state enhancement */
.menu li > *:not(.menu-title):not(.btn):active,
.menu li > *:not(.menu-title):not(.btn).active {
background-color: oklch(var(--p));
color: oklch(var(--pc));
}
</style>
</head>
<body class="min-h-screen flex bg-base-200" x-data="layout()">
<!-- Sidebar -->
<aside
class="sidebar-transition fixed lg:relative z-40 h-screen bg-base-300 flex flex-col shadow-xl"
:class="sidebarOpen ? 'w-56' : 'w-0 lg:w-16'"
>
<!-- Sidebar Header -->
<div class="h-16 flex items-center justify-between px-4 border-b border-base-content/10" x-show="sidebarOpen" x-cloak>
<span class="text-xl font-bold text-primary">CLQMS</span>
</div>
<!-- Navigation -->
<nav class="flex-1 py-4 overflow-y-auto" :class="sidebarOpen ? 'px-3' : 'px-2'">
<ul class="menu space-y-2">
<!-- Dashboard -->
<li>
<a href="<?= base_url('/v2/') ?>"
:class="'<?= $activePage ?? '' ?>' === 'dashboard' ? 'active' : ''"
class="flex items-center gap-3">
<i class="fa-solid fa-th-large w-5 text-center"></i>
<span x-show="sidebarOpen" x-cloak>Dashboard</span>
</a>
</li>
<!-- Patients -->
<li>
<a href="<?= base_url('/v2/patients') ?>"
:class="'<?= $activePage ?? '' ?>' === 'patients' ? 'active' : ''"
class="flex items-center gap-3">
<i class="fa-solid fa-users w-5 text-center"></i>
<span x-show="sidebarOpen" x-cloak>Patients</span>
</a>
</li>
<!-- Lab Requests -->
<li>
<a href="<?= base_url('/v2/requests') ?>"
:class="'<?= $activePage ?? '' ?>' === 'requests' ? 'active' : ''"
class="flex items-center gap-3">
<i class="fa-solid fa-flask w-5 text-center"></i>
<span x-show="sidebarOpen" x-cloak>Lab Requests</span>
</a>
</li>
<!-- Settings -->
<li>
<a href="<?= base_url('/v2/settings') ?>"
:class="'<?= $activePage ?? '' ?>' === 'settings' ? 'active' : ''"
class="flex items-center gap-3">
<i class="fa-solid fa-cog w-5 text-center"></i>
<span x-show="sidebarOpen" x-cloak>Settings</span>
</a>
</li>
</ul>
</nav>
</aside>
<!-- Overlay for mobile -->
<div
x-show="sidebarOpen"
@click="sidebarOpen = false"
class="fixed inset-0 bg-black/50 z-30 lg:hidden"
x-cloak
></div>
<!-- Main Content Wrapper -->
<div class="flex-1 flex flex-col min-h-screen">
<!-- Top Navbar -->
<nav class="h-16 bg-base-100 border-b border-base-content/10 flex items-center justify-between px-4 sticky top-0 z-20">
<!-- Left: Burger Menu -->
<div class="flex items-center gap-4">
<button @click="sidebarOpen = !sidebarOpen" class="btn btn-ghost btn-sm btn-square">
<i class="fa-solid fa-bars text-lg"></i>
</button>
<h1 class="text-lg font-semibold"><?= esc($pageTitle ?? 'Dashboard') ?></h1>
</div>
<!-- Right: Theme Toggle & User -->
<div class="flex items-center gap-2">
<!-- Theme Toggle -->
<label class="swap swap-rotate btn btn-ghost btn-sm btn-square">
<input type="checkbox" class="theme-controller" value="corporate" @change="toggleTheme($event)" :checked="lightMode" />
<i class="swap-off fa-solid fa-moon text-lg"></i>
<i class="swap-on fa-solid fa-sun text-lg"></i>
</label>
<!-- User Dropdown -->
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-ghost btn-circle avatar placeholder">
<div class="bg-primary text-primary-content rounded-full w-10">
<span class="text-sm">U</span>
</div>
</div>
<ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2 shadow-lg border border-base-content/10">
<li><a href="#"><i class="fa-solid fa-user mr-2"></i> Profile</a></li>
<li><a href="#"><i class="fa-solid fa-cog mr-2"></i> Settings</a></li>
<li class="border-t border-base-content/10 mt-1 pt-1">
<a @click="logout()" class="text-error">
<i class="fa-solid fa-sign-out-alt mr-2"></i> Logout
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Page Content -->
<main class="flex-1 p-4 lg:p-6 overflow-auto">
<?= $this->renderSection('content') ?>
</main>
<!-- Footer -->
<footer class="bg-base-100 border-t border-base-content/10 py-4 px-6">
<div class="flex flex-col sm:flex-row items-center justify-between gap-2 text-sm text-base-content/60">
<span>© 2025 5Panda. All rights reserved.</span>
<span>CLQMS v1.0.0</span>
</div>
</footer>
</div>
<!-- Global Scripts -->
<script>
window.BASEURL = "<?= base_url() ?>";
function layout() {
return {
sidebarOpen: window.innerWidth >= 1024,
lightMode: localStorage.getItem('theme') === 'corporate',
init() {
// Apply saved theme (default to light theme)
const savedTheme = localStorage.getItem('theme') || 'corporate';
document.documentElement.setAttribute('data-theme', savedTheme);
this.lightMode = savedTheme === 'corporate';
// Handle resize
window.addEventListener('resize', () => {
if (window.innerWidth >= 1024) {
this.sidebarOpen = true;
}
});
},
toggleTheme(event) {
const theme = event.target.checked ? 'corporate' : 'business';
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
this.lightMode = event.target.checked;
},
async logout() {
try {
const res = await fetch(`${BASEURL}api/auth/logout`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
if (res.ok) {
window.location.href = `${BASEURL}v2/login`;
}
} catch (err) {
console.error('Logout error:', err);
// Force redirect even on error
window.location.href = `${BASEURL}v2/login`;
}
}
}
}
</script>
<?= $this->renderSection('script') ?>
</body>
</html>