feat: enrich dashboard filters and feedback

This commit is contained in:
2026-08-20 22:31:41 +03:30
parent 9ce2e6ec83
commit 458ab69a3c
24 changed files with 1895 additions and 340 deletions

View File

@@ -4,7 +4,6 @@
import ActivityIcon from '@lucide/svelte/icons/activity';
import ArrowUpDownIcon from '@lucide/svelte/icons/arrow-up-down';
import BookOpenIcon from '@lucide/svelte/icons/book-open';
import ChartNoAxesCombinedIcon from '@lucide/svelte/icons/chart-no-axes-combined';
import HandCoinsIcon from '@lucide/svelte/icons/hand-coins';
import LogOutIcon from '@lucide/svelte/icons/log-out';
import MenuIcon from '@lucide/svelte/icons/menu';
@@ -15,11 +14,11 @@
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';
import { formatValue } from '$lib/format';
let { children } = $props();
const dashboard = useDashboardContext();
@@ -37,7 +36,7 @@
const url = new URL(page.url);
url.pathname = path;
for (const key of [...url.searchParams.keys()]) {
if (!['range', 'terminal'].includes(key)) url.searchParams.delete(key);
if (!['range', 'period', 'terminal'].includes(key)) url.searchParams.delete(key);
}
return `${url.pathname}${url.search}`;
}
@@ -49,9 +48,9 @@
);
}
function terminalChanged(event: Event) {
function terminalChanged(value: string) {
const next = setDashboardQuery(page.url, {
terminal: (event.currentTarget as HTMLSelectElement).value,
terminal: value,
});
void goto(`${next.pathname}${next.search}`, { keepFocus: true, noScroll: true });
}
@@ -83,19 +82,12 @@
<label class="text-muted-foreground mb-1 block text-[11px]" for="sidebar-terminal-select"
>درگاه فعال گزارش</label
>
<select
<TerminalSelect
id="sidebar-terminal-select"
value={page.url.searchParams.get('terminal') ?? 'all'}
onchange={terminalChanged}
class="border-input bg-background focus:ring-ring/30 h-9 w-full rounded-md border px-2 text-xs outline-none focus:ring-2"
>
<option value="all">همه درگاه‌ها</option>
{#each dashboard.terminals as terminal}
<option value={terminal.terminalId}
>{terminal.displayName} · {formatValue(terminal.sessionCount)}</option
>
{/each}
</select>
terminals={dashboard.terminals}
onValueChange={terminalChanged}
/>
</div>
</Sidebar.Header>
<Sidebar.Content class="px-2 py-3">
@@ -107,11 +99,18 @@
<Sidebar.MenuButton
isActive={isActive(item.href)}
tooltipContent={item.label}
class="data-[active=true]:text-primary h-10 justify-start px-3 text-[13px]"
onclick={() => goto(navUrl(item.href))}
class="data-[active=true]:text-primary h-11 justify-start px-3 text-[13px]"
>
<item.icon class="size-[19px]" strokeWidth={1.8} />
<span>{item.label}</span>
{#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>{item.label}</span>
</a>
{/snippet}
</Sidebar.MenuButton>
</Sidebar.MenuItem>
{/each}
@@ -126,11 +125,18 @@
<Sidebar.MenuButton
isActive={isActive('/methodology')}
tooltipContent="روش محاسبه"
class="data-[active=true]:text-primary h-10 justify-start px-3 text-[13px]"
onclick={() => goto(navUrl('/methodology'))}
class="data-[active=true]:text-primary h-11 justify-start px-3 text-[13px]"
>
<BookOpenIcon class="size-[19px]" strokeWidth={1.8} />
<span>روش محاسبه</span>
{#snippet child({ props })}
<a
{...props}
href={navUrl('/methodology')}
aria-current={isActive('/methodology') ? 'page' : undefined}
>
<BookOpenIcon class="size-[19px]" strokeWidth={1.8} />
<span>روش محاسبه</span>
</a>
{/snippet}
</Sidebar.MenuButton>
</Sidebar.MenuItem>
</Sidebar.Menu>
@@ -140,7 +146,7 @@
<Sidebar.Footer class="border-sidebar-border border-t p-2">
<Sidebar.Menu>
<Sidebar.MenuItem>
<Sidebar.MenuButton tooltipContent="خروج" class="h-10 px-3" onclick={logout}>
<Sidebar.MenuButton tooltipContent="خروج" class="h-11 px-3" onclick={logout}>
<LogOutIcon class="size-[18px]" />
<span>خروج از حساب</span>
</Sidebar.MenuButton>
@@ -155,27 +161,25 @@
>
<div class="flex w-full items-center justify-between">
<div class="flex items-center gap-2">
<Sidebar.Trigger class="size-9" aria-label="باز و بسته کردن منو"><MenuIcon /></Sidebar.Trigger>
<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>
<select
<TerminalSelect
id="mobile-terminal-select"
value={page.url.searchParams.get('terminal') ?? 'all'}
onchange={terminalChanged}
class="border-input bg-background h-8 max-w-36 rounded-md border px-2 text-xs md:hidden"
>
<option value="all">همه درگاه‌ها</option>
{#each dashboard.terminals as terminal}
<option value={terminal.terminalId}>{terminal.displayName}</option>
{/each}
</select>
<Button variant="ghost" size="icon" onclick={toggleTheme} aria-label="تغییر پوسته">
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 h-6 w-px"></span>
<div class="grid size-8 place-items-center rounded-full bg-zinc-900 text-xs font-bold text-white">
<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>