feat: port tauri header top-menu to admin topbar

Rebuild admin Topbar to match clinic-pro-tauri header:
- Icon order: theme -> settings -> bell (was theme -> bell -> settings)
- Settings gear now navigates to the role-based settings page
  instead of opening the personalization panel
- Search placeholder changed to «جستجو», dropped the ⌘K hint
- Add ProfileMenu: avatar + user name + context/role subtitle + caret,
  with a dropdown (profile / payments / settings / personalization / logout).
  Links are role-aware; personalization keeps the existing settings panel.

Frontend-only, no API changes. Adds ProfileMenu.test + Topbar.test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-15 17:44:47 +03:30
co-authored by Claude Opus 4.8
parent 952fcf3634
commit 1aa6794786
5 changed files with 368 additions and 19 deletions
+15 -19
View File
@@ -1,12 +1,17 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import {
BellIcon, SunIcon, MoonIcon, Bars3Icon, Cog6ToothIcon,
MagnifyingGlassIcon, XMarkIcon, CheckIcon,
} from '@heroicons/react/24/outline';
import { useUiStore, HUES, type BrandHue } from '../../stores/uiStore';
import { useAuthStore } from '../../stores/authStore';
import Portal from '../ui/Portal';
import ProfileMenu, { settingsPath } from './ProfileMenu';
export default function Topbar({ onMobileMenuOpen }: { onMobileMenuOpen?: () => void }) {
const navigate = useNavigate();
const primaryRole = useAuthStore((s) => s.primaryRole);
const toggleSidebar = useUiStore((s) => s.toggleSidebar);
const darkMode = useUiStore((s) => s.darkMode);
const toggleDarkMode = useUiStore((s) => s.toggleDarkMode);
@@ -30,15 +35,15 @@ export default function Topbar({ onMobileMenuOpen }: { onMobileMenuOpen?: () =>
<Bars3Icon style={{ width: 20, height: 20 }} />
</button>
{/* Search bar */}
{/* Search bar — placeholder «جستجو» مطابق clinic-pro-tauri */}
<div className="topbar-search">
<MagnifyingGlassIcon style={{ width: 17, height: 17, flexShrink: 0 }} />
<input placeholder="جستجوی بیمار، پزشک، نوبت..." readOnly />
<kbd>K</kbd>
<input placeholder="جستجو" readOnly />
</div>
<div style={{ flex: 1 }} />
{/* ترتیب آیکون‌ها مطابق header در clinic-pro-tauri: تم → تنظیمات → اعلان */}
{/* Dark mode */}
<button className="icon-btn" onClick={toggleDarkMode} title={darkMode ? 'حالت روشن' : 'حالت تاریک'}>
{darkMode
@@ -46,28 +51,19 @@ export default function Topbar({ onMobileMenuOpen }: { onMobileMenuOpen?: () =>
: <MoonIcon style={{ width: 18, height: 18 }} />}
</button>
{/* Settings — به صفحه تنظیمات می‌رود (مثل tauri) */}
<button className="icon-btn" onClick={() => navigate(settingsPath(primaryRole))} title="تنظیمات">
<Cog6ToothIcon style={{ width: 18, height: 18 }} />
</button>
{/* Bell */}
<button className="icon-btn" title="اعلان‌ها" style={{ position: 'relative' }}>
<BellIcon style={{ width: 18, height: 18 }} />
<span className="dot" />
</button>
{/* Settings */}
<button className="icon-btn" onClick={toggleSettingsPanel} title="شخصی‌سازی">
<Cog6ToothIcon style={{ width: 18, height: 18 }} />
</button>
{/* Avatar */}
<div
className="avatar sm"
style={{
background: 'linear-gradient(145deg, oklch(0.62 0.15 256), oklch(0.48 0.16 256))',
cursor: 'pointer',
flexShrink: 0,
}}
>
A
</div>
{/* Profile menu — آواتار + نام + تخصص + منوی کشویی مطابق tauri */}
<ProfileMenu />
</header>
{/* Settings Panel */}