192 lines
7.7 KiB
Svelte
192 lines
7.7 KiB
Svelte
<script lang="ts">
|
|
import { goto } from '$app/navigation';
|
|
import { page } from '$app/state';
|
|
import ActivityIcon from '@lucide/svelte/icons/activity';
|
|
import ArrowUpDownIcon from '@lucide/svelte/icons/arrow-up-down';
|
|
import HandCoinsIcon from '@lucide/svelte/icons/hand-coins';
|
|
import LogOutIcon from '@lucide/svelte/icons/log-out';
|
|
import MenuIcon from '@lucide/svelte/icons/menu';
|
|
import MoonIcon from '@lucide/svelte/icons/moon';
|
|
import PanelsTopLeftIcon from '@lucide/svelte/icons/panels-top-left';
|
|
import SunIcon from '@lucide/svelte/icons/sun';
|
|
import UsersRoundIcon from '@lucide/svelte/icons/users-round';
|
|
import WalletCardsIcon from '@lucide/svelte/icons/wallet-cards';
|
|
|
|
import BrandLogo from '$lib/components/BrandLogo.svelte';
|
|
import TerminalSelect from '$lib/components/TerminalSelect.svelte';
|
|
import { Button } from '$lib/components/ui/button';
|
|
import * as Sidebar from '$lib/components/ui/sidebar';
|
|
import { useDashboardContext } from '$lib/dashboard-context.svelte';
|
|
import { setDashboardQuery } from '$lib/dashboard-filters';
|
|
|
|
let { children } = $props();
|
|
const dashboard = useDashboardContext();
|
|
let dark = $state(document.documentElement.dataset.theme === 'dark');
|
|
|
|
const items = [
|
|
{ href: '/dashboard', label: 'پیشخوان', icon: PanelsTopLeftIcon },
|
|
{ href: '/transactions', label: 'تراکنشها', icon: ArrowUpDownIcon },
|
|
{ href: '/payments', label: 'عملکرد پرداخت', icon: ActivityIcon },
|
|
{ href: '/fees', label: 'کارمزدها', icon: HandCoinsIcon },
|
|
{ href: '/peers', label: 'همگروهها', icon: UsersRoundIcon },
|
|
];
|
|
|
|
function navUrl(path: string) {
|
|
const url = new URL(page.url);
|
|
url.pathname = path;
|
|
for (const key of [...url.searchParams.keys()]) {
|
|
if (!['range', 'period', 'terminal'].includes(key)) url.searchParams.delete(key);
|
|
}
|
|
return `${url.pathname}${url.search}`;
|
|
}
|
|
|
|
function isActive(path: string) {
|
|
return (
|
|
page.url.pathname === path ||
|
|
(path === '/transactions' && page.url.pathname.startsWith('/transactions/'))
|
|
);
|
|
}
|
|
|
|
function terminalChanged(value: string) {
|
|
const next = setDashboardQuery(page.url, {
|
|
terminal: value,
|
|
});
|
|
void goto(`${next.pathname}${next.search}`, { keepFocus: true, noScroll: true });
|
|
}
|
|
|
|
function toggleTheme() {
|
|
dark = !dark;
|
|
document.documentElement.dataset.theme = dark ? 'dark' : 'light';
|
|
document.documentElement.classList.toggle('dark', dark);
|
|
localStorage.setItem('zpa-theme', dark ? 'dark' : 'light');
|
|
}
|
|
|
|
async function logout() {
|
|
await dashboard.logout();
|
|
await goto('/');
|
|
}
|
|
</script>
|
|
|
|
<Sidebar.Provider class="dashboard-shell" style="--sidebar-width: 16rem; --sidebar-width-icon: 4.5rem;">
|
|
<Sidebar.Root side="right" collapsible="icon" class="top-14 h-[calc(100svh-3.5rem)] border-l">
|
|
<Sidebar.Header class="border-sidebar-border gap-2 border-b p-2.5">
|
|
<div
|
|
class="terminal-card group-data-[collapsible=icon]:size-11 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0"
|
|
>
|
|
<WalletCardsIcon class="size-5 shrink-0 group-data-[collapsible=icon]:size-[22px]" />
|
|
<div class="min-w-0 group-data-[collapsible=icon]:hidden">
|
|
<strong class="block truncate text-sm">{dashboard.session?.merchant.displayName}</strong>
|
|
<span class="block truncate text-[11px] text-white/75">حساب تحلیل پذیرنده</span>
|
|
</div>
|
|
</div>
|
|
<div class="group-data-[collapsible=icon]:hidden">
|
|
<label class="text-muted-foreground mb-1 block text-[11px]" for="sidebar-terminal-select"
|
|
>درگاه فعال گزارش</label
|
|
>
|
|
<TerminalSelect
|
|
id="sidebar-terminal-select"
|
|
value={page.url.searchParams.get('terminal') ?? 'all'}
|
|
terminals={dashboard.terminals}
|
|
onValueChange={terminalChanged}
|
|
/>
|
|
</div>
|
|
</Sidebar.Header>
|
|
<Sidebar.Content class="px-2 py-3">
|
|
<Sidebar.Group>
|
|
<Sidebar.GroupContent>
|
|
<Sidebar.Menu class="gap-1">
|
|
{#each items as item}
|
|
<Sidebar.MenuItem>
|
|
<Sidebar.MenuButton
|
|
isActive={isActive(item.href)}
|
|
tooltipContent={item.label}
|
|
class="data-[active=true]:text-primary h-11 justify-start px-3 text-[13px] group-data-[collapsible=icon]:size-11! group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0! group-data-[collapsible=icon]:[&_svg]:size-[21px]! group-data-[collapsible=icon]:[&>span]:hidden"
|
|
>
|
|
{#snippet child({ props })}
|
|
<a
|
|
{...props}
|
|
href={navUrl(item.href)}
|
|
aria-current={isActive(item.href) ? 'page' : undefined}
|
|
>
|
|
<item.icon class="size-[19px]" strokeWidth={1.8} />
|
|
<span class="group-data-[collapsible=icon]:hidden">{item.label}</span>
|
|
</a>
|
|
{/snippet}
|
|
</Sidebar.MenuButton>
|
|
</Sidebar.MenuItem>
|
|
{/each}
|
|
</Sidebar.Menu>
|
|
</Sidebar.GroupContent>
|
|
</Sidebar.Group>
|
|
</Sidebar.Content>
|
|
<Sidebar.Footer class="border-sidebar-border border-t p-2">
|
|
<Sidebar.Menu>
|
|
<Sidebar.MenuItem>
|
|
<Sidebar.MenuButton
|
|
tooltipContent="خروج"
|
|
class="h-11 px-3 group-data-[collapsible=icon]:size-11! group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0! group-data-[collapsible=icon]:[&_svg]:size-5! group-data-[collapsible=icon]:[&>span]:hidden"
|
|
onclick={logout}
|
|
>
|
|
<LogOutIcon class="size-[18px]" />
|
|
<span class="group-data-[collapsible=icon]:hidden">خروج از حساب</span>
|
|
</Sidebar.MenuButton>
|
|
</Sidebar.MenuItem>
|
|
</Sidebar.Menu>
|
|
</Sidebar.Footer>
|
|
<Sidebar.Rail />
|
|
</Sidebar.Root>
|
|
|
|
<div
|
|
class="bg-card fixed inset-x-0 top-0 z-30 flex h-14 items-center border-b px-4 shadow-[0_1px_2px_rgba(0,0,0,.03)]"
|
|
>
|
|
<div class="flex w-full items-center justify-between">
|
|
<div class="flex items-center gap-2">
|
|
<Sidebar.Trigger class="size-11" aria-label="باز و بسته کردن منو"><MenuIcon /></Sidebar.Trigger>
|
|
<BrandLogo />
|
|
</div>
|
|
<div class="flex items-center gap-1">
|
|
<label class="sr-only" for="mobile-terminal-select">درگاه فعال گزارش</label>
|
|
<TerminalSelect
|
|
id="mobile-terminal-select"
|
|
value={page.url.searchParams.get('terminal') ?? 'all'}
|
|
terminals={dashboard.terminals}
|
|
onValueChange={terminalChanged}
|
|
mobile
|
|
/>
|
|
<Button variant="ghost" size="icon" class="size-11" onclick={toggleTheme} aria-label="تغییر پوسته">
|
|
{#if dark}<SunIcon />{:else}<MoonIcon />{/if}
|
|
</Button>
|
|
<span class="bg-border mx-1 hidden h-6 w-px sm:block"></span>
|
|
<div
|
|
class="hidden size-8 place-items-center rounded-full bg-zinc-900 text-xs font-bold text-white sm:grid"
|
|
>
|
|
{dashboard.session?.user.username.slice(0, 1).toUpperCase()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<Sidebar.Inset class="bg-background mt-14 min-w-0">
|
|
<main class="mx-auto w-full max-w-[1220px] px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
|
{@render children()}
|
|
</main>
|
|
</Sidebar.Inset>
|
|
</Sidebar.Provider>
|
|
|
|
<style>
|
|
:global(.dashboard-shell) {
|
|
direction: rtl;
|
|
}
|
|
.terminal-card {
|
|
display: flex;
|
|
min-height: 52px;
|
|
align-items: center;
|
|
gap: 10px;
|
|
overflow: hidden;
|
|
border-radius: 7px;
|
|
background: var(--primary);
|
|
padding: 10px;
|
|
color: var(--primary-foreground);
|
|
}
|
|
</style>
|