Supra_App/src/lib/components/app-sidebar.svelte

237 lines
5.1 KiB
Svelte
Raw Normal View History

2026-02-17 14:30:02 +07:00
<script lang="ts">
import * as Sidebar from '$lib/components/ui/sidebar/index';
import { onDestroy, type ComponentProps } from 'svelte';
import { asset } from '$app/paths';
import AppAccountSelect from './app-account-select.svelte';
2026-03-27 14:26:44 +07:00
import { needPermission } from '$lib/core/handlers/permissionHandler';
2026-02-17 14:30:02 +07:00
import {
Code,
LayoutDashboard,
LucideEye,
CherryIcon,
DiamondIcon,
BugIcon,
2026-03-26 14:57:11 +07:00
CupSodaIcon,
Shield,
2026-03-26 14:23:12 +07:00
FileSpreadsheet
2026-02-17 14:30:02 +07:00
} from '@lucide/svelte/icons';
import TaobinLogo from '$lib/assets/logo.svelte';
import { goto } from '$app/navigation';
import Button from '$lib/components/ui/button/button.svelte';
import { sidebarStore } from '$lib/core/stores/sidebar';
2026-03-26 14:57:11 +07:00
import { auth } from '$lib/core/stores/auth';
import { isUserAdmin } from '$lib/core/admin/adminService';
2026-03-26 14:23:12 +07:00
import { referenceFromPage } from '$lib/core/stores/recipeStore';
2026-02-17 14:30:02 +07:00
let sideBar: HTMLElement | null = $state(null);
let isSideBarOpen: boolean = $state(true);
2026-03-26 14:57:11 +07:00
let isAdmin: boolean = $state(false);
2026-02-17 14:30:02 +07:00
const data = {
navMain: [
{
title: 'Home',
items: [
{
title: 'Dashboard',
url: '/dashboard',
2026-03-27 14:26:44 +07:00
icon: LayoutDashboard,
requirePerm: ''
2026-02-17 14:30:02 +07:00
}
]
},
{
title: 'Recipe',
items: [
{
title: 'Overview',
url: '/recipe/overview',
2026-03-27 14:26:44 +07:00
icon: LucideEye,
requirePerm: ''
2026-02-17 14:30:02 +07:00
},
{
title: 'Topping',
url: '/recipe/topping',
2026-03-27 14:26:44 +07:00
icon: CherryIcon,
requirePerm: ''
2026-02-17 14:30:02 +07:00
},
{
title: 'Material',
url: '/recipe/material',
2026-03-27 14:26:44 +07:00
icon: DiamondIcon,
requirePerm: ''
2026-02-17 14:30:02 +07:00
}
]
},
{
title: 'Tools',
items: [
{
title: 'Brew',
url: '/tools/brew',
2026-03-27 14:26:44 +07:00
icon: CupSodaIcon,
requirePerm: ''
2026-02-17 14:30:02 +07:00
},
{
title: 'Debug',
url: '/tools/debug',
2026-03-27 14:26:44 +07:00
icon: BugIcon,
requirePerm: ''
2026-02-17 14:30:02 +07:00
}
]
2026-03-26 14:23:12 +07:00
},
{
title: 'Sheet',
items: [
{
title: 'Overview',
url: '/departments',
2026-03-27 14:26:44 +07:00
icon: FileSpreadsheet,
requirePerm: 'document.write.*'
2026-03-26 14:23:12 +07:00
}
]
2026-02-17 14:30:02 +07:00
}
]
};
2026-03-26 14:57:11 +07:00
const adminNav = {
title: 'Admin',
items: [
{
title: 'Permissions',
url: '/admin/users',
icon: Shield
}
]
};
$effect(() => {
const currentUser = $auth;
if (currentUser) {
isUserAdmin(currentUser.uid)
.then((result) => {
isAdmin = result;
})
.catch((e) => {
console.error('Error checking admin status:', e);
isAdmin = false;
});
} else {
isAdmin = false;
}
});
2026-02-17 14:30:02 +07:00
function onClickLogoIcon() {
goto('/departments');
}
let unsubSidebar = sidebarStore.subscribe((state) => {
isSideBarOpen = state;
});
onDestroy(() => {
unsubSidebar();
});
2026-03-27 14:26:44 +07:00
let authorizedNavMain = $derived(
data.navMain.map(nav => {
const filteredItems = nav.items.filter(item => {
if (!item.requirePerm) return true;
return needPermission(item.requirePerm);
});
return { ...nav, items: filteredItems };
}).filter(nav => nav.items.length > 0)
);
2026-02-17 14:30:02 +07:00
let {
ref = sideBar,
collapsible = 'icon',
...restProps
}: ComponentProps<typeof Sidebar.Root> = $props();
</script>
<Sidebar.Root {collapsible} {...restProps}>
<Sidebar.Header>
<div class="flex items-center justify-center">
<button class="hover:cursor-pointer" onclick={onClickLogoIcon}>
<TaobinLogo size={isSideBarOpen ? 96 : 24} fillColor={'#FFFFFF'} />
</button>
</div>
</Sidebar.Header>
<Sidebar.Content>
2026-03-27 14:26:44 +07:00
{#each authorizedNavMain as nav}
2026-02-17 14:30:02 +07:00
<Sidebar.Group>
<Sidebar.GroupLabel>{nav.title}</Sidebar.GroupLabel>
<Sidebar.GroupContent>
<Sidebar.Menu>
{#each nav.items as sub}
<Sidebar.MenuItem>
<Sidebar.MenuButton>
{#snippet child({ props })}
2026-03-26 14:23:12 +07:00
<a
href={sub.url}
{...props}
onclick={(e) => {
if (nav.title === 'Sheet') {
e.preventDefault();
referenceFromPage.set('sheet');
goto(sub.url);
}
}}
>
2026-02-17 14:30:02 +07:00
{#if sub.icon}
<sub.icon />
{/if}
<span>{sub.title}</span>
</a>
{/snippet}
</Sidebar.MenuButton>
</Sidebar.MenuItem>
{/each}
</Sidebar.Menu>
</Sidebar.GroupContent>
</Sidebar.Group>
{/each}
2026-03-26 14:57:11 +07:00
{#if isAdmin}
<Sidebar.Group>
<Sidebar.GroupLabel>{adminNav.title}</Sidebar.GroupLabel>
<Sidebar.GroupContent>
<Sidebar.Menu>
{#each adminNav.items as sub}
<Sidebar.MenuItem>
<Sidebar.MenuButton>
{#snippet child({ props })}
<a
href={sub.url}
{...props}
onclick={(e) => {
if (nav.title === 'Sheet') {
e.preventDefault();
referenceFromPage.set('sheet');
goto(sub.url);
}
}}
>
2026-03-26 14:57:11 +07:00
{#if sub.icon}
<sub.icon />
{/if}
<span>{sub.title}</span>
</a>
{/snippet}
</Sidebar.MenuButton>
</Sidebar.MenuItem>
{/each}
</Sidebar.Menu>
</Sidebar.GroupContent>
</Sidebar.Group>
{/if}
2026-02-17 14:30:02 +07:00
</Sidebar.Content>
<Sidebar.Footer>
<AppAccountSelect />
</Sidebar.Footer>
</Sidebar.Root>