mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
266 lines
11 KiB
TypeScript
266 lines
11 KiB
TypeScript
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { usePathname } from 'next/navigation';
|
|
import { useState, useEffect } from 'react';
|
|
import {
|
|
LayoutDashboard,
|
|
Ticket,
|
|
Users,
|
|
Route,
|
|
DollarSign,
|
|
Bell,
|
|
BarChart3,
|
|
Settings,
|
|
LogOut,
|
|
LogIn,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
Train,
|
|
MapPin,
|
|
CreditCard,
|
|
Shield,
|
|
UserCheck,
|
|
Wallet,
|
|
Gift,
|
|
MessageSquare,
|
|
AlertTriangle,
|
|
FileText,
|
|
Briefcase,
|
|
Calendar,
|
|
Utensils,
|
|
Package,
|
|
Moon,
|
|
Sun,
|
|
Armchair,
|
|
Ban,
|
|
Grid3x3,
|
|
Banknote,
|
|
Activity,
|
|
Smartphone,
|
|
Layers,
|
|
} from 'lucide-react';
|
|
import { useAuthStore } from '@/lib/auth-store';
|
|
import { cn } from '@/lib/utils';
|
|
import { useTheme } from '@/lib/theme-store';
|
|
import { PERMS } from '@/lib/permissions';
|
|
|
|
interface NavItem {
|
|
name: string;
|
|
href: string;
|
|
icon: React.ComponentType<{ className?: string }>;
|
|
permission?: string;
|
|
}
|
|
|
|
const navigationSections: { title: string; items: NavItem[] }[] = [
|
|
{
|
|
title: 'Overview',
|
|
items: [
|
|
{ name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard, permission: PERMS.dashboard },
|
|
]
|
|
},
|
|
{
|
|
title: 'Operations',
|
|
items: [
|
|
{ name: 'Bookings', href: '/bookings', icon: Ticket, permission: PERMS.bookings.view },
|
|
{ name: 'Passengers', href: '/passengers', icon: Users, permission: PERMS.passengers.view },
|
|
{ name: 'Tickets', href: '/tickets', icon: FileText, permission: PERMS.tickets.view },
|
|
{ name: 'Boarding', href: '/boarding', icon: LogIn, permission: PERMS.tickets.manage },
|
|
{ name: 'Luggage', href: '/excess-baggage', icon: Banknote, permission: PERMS.bookings.view },
|
|
{ name: 'Discrepancy', href: '/discrepancy', icon: Layers, permission: PERMS.seats.manage },
|
|
]
|
|
},
|
|
{
|
|
title: 'Tourism',
|
|
items: [
|
|
{ name: 'Packages', href: '/packages', icon: Package, permission: PERMS.packages.view },
|
|
// { name: 'Bookings', href: '/package-bookings', icon: Ticket, permission: PERMS.packages.view },
|
|
{ name: 'Inquiries', href: '/package-inquiries', icon: MessageSquare, permission: PERMS.inquiries.view },
|
|
]
|
|
},
|
|
{
|
|
title: 'Master Data',
|
|
items: [
|
|
{ name: 'Stations', href: '/stations', icon: MapPin, permission: PERMS.stations.view },
|
|
{ name: 'Trains', href: '/trains', icon: Train, permission: PERMS.trains.view },
|
|
{ name: 'Coaches', href: '/coaches', icon: Grid3x3, permission: PERMS.coaches.view },
|
|
{ name: 'Seats', href: '/seats', icon: Armchair, permission: PERMS.seats.view },
|
|
{ name: 'Classes', href: '/classes', icon: Settings, permission: PERMS.classes.view },
|
|
{ name: 'Routes', href: '/routes', icon: Route, permission: PERMS.routes.view },
|
|
{ name: 'Schedules', href: '/schedules', icon: Calendar, permission: PERMS.schedules.view },
|
|
]
|
|
},
|
|
{
|
|
title: 'Financial',
|
|
items: [
|
|
// { name: 'Pricing & Fares', href: '/pricing', icon: DollarSign, permission: PERMS.admin },
|
|
{ name: 'Tariff Rates', href: '/tariff-rates', icon: Banknote, permission: PERMS.tariffRates.view },
|
|
// { name: 'Fare Rules', href: '/fare-management', icon: Settings, permission: PERMS.admin },
|
|
{ name: 'Payments', href: '/payments', icon: CreditCard, permission: PERMS.payments.view },
|
|
{ name: 'Currencies', href: '/currencies', icon: Banknote, permission: PERMS.currencies.view },
|
|
// { name: 'Promo Codes', href: '/promos', icon: Gift, permission: PERMS.admin },
|
|
{ name: 'Payment Methods', href: '/payment-methods', icon: CreditCard, permission: PERMS.paymentMethods.view },
|
|
// { name: 'Wallet Accounts', href: '/wallet-accounts', icon: Wallet, permission: PERMS.payments.view },
|
|
]
|
|
},
|
|
{
|
|
title: 'Customer Services',
|
|
items: [
|
|
// { name: 'Loyalty Program', href: '/loyalty', icon: Gift, permission: PERMS.passengers.view },
|
|
{ name: 'Support Center', href: '/support', icon: MessageSquare, permission: PERMS.bookings.view },
|
|
{ name: 'Notifications', href: '/notifications', icon: Bell, permission: PERMS.notifications.send },
|
|
]
|
|
},
|
|
{
|
|
title: 'Security & Compliance',
|
|
items: [
|
|
{ name: 'Audit Logs', href: '/audit', icon: AlertTriangle, permission: PERMS.audit.view },
|
|
{ name: 'Fraud Detection', href: '/fraud', icon: Shield, permission: PERMS.fraud.view },
|
|
// { name: 'Verifayda Integration', href: '/verifayda', icon: UserCheck, permission: PERMS.admin },
|
|
]
|
|
},
|
|
{
|
|
title: 'Analytics & Reports',
|
|
items: [
|
|
{ name: 'Overall', href: '/reports/overall', icon: BarChart3, permission: PERMS.reports.view },
|
|
{ name: 'Finance', href: '/reports/finance', icon: DollarSign, permission: PERMS.reports.view },
|
|
{ name: 'Coaches', href: '/reports/coach-utilization', icon: Grid3x3, permission: PERMS.reports.view },
|
|
{ name: 'Seats', href: '/reports/seats', icon: Armchair, permission: PERMS.reports.view },
|
|
{ name: 'Blocked Seats', href: '/reports/blocked-seats', icon: Ban, permission: PERMS.reports.view },
|
|
{ name: 'Passengers', href: '/reports/passengers', icon: Users, permission: PERMS.reports.view },
|
|
{ name: 'Boarding', href: '/reports/boarding', icon: LogIn, permission: PERMS.reports.view },
|
|
{ name: 'Payments', href: '/reports/payments', icon: CreditCard, permission: PERMS.reports.view },
|
|
// { name: 'Payment Discrepancy', href: '/reports/payment-discrepancy', icon: AlertTriangle, permission: PERMS.reports.view },
|
|
// { name: 'Operational Reports', href: '/operational-reports', icon: FileText, permission: PERMS.reports.view },
|
|
]
|
|
},
|
|
{
|
|
title: 'System',
|
|
items: [
|
|
// { name: 'Agents', href: '/agents', icon: Briefcase, permission: PERMS.agents.view },
|
|
// { name: 'Users', href: '/settings/users', icon: Users, permission: PERMS.admin },
|
|
{ name: 'Settings', href: '/settings', icon: Settings, permission: PERMS.admin },
|
|
{ name: 'Health', href: '/health', icon: Activity, permission: PERMS.admin },
|
|
{ name: 'App Releases', href: '/app-releases', icon: Smartphone, permission: PERMS.admin },
|
|
]
|
|
}
|
|
];
|
|
|
|
// Hook to detect mobile devices
|
|
function useIsMobile() {
|
|
const [isMobile, setIsMobile] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const checkIsMobile = () => {
|
|
setIsMobile(window.innerWidth < 768); // md breakpoint
|
|
};
|
|
|
|
// Check on mount
|
|
checkIsMobile();
|
|
|
|
// Listen for resize events
|
|
window.addEventListener('resize', checkIsMobile);
|
|
return () => window.removeEventListener('resize', checkIsMobile);
|
|
}, []);
|
|
|
|
return isMobile;
|
|
}
|
|
|
|
export default function Sidebar() {
|
|
const pathname = usePathname();
|
|
const { user, logout, hasPermission } = useAuthStore();
|
|
const { isDark, toggleTheme } = useTheme();
|
|
const isMobile = useIsMobile();
|
|
|
|
// Initialize collapsed state based on mobile detection
|
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
|
|
// Update collapsed state when mobile status changes
|
|
useEffect(() => {
|
|
setIsCollapsed(isMobile);
|
|
}, [isMobile]);
|
|
|
|
return (
|
|
<div className={cn(
|
|
'flex h-screen flex-col transition-all duration-300',
|
|
'bg-[rgb(20,113,76)] dark:bg-gradient-to-b dark:from-slate-900 dark:via-slate-800 dark:to-slate-900',
|
|
'border-r border-[rgb(16,90,61)] dark:border-slate-700/50',
|
|
isCollapsed ? 'w-16' : 'w-72'
|
|
)}>
|
|
{/* Header */}
|
|
<div className="flex h-16 items-center justify-between px-4 border-b border-[rgb(16,90,61)] dark:border-slate-700/50">
|
|
{!isCollapsed && (
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-white/10 shadow-lg">
|
|
<Train className="h-6 w-6 text-white" />
|
|
</div>
|
|
<div>
|
|
<h1 className="text-lg font-bold text-white">EDR</h1>
|
|
<p className="text-xs text-white/70">Back-office Passenger Portal</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
<button
|
|
onClick={() => setIsCollapsed(!isCollapsed)}
|
|
className="rounded-lg p-1.5 text-white/70 hover:bg-white/10 hover:text-white dark:text-slate-400 dark:hover:bg-slate-700/50 dark:hover:text-white transition-colors"
|
|
>
|
|
{isCollapsed ? <ChevronRight className="h-4 w-4" /> : <ChevronLeft className="h-4 w-4" />}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex-1 overflow-y-auto px-3 py-4 space-y-6">
|
|
{navigationSections.map((section) => {
|
|
const visibleItems = section.items.filter(
|
|
(item) => !item.permission || hasPermission(item.permission)
|
|
);
|
|
if (visibleItems.length === 0) return null;
|
|
return (
|
|
<div key={section.title}>
|
|
{!isCollapsed && (
|
|
<h3 className="mb-2 px-3 text-xs font-semibold uppercase tracking-wider text-white/60 dark:text-slate-400">
|
|
{section.title}
|
|
</h3>
|
|
)}
|
|
<div className="space-y-1">
|
|
{visibleItems.map((item) => {
|
|
// Special handling for Settings to avoid conflict with User Management
|
|
let isActive;
|
|
if (item.href === '/settings') {
|
|
isActive = pathname === '/settings' ||
|
|
(pathname?.startsWith('/settings/') && !pathname.startsWith('/settings/users'));
|
|
} else if (item.href === '/payments') {
|
|
// Exact match only — avoid colliding with /reports/payments
|
|
isActive = pathname === '/payments' || pathname?.startsWith('/payments/');
|
|
} else {
|
|
isActive = pathname === item.href || pathname?.startsWith(item.href + '/');
|
|
}
|
|
return (
|
|
<Link
|
|
key={item.name}
|
|
href={item.href}
|
|
className={cn(
|
|
'flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200',
|
|
'hover:bg-white/10 dark:hover:bg-slate-700/50',
|
|
isActive
|
|
? 'bg-white/20 text-white shadow-md hover:bg-white/25'
|
|
: 'text-white/80 hover:text-white dark:text-slate-300 dark:hover:text-white',
|
|
isCollapsed && 'justify-center'
|
|
)}
|
|
title={isCollapsed ? item.name : undefined}
|
|
>
|
|
<item.icon className="h-5 w-5 flex-shrink-0" />
|
|
{!isCollapsed && <span>{item.name}</span>}
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
</div>
|
|
);
|
|
}
|