230 lines
6.9 KiB
Svelte
230 lines
6.9 KiB
Svelte
<script lang="ts">
|
|
import { goto } from "$app/navigation";
|
|
import { onMount } from "svelte";
|
|
import { supabase } from "$lib/supabaseClient";
|
|
|
|
let user: any = null;
|
|
onMount(() => {
|
|
const userStr = localStorage.getItem("user");
|
|
if (userStr) {
|
|
user = JSON.parse(userStr);
|
|
if (user?.role) {
|
|
userRole = user.role;
|
|
}
|
|
}
|
|
|
|
activeUrl = window.location.pathname;
|
|
|
|
// Expand parent menu if current path matches sub menu
|
|
for (const item of fullMenu) {
|
|
if (item.sub?.some((s) => s.url === activeUrl)) {
|
|
openMenus[item.name] = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
type SubMenuItem = {
|
|
name: string;
|
|
icon: string;
|
|
url: string;
|
|
roles?: string[];
|
|
};
|
|
|
|
type MenuItem = {
|
|
name: string;
|
|
icon: string;
|
|
url: string;
|
|
sub?: SubMenuItem[];
|
|
roles?: string[];
|
|
};
|
|
|
|
let userRole:
|
|
| "it"
|
|
| "guest"
|
|
| "accounting"
|
|
| "ga"
|
|
| "hr"
|
|
| "s&m"
|
|
| "office"
|
|
| "hm"
|
|
| "vm"
|
|
| "it";
|
|
|
|
// Semua menu
|
|
const fullMenu: MenuItem[] = [
|
|
{
|
|
name: "Beranda",
|
|
icon: "🏠",
|
|
url: "/backoffice",
|
|
roles: [
|
|
"it",
|
|
"guest",
|
|
"accounting",
|
|
"ga",
|
|
"hr",
|
|
"s&m",
|
|
"office",
|
|
"hm",
|
|
"vm",
|
|
],
|
|
},
|
|
{
|
|
name: "Issues",
|
|
icon: "📂",
|
|
url: "/backoffice/issue",
|
|
roles: ["it", "ga", "office", "hm", "vm", "accounting"],
|
|
},
|
|
{
|
|
name: "Projects",
|
|
icon: "📂",
|
|
url: "/backoffice/project",
|
|
roles: ["it", "ga", "office", "hm", "vm", "accounting"],
|
|
},
|
|
{
|
|
name: "Purchase Orders",
|
|
icon: "📦",
|
|
url: "/backoffice/purchaseorder",
|
|
roles: ["it", "guest", "accounting", "ga", "office", "hm", "vm"],
|
|
sub: [
|
|
{
|
|
name: "PO Item",
|
|
icon: "📋",
|
|
url: "/backoffice/purchaseorder/poitem",
|
|
roles: ["it", "ga", "office", "hm", "vm", "accounting"],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Timesheets",
|
|
icon: "⏰",
|
|
url: "/backoffice/timesheets",
|
|
roles: ["it", "ga", "hr", "office", "hm", "vm"],
|
|
},
|
|
{
|
|
name: "Vendors",
|
|
icon: "🏢",
|
|
url: "/backoffice/vendor",
|
|
roles: ["it", "ga", "office", "hm", "vm"],
|
|
},
|
|
{
|
|
name: "Inventory",
|
|
icon: "📋",
|
|
url: "/backoffice/inventory",
|
|
roles: ["it", "ga", "s&m", "office", "hm", "vm"],
|
|
},
|
|
{
|
|
name: "Villa",
|
|
icon: "🏡",
|
|
url: "/backoffice/villa",
|
|
roles: ["it", "ga", "s&m", "office", "hm", "vm"],
|
|
},
|
|
{
|
|
name: "Transport",
|
|
icon: "🚗",
|
|
url: "/backoffice/transport",
|
|
roles: ["it", "ga", "s&m", "office", "hm", "vm"],
|
|
},
|
|
{
|
|
name: "Dining",
|
|
icon: "🍽️",
|
|
url: "/backoffice/dining",
|
|
roles: ["it", "ga", "s&m", "office", "hm", "vm"],
|
|
},
|
|
{
|
|
name: "Users",
|
|
icon: "👤",
|
|
url: "/backoffice/account",
|
|
roles: ["it"],
|
|
},
|
|
{
|
|
name: "Feedback",
|
|
icon: "💬",
|
|
url: "/backoffice/feedback",
|
|
roles: ["it", "ga", "s&m", "office", "hm", "vm"],
|
|
},
|
|
];
|
|
|
|
let activeUrl = "/";
|
|
let openMenus: Record<string, boolean> = {};
|
|
|
|
onMount(() => {
|
|
activeUrl = window.location.pathname;
|
|
});
|
|
|
|
function handleMenuClick(item: MenuItem) {
|
|
if (item.sub) {
|
|
goto(item.url);
|
|
activeUrl = item.url;
|
|
openMenus[item.name] = !openMenus[item.name];
|
|
} else {
|
|
activeUrl = item.url;
|
|
goto(item.url);
|
|
}
|
|
}
|
|
|
|
function handleSubClick(sub: SubMenuItem) {
|
|
activeUrl = sub.url;
|
|
goto(sub.url);
|
|
}
|
|
|
|
// Filter menu dan submenu sesuai role
|
|
$: filteredMenu = fullMenu
|
|
.filter((item) => !item.roles || item.roles.includes(userRole))
|
|
.map((item) => ({
|
|
...item,
|
|
sub: item.sub
|
|
? item.sub.filter(
|
|
(sub) => !sub.roles || sub.roles.includes(userRole),
|
|
)
|
|
: undefined,
|
|
}));
|
|
</script>
|
|
|
|
<div class="w-64 h-screen bg-white border-r shadow-sm">
|
|
<div class="p-8 border-b">
|
|
<h1 class="text-xl font-semibold text-gray-800">Backoffice</h1>
|
|
<p class="text-sm text-gray-500">Manage your application</p>
|
|
</div>
|
|
<ul class="p-2 space-y-1">
|
|
{#each filteredMenu as item}
|
|
<li>
|
|
<a
|
|
href={item.url}
|
|
class={`flex items-center gap-2 px-4 py-2 rounded transition-colors duration-150
|
|
${activeUrl === item.url ? "bg-blue-100 text-blue-600 font-semibold" : "text-gray-700 hover:bg-blue-50"}
|
|
${item.sub ? "justify-between" : ""}
|
|
`}
|
|
on:click|preventDefault={() => handleMenuClick(item)}
|
|
>
|
|
<span class="flex items-center gap-2">
|
|
<span class="text-xl">{item.icon}</span>
|
|
<span class="truncate">{item.name}</span>
|
|
</span>
|
|
{#if item.sub && item.sub.length}
|
|
<span>{openMenus[item.name] ? "▲" : "▼"}</span>
|
|
{/if}
|
|
</a>
|
|
{#if item.sub && openMenus[item.name] && item.sub.length}
|
|
<ul class="ml-8 mt-1 space-y-1">
|
|
{#each item.sub as sub}
|
|
<li>
|
|
<a
|
|
href={sub.url}
|
|
class={`flex items-center gap-2 px-3 py-1 rounded transition-colors duration-150
|
|
${activeUrl === sub.url ? "bg-blue-50 text-blue-600 font-semibold" : "text-gray-700 hover:bg-blue-50"}
|
|
`}
|
|
on:click|preventDefault={() =>
|
|
handleSubClick(sub)}
|
|
>
|
|
<span class="text-lg">{sub.icon}</span>
|
|
<span class="truncate">{sub.name}</span>
|
|
</a>
|
|
</li>
|
|
{/each}
|
|
</ul>
|
|
{/if}
|
|
</li>
|
|
{/each}
|
|
</ul>
|
|
</div>
|