Files
dyolink/frontend/src/components/ui/shared/Sidebar.tsx

92 lines
3.2 KiB
TypeScript

'use client';
import { memo, useMemo } from 'react';
import { useTranslations } from 'next-intl';
import { Link, usePathname } from '@/i18n/navigation';
import {
LayoutDashboard,
Users,
Calendar,
UserCog,
FlaskConical,
FileText,
CreditCard,
} from 'lucide-react';
import { useAuth } from '@/lib/hooks/useAuth';
import { canAccessAppointmentsSection, canViewTab } from '@/components/shared/permissions';
import {
counterpartOrganizationType,
organizationTypeIcon,
} from '@/components/shared/organizationTypeIcon';
function Sidebar() {
const t = useTranslations('nav');
const tCommon = useTranslations('common');
const pathname = usePathname();
const { currentOrganization } = useAuth();
const menu = useMemo(
() => [
{ name: t('dashboard'), path: '/today', icon: LayoutDashboard, read: 'TAB_TODAY_READ' as const },
{ name: t('staff'), path: '/staff', icon: UserCog, read: 'TAB_STAFF_READ' as const },
{
name: currentOrganization?.type === 'LAB' ? t('clinics') : t('labs'),
path: '/organizations',
icon: organizationTypeIcon(counterpartOrganizationType(currentOrganization?.type)),
read: 'TAB_ORGANIZATIONS_READ' as const,
},
{ name: t('patients'), path: '/patients', icon: Users, read: 'TAB_PATIENTS_READ' as const },
{ name: t('appointment'), path: '/appointments', icon: Calendar, read: 'TAB_APPOINTMENTS_READ' as const },
{ name: t('treatment'), path: '/treatment', icon: FlaskConical, read: 'TAB_TREATMENT_READ' as const },
{ name: t('billing'), path: '/billing', icon: CreditCard, read: 'TAB_BILLING_READ' as const },
{ name: t('reports'), path: '/reports', icon: FileText, read: 'TAB_REPORTS_READ' as const },
],
[currentOrganization?.type, t],
);
const visibleMenu = useMemo(
() =>
menu.filter((item) => {
if (item.path === '/appointments') {
return canAccessAppointmentsSection(currentOrganization);
}
return canViewTab(currentOrganization, item.read);
}),
[currentOrganization, menu],
);
return (
<aside className="w-64 bg-background-secondary/90 border-r border-border text-text-primary flex flex-col">
<div className="h-[71px] px-4 flex items-center">
<h1 className="text-lg font-medium tracking-tight">{tCommon('appName')}</h1>
</div>
<div className="mx-4 border-b border-border/70" />
<nav className="flex flex-col gap-2 p-4">
{visibleMenu.map((item) => {
const Icon = item.icon;
const isActive = pathname === item.path;
return (
<Link
key={item.path}
href={item.path}
prefetch
className={`flex items-center gap-3 px-3 py-2.5 rounded-[var(--radius-sm)] border transition-colors ${
isActive
? 'bg-primary-soft border-primary/60 text-text-primary'
: 'border-border/50 text-text-secondary hover:bg-background-card/70 hover:text-text-primary hover:border-border'
}`}
>
<Icon className="w-[18px] h-[18px] icon-flat" />
<span className="text-sm">{item.name}</span>
</Link>
);
})}
</nav>
</aside>
);
}
export default memo(Sidebar);