peer-at-code-web/src/lib/components/app-sidebar.svelte
2025-01-29 12:29:45 +01:00

207 lines
5.7 KiB
Svelte

<script lang="ts" module>
import ChartNoAxesColumn from 'lucide-svelte/icons/chart-no-axes-column';
import ChevronsUpDown from 'lucide-svelte/icons/chevrons-up-down';
import CircleHelp from 'lucide-svelte/icons/circle-help';
import Code from 'lucide-svelte/icons/code';
import GitBranch from 'lucide-svelte/icons/git-branch';
import LayoutDashboard from 'lucide-svelte/icons/layout-dashboard';
import LogOut from 'lucide-svelte/icons/log-out';
import Radio from 'lucide-svelte/icons/radio';
import ScrollText from 'lucide-svelte/icons/scroll-text';
import Settings from 'lucide-svelte/icons/settings';
let navigation = $state([
{
title: null,
isAdmin: false,
items: [
{
title: 'Dashboard',
url: '/',
icon: LayoutDashboard
},
{
title: 'Classement',
url: '/leaderboard',
icon: ChartNoAxesColumn
},
{
title: 'Chapitres',
url: '/chapters',
icon: Code
}
]
},
{
title: 'Documentation',
isAdmin: false,
items: [
{
title: 'Git',
url: 'https://git.peerat.dev',
icon: GitBranch
},
{
title: 'Discord',
url: 'https://discord.gg/72vuHcwUkE',
icon: CircleHelp
}
]
},
{
title: 'Administration',
isAdmin: true,
items: [
{
title: 'Logs',
url: '/admin/logs',
icon: ScrollText
},
{
title: 'Puzzles',
url: '/admin/puzzles',
icon: Code
}
,
{
title: 'Broadcast',
url: '/admin/broadcast',
icon: Radio
}
]
}
]);
</script>
<script lang="ts">
import { afterNavigate, goto } from '$app/navigation';
import { page } from '$app/state';
import type { ComponentProps } from 'svelte';
import * as Avatar from '$lib/components/ui/avatar';
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
import * as Sidebar from '$lib/components/ui/sidebar';
import { useSidebar } from '$lib/components/ui/sidebar';
let {
ref = $bindable(null),
collapsible = 'icon',
...restProps
}: ComponentProps<typeof Sidebar.Root> = $props();
const sidebar = useSidebar();
navigation = navigation.filter(
(nav) => !nav.isAdmin || page.data.user?.email.endsWith('@peerat.dev')
);
afterNavigate(() => {
if (sidebar.isMobile) {
sidebar.setOpenMobile(false);
}
});
</script>
<Sidebar.Root bind:ref {collapsible} {...restProps}>
<Sidebar.Header>
<Sidebar.Menu>
<a href="/">
<Sidebar.MenuItem>
<Sidebar.MenuButton
size="lg"
class="rounded data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<div class="flex aspect-square size-8 items-center justify-center rounded">
<img src="/favicon.ico" alt="Peer-at Code" class="size-6" />
</div>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold"> Peer-at Code </span>
</div>
</Sidebar.MenuButton>
</Sidebar.MenuItem>
</a>
</Sidebar.Menu>
</Sidebar.Header>
<Sidebar.Content>
{#each navigation as navigation}
<Sidebar.Group>
{#if navigation.title}
<Sidebar.GroupLabel>{navigation.title}</Sidebar.GroupLabel>
{/if}
<Sidebar.Menu>
{#each navigation.items as item (item.title)}
<Sidebar.MenuItem>
<Sidebar.MenuButton class="rounded" isActive={page.url.pathname === item.url}>
{#snippet child({ props })}
<a href={item.url} {...props}>
<item.icon />
<span>{item.title}</span>
</a>
{/snippet}
</Sidebar.MenuButton>
</Sidebar.MenuItem>
{/each}
</Sidebar.Menu>
</Sidebar.Group>
{/each}
</Sidebar.Content>
<Sidebar.Footer>
<Sidebar.Menu>
<Sidebar.MenuItem>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Sidebar.MenuButton
size="lg"
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
{...props}
>
<Avatar.Root class="h-8 w-8 rounded">
<Avatar.Image src={page.data.user?.avatar} alt={page.data.user?.pseudo} />
<Avatar.Fallback class="rounded">CN</Avatar.Fallback>
</Avatar.Root>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">{page.data.user?.pseudo}</span>
<span class="truncate text-xs">{page.data.user?.email}</span>
</div>
<ChevronsUpDown class="ml-auto size-4" />
</Sidebar.MenuButton>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content
class="w-[--bits-dropdown-menu-anchor-width] min-w-56 rounded"
side={sidebar.isMobile ? 'bottom' : 'right'}
align="end"
sideOffset={4}
>
<DropdownMenu.Label class="p-0 font-normal">
<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar.Root class="h-8 w-8 rounded">
<Avatar.Image src={page.data.user?.avatar} alt={page.data.user?.pseudo} />
<Avatar.Fallback class="rounded">CN</Avatar.Fallback>
</Avatar.Root>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">{page.data.user?.pseudo}</span>
<span class="truncate text-xs">{page.data.user?.email}</span>
</div>
</div>
</DropdownMenu.Label>
<DropdownMenu.Separator />
<DropdownMenu.Group>
<DropdownMenu.Item disabled>
<Settings />
Paramètres
</DropdownMenu.Item>
</DropdownMenu.Group>
<DropdownMenu.Separator />
<DropdownMenu.Item onclick={() => goto('/logout')}>
<LogOut />
Se déconnecter
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
</Sidebar.MenuItem>
</Sidebar.Menu>
</Sidebar.Footer>
<Sidebar.Rail />
</Sidebar.Root>