- Header: Change 'Data Station' to 'RTU', use TCK logo, fix sizing - Sidebar: MENU text now clickable to collapse/expand - RainfallView: Fit 1024x600 without scrolling - GraphView: Fit 1024x600 screen
194 lines
5.8 KiB
TypeScript
194 lines
5.8 KiB
TypeScript
import { useState } from "react";
|
|
import { Link, useLocation } from "react-router";
|
|
import {
|
|
Home,
|
|
BarChart3,
|
|
Settings,
|
|
Wrench,
|
|
Gauge,
|
|
HardDrive,
|
|
LogIn,
|
|
ChevronRight,
|
|
ChevronDown
|
|
} from "lucide-react";
|
|
|
|
interface SidebarProps {
|
|
collapsed: boolean;
|
|
onToggle: () => void;
|
|
}
|
|
|
|
interface MenuItem {
|
|
id: string;
|
|
label: string;
|
|
icon: React.ReactNode;
|
|
path?: string;
|
|
children?: SubMenuItem[];
|
|
}
|
|
|
|
interface SubMenuItem {
|
|
id: string;
|
|
label: string;
|
|
path: string;
|
|
}
|
|
|
|
const menuItems: MenuItem[] = [
|
|
{
|
|
id: "home",
|
|
label: "HOME",
|
|
icon: <Home className="w-5 h-5" />,
|
|
path: "/rainfall",
|
|
},
|
|
{
|
|
id: "graph",
|
|
label: "GRAPH",
|
|
icon: <BarChart3 className="w-5 h-5" />,
|
|
path: "/graph",
|
|
},
|
|
{
|
|
id: "utility",
|
|
label: "UTILITY",
|
|
icon: <Wrench className="w-5 h-5" />,
|
|
children: [
|
|
{ id: "station-info", label: "Station Info", path: "/utility/station-info" },
|
|
{ id: "datetime", label: "Date / Time setting", path: "/utility/datetime" },
|
|
{ id: "mobile", label: "Mobile Setting", path: "/utility/mobile" },
|
|
{ id: "adc", label: "ADC Setting", path: "/utility/adc" },
|
|
{ id: "rainfall", label: "Rainfall Setting", path: "/utility/rainfall" },
|
|
{ id: "evap", label: "EVAP Setting", path: "/utility/evap" },
|
|
{ id: "gprs", label: "GPRS Setting", path: "/utility/gprs" },
|
|
{ id: "level", label: "Level Setting", path: "/utility/level" },
|
|
{ id: "siren", label: "SIREN Setting", path: "/utility/siren" },
|
|
{ id: "network", label: "Network Setup", path: "/utility/network" },
|
|
],
|
|
},
|
|
{
|
|
id: "calibration",
|
|
label: "CALIBRATION",
|
|
icon: <Gauge className="w-5 h-5" />,
|
|
path: "/calibration",
|
|
},
|
|
{
|
|
id: "flash-memory",
|
|
label: "FLASH MEMORY",
|
|
icon: <HardDrive className="w-5 h-5" />,
|
|
path: "/flash-memory",
|
|
},
|
|
{
|
|
id: "setting",
|
|
label: "SETTING",
|
|
icon: <Settings className="w-5 h-5" />,
|
|
path: "/setting",
|
|
},
|
|
{
|
|
id: "login",
|
|
label: "LOGIN",
|
|
icon: <LogIn className="w-5 h-5" />,
|
|
path: "/login",
|
|
},
|
|
];
|
|
|
|
export function Sidebar({ collapsed, onToggle }: SidebarProps) {
|
|
const location = useLocation();
|
|
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set(["utility"]));
|
|
|
|
const toggleExpanded = (id: string) => {
|
|
const newExpanded = new Set(expandedItems);
|
|
if (newExpanded.has(id)) {
|
|
newExpanded.delete(id);
|
|
} else {
|
|
newExpanded.add(id);
|
|
}
|
|
setExpandedItems(newExpanded);
|
|
};
|
|
|
|
const isActive = (path?: string) => {
|
|
if (!path) return false;
|
|
return location.pathname === path;
|
|
};
|
|
|
|
const isParentActive = (children?: SubMenuItem[]) => {
|
|
if (!children) return false;
|
|
return children.some(child => location.pathname === child.path);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={`bg-gray-950 border-r border-gray-700 transition-all duration-300 flex flex-col ${
|
|
collapsed ? "w-16" : "w-64"
|
|
}`}
|
|
>
|
|
<div className="p-2 border-b border-gray-700 flex items-center justify-between">
|
|
<button
|
|
onClick={onToggle}
|
|
className="flex items-center gap-2 text-white font-bold text-sm hover:bg-gray-800 px-2 py-1 rounded transition-colors"
|
|
>
|
|
<ChevronRight className={`w-4 h-4 transition-transform ${collapsed ? "" : "rotate-180"}`} />
|
|
{!collapsed && <span>MENU</span>}
|
|
</button>
|
|
</div>
|
|
|
|
<nav className="flex-1 overflow-y-auto py-2">
|
|
{menuItems.map((item) => (
|
|
<div key={item.id}>
|
|
{item.children ? (
|
|
<>
|
|
<button
|
|
onClick={() => toggleExpanded(item.id)}
|
|
className={`w-full flex items-center gap-3 px-3 py-2.5 text-sm transition-colors ${
|
|
isParentActive(item.children)
|
|
? "bg-blue-600 text-white"
|
|
: "text-gray-300 hover:bg-gray-800 hover:text-white"
|
|
}`}
|
|
title={collapsed ? item.label : undefined}
|
|
>
|
|
<span className="flex-shrink-0">{item.icon}</span>
|
|
{!collapsed && (
|
|
<>
|
|
<span className="flex-1 text-left">{item.label}</span>
|
|
<ChevronDown
|
|
className={`w-4 h-4 transition-transform ${
|
|
expandedItems.has(item.id) ? "" : "-rotate-90"
|
|
}`}
|
|
/>
|
|
</>
|
|
)}
|
|
</button>
|
|
{!collapsed && expandedItems.has(item.id) && (
|
|
<div className="bg-gray-900">
|
|
{item.children.map((child) => (
|
|
<Link
|
|
key={child.id}
|
|
to={child.path}
|
|
className={`block px-3 py-2 pl-11 text-xs transition-colors ${
|
|
isActive(child.path)
|
|
? "bg-blue-600 text-white"
|
|
: "text-gray-400 hover:bg-gray-800 hover:text-white"
|
|
}`}
|
|
>
|
|
{child.label}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</>
|
|
) : (
|
|
<Link
|
|
to={item.path!}
|
|
className={`flex items-center gap-3 px-3 py-2.5 text-sm transition-colors ${
|
|
isActive(item.path)
|
|
? "bg-blue-600 text-white"
|
|
: "text-gray-300 hover:bg-gray-800 hover:text-white"
|
|
}`}
|
|
title={collapsed ? item.label : undefined}
|
|
>
|
|
<span className="flex-shrink-0">{item.icon}</span>
|
|
{!collapsed && <span>{item.label}</span>}
|
|
</Link>
|
|
)}
|
|
</div>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
);
|
|
}
|