Yuki / components /Sidebar.tsx
Severian's picture
Upload 43 files
be02369 verified
import React from 'react';
import type { View } from '../types';
import {
BonsaiIcon,
SparklesIcon,
BugIcon,
LeafIcon,
WrenchIcon,
BookUserIcon,
StethoscopeIcon,
PaletteIcon,
ScanIcon,
SnailIcon,
FilterIcon,
RootsIcon,
PotRulerIcon,
SunClockIcon,
BeakerIcon,
ShovelIcon,
UmbrellaIcon,
ScissorsIcon,
SettingsIcon,
ChevronsLeftIcon
} from './icons';
import { AuthContext } from '../context/AuthContext';
interface SidebarProps {
activeView: View;
setActiveView: (view: View) => void;
isCollapsed: boolean;
setIsCollapsed: (isCollapsed: boolean) => void;
}
const CATEGORIZED_NAV_ITEMS = [
{
category: 'Core',
items: [
{ id: 'garden', name: 'My Garden', icon: BookUserIcon },
{ id: 'steward', name: 'New Tree Analysis', icon: SparklesIcon },
]
},
{
category: 'AI Studios',
items: [
{ id: 'designStudio', name: 'AI Design Studio', icon: PaletteIcon },
{ id: 'wiringGuide', name: 'AI Wiring Guide', icon: SnailIcon },
{ id: 'nebariDeveloper', name: 'Nebari Developer', icon: RootsIcon },
// { id: 'virtualTrimmer', name: 'Virtual Trimmer', icon: ScissorsIcon },
]
},
{
category: 'Diagnostics',
items: [
{ id: 'healthCheck', name: 'Health Check-up', icon: StethoscopeIcon },
{ id: 'speciesIdentifier', name: 'Species Identifier', icon: ScanIcon },
{ id: 'soilAnalyzer', name: 'Soil Analyzer', icon: FilterIcon },
{ id: 'weatherShield', name: 'Weather Shield', icon: UmbrellaIcon },
]
},
{
category: 'Utilities',
items: [
{ id: 'sunTracker', name: 'Sun Tracker', icon: SunClockIcon },
{ id: 'potCalculator', name: 'Pot Calculator', icon: PotRulerIcon },
{ id: 'fertilizerMixer', name: 'Fertilizer Mixer', icon: BeakerIcon },
{ id: 'soilVolumeCalculator', name: 'Soil Mix Calculator', icon: ShovelIcon },
{ id: 'tools', name: 'Tool Guide', icon: WrenchIcon },
]
},
{
category: 'Reference',
items: [
{ id: 'pests', name: 'Pest Library', icon: BugIcon },
{ id: 'seasons', name: 'Seasonal Guides', icon: LeafIcon },
]
},
];
const Sidebar: React.FC<SidebarProps> = ({ activeView, setActiveView, isCollapsed, setIsCollapsed }) => {
const { logout } = React.useContext(AuthContext);
return (
<aside className={`flex-shrink-0 bg-white border-r border-stone-200 flex flex-col transition-all duration-300 ${isCollapsed ? 'w-20' : 'w-64'}`}>
<div className={`h-16 flex items-center border-b border-stone-200 transition-all duration-300 ${isCollapsed ? 'justify-center' : 'justify-center px-4'}`}>
<div className="flex items-center gap-2">
<BonsaiIcon className="h-8 w-8 text-green-700 flex-shrink-0" />
{!isCollapsed && <h1 className="text-xl font-bold text-stone-800">Yuki</h1>}
</div>
</div>
<nav className="flex-1 px-2 py-4 space-y-2 overflow-y-auto">
{CATEGORIZED_NAV_ITEMS.map((category) => (
<div key={category.category}>
{!isCollapsed && <h2 className="px-4 text-xs font-bold uppercase text-stone-500 tracking-wider">{category.category}</h2>}
{isCollapsed && category.category === 'Core' && <div className="h-px bg-stone-200 my-2 mx-4"></div>}
<div className="mt-2 space-y-1">
{category.items.map((item) => (
<button
key={item.id}
onClick={() => setActiveView(item.id as View)}
title={item.name}
className={`w-full flex items-center gap-3 px-4 py-2.5 rounded-lg text-sm font-medium transition-colors ${isCollapsed ? 'justify-center' : ''}
${
activeView === item.id
? 'bg-green-100 text-green-800'
: 'text-stone-600 hover:bg-stone-100 hover:text-stone-900'
}
`}
>
<item.icon className="h-5 w-5 flex-shrink-0" />
{!isCollapsed && <span>{item.name}</span>}
</button>
))}
</div>
</div>
))}
</nav>
<div className="flex-shrink-0 p-2 border-t border-stone-200">
<button
key="settings"
onClick={() => setActiveView('settings')}
title="Settings"
className={`w-full flex items-center gap-3 px-4 py-2.5 rounded-lg text-sm font-medium transition-colors ${isCollapsed ? 'justify-center' : ''}
${
activeView === 'settings'
? 'bg-green-100 text-green-800'
: 'text-stone-600 hover:bg-stone-100 hover:text-stone-900'
}
`}
>
<SettingsIcon className="h-5 w-5 flex-shrink-0" />
{!isCollapsed && <span>Settings</span>}
</button>
<div className="p-2 border-t border-stone-200 mt-2">
<button
onClick={() => setIsCollapsed(!isCollapsed)}
className={`w-full flex items-center gap-3 px-4 py-2 text-sm font-medium text-stone-600 hover:bg-stone-100 rounded-lg ${isCollapsed ? 'justify-center' : ''}`}
title={isCollapsed ? 'Expand Sidebar' : 'Collapse Sidebar'}
>
<ChevronsLeftIcon className={`h-5 w-5 flex-shrink-0 transition-transform duration-300 ${isCollapsed ? 'rotate-180' : ''}`} />
{!isCollapsed && <span>Collapse</span>}
</button>
</div>
</div>
</aside>
);
};
export default Sidebar;