2import { useEffect, useState } from "react";
3import Link from "next/link";
4import { usePathname } from "next/navigation";
5import { defaultConfig } from "@/lib/config";
6import { useStore } from "@/contexts/StoreContext";
7import { Icon } from '@/contexts/IconContext';
9interface DashboardItem {
13 items?: { name: string; path: string; description?: string }[];
16// Helper function to get Material Icon name for section
17function getIconForSection(title: string): string {
18 const iconMap: Record<string, string> = {
19 'Point of Sale': 'point_of_sale',
20 'Account': 'account_circle',
21 'Dashboard': 'dashboard',
22 'Products': 'inventory_2',
24 'Purchasing': 'shopping_bag',
25 'Administration': 'admin_panel_settings',
26 'Reports': 'assessment',
27 'Analytics': 'analytics',
28 'Marketing': 'campaign',
33 return iconMap[title] || 'folder';
36export default function Navigation() {
37 const { storeName, session, logout } = useStore();
38 const [apiKey, setApiKey] = useState<string | null>(null);
39 const [config, setConfig] = useState(defaultConfig);
40 const pathname = usePathname();
41 const [mobileOpen, setMobileOpen] = useState(false);
42 const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>({});
44 // Determine if user is in management portal or retail store
45 const isManagementPortal = session?.store?.type === 'management';
48 const storedApiKey = sessionStorage.getItem("fieldpine_apikey");
49 setApiKey(storedApiKey);
51 const handler = () => setMobileOpen((v) => !v);
52 window.addEventListener('toggle-sidebar', handler as EventListener);
53 return () => window.removeEventListener('toggle-sidebar', handler as EventListener);
56 // Simple POS menu for retail stores
57 const storePosDashboard: DashboardItem[] = [
62 { name: "Home", path: "/pages/home", description: "Dashboard overview" }
66 title: "Point of Sale",
69 { name: "Sales", path: "/sales", description: "Process sales and returns" },
70 { name: "Sales History", path: "/pages/invoices", description: "View customer sales and receipts" },
71 { name: "Sales Report", path: "/pages/reports/pos-sales-report", description: "View today's sales" },
72 { name: "Monthly Report", path: "/pages/reports/franchise-report", description: "Monthly sales summary" },
73 { name: "Products", path: "/pages/products", description: "Edit products" },
74 { name: "Stock Levels", path: "/pages/products/stock-levels", description: "Check inventory" }
81 { name: "My Profile", path: "/pages/profile", description: "View profile" },
82 { name: "Settings", path: "/pages/settings", description: "Store settings" }
89 { name: "POS Help Center", path: "/pages/pos-help", description: "Store POS guide and FAQ" }
94 // Full admin menu for IIG management portal
95 const managementDashboard: DashboardItem[] = [
100 { name: "Home", path: "/pages/home", description: "Overview" },
101 { name: "Start Here", path: "/pages/start-here", description: "Training" },
102 { name: "Advisor", path: "/advisor", description: "Insights" }
106 title: "Point of Sale",
109 { name: "Sales", path: "/sales", description: "Process sales and returns" },
110 { name: "Invoices", path: "/pages/invoices", description: "Manage invoices" },
111 { name: "Sales Picking", path: "/pages/sales/picking", description: "Pick & ship orders" },
112 { name: "End of Day", path: "/pages/sales/end-of-day", description: "Closeout" },
113 { name: "Sales Reports", path: "/pages/reports/sales-reports", description: "Analysis" }
120 { name: "Products", path: "/pages/products", description: "Catalog" },
121 { name: "Purchase Orders", path: "/purchase-orders", description: "Product POs" },
122 { name: "Price Changes", path: "/pages/products/price-changes", description: "Pending Price Changes" },
123 { name: "Signs and Labels", path: "/pages/products/signs-labels", description: "Print" },
124 { name: "Stocktake", path: "/pages/products/stocktake", description: "New, Existing, Archived" }
131 { name: "Customers", path: "/pages/customers", description: "CRM" },
132 { name: "Loyalty", path: "/pages/customers/loyalty", description: "Retention" },
133 { name: "Customer Accounts", path: "/pages/customers/customer-accounts", description: "Customer Credit Management" }
140 { name: "Suppliers", path: "/pages/suppliers", description: "Vendors" }
147 { name: "Reports Library", path: "/pages/reports", description: "All business reports" },
148 { name: "Analytics Dashboard", path: "/pages/reports/analytics", description: "Visual Analytics" },
149 { name: "Franchise Reports", path: "/pages/reports/franchise-report", description: "Royalty Reports" },
150 { name: "Sales Reports", path: "/pages/reports/sales-reports", description: "Analysis" }
157 { name: "Marketing Programs", path: "/marketing", description: "Campaigns" },
158 { name: "Prepay", path: "/prepay", description: "Gift cards" }
165 { name: "Sale Processing", path: "/pages/settings/operations/sale-processing", description: "Sale behavior" },
166 { name: "Pricing", path: "/pages/settings/operations/pricing", description: "Price rules" },
167 { name: "Stock Control", path: "/pages/settings/operations/stock-control", description: "Inventory" },
168 { name: "Customer Accounts", path: "/pages/settings/operations/accounts", description: "Credit management" },
169 { name: "Loyalty Programs", path: "/pages/settings/operations/loyalty", description: "Rewards" },
170 { name: "Gift Cards", path: "/pages/settings/operations/gift-cards", description: "Prepay cards" },
171 { name: "End of Day", path: "/pages/settings/operations/end-of-day", description: "Till closeout" }
178 { name: "Departments", path: "/pages/settings/departments", description: "Department hierarchy" },
179 { name: "Printers & Cartridges", path: "/pages/settings/printers-cartridges", description: "Printer compatibility" },
180 { name: "Staff", path: "/pages/settings/staff", description: "Users" },
181 { name: "Stores", path: "/pages/settings/stores", description: "Locations" },
182 { name: "Lanes", path: "/pages/settings/stores/lanes", description: "POS lanes" },
183 { name: "Payment Types", path: "/pages/settings/payment-types", description: "Accepted payment methods" },
184 { name: "Your Details", path: "/pages/settings/your-details", description: "Company information" },
185 { name: "Photo Management", path: "/pages/settings/media/photos", description: "Product and store photos" },
186 { name: "Cloud Storage", path: "/pages/settings/media/cloud-storage", description: "Configure cloud destinations" },
187 { name: "Receipt Formats", path: "/pages/settings/media/receipt-formats", description: "Customize receipt layouts" }
194 { name: "Barcode Scanners", path: "/pages/settings/devices/barcode-scanners", description: "Scanner settings" },
195 { name: "Receipt Printers", path: "/pages/settings/devices/receipt-printers", description: "Printer configuration" },
196 { name: "Cash Drawers", path: "/pages/settings/devices/cash-drawers", description: "Cash drawer setup" },
197 { name: "Label Formats", path: "/pages/settings/devices/label-formats", description: "Price labels and tags" },
198 { name: "EFTPOS Terminals", path: "/pages/settings/devices/eftpos", description: "Payment terminals" },
199 { name: "Cameras", path: "/pages/settings/devices/cameras", description: "Security and product photos" },
200 { name: "Customer Displays", path: "/pages/settings/devices/customer-displays", description: "Pole displays and screens" },
201 { name: "Scales", path: "/pages/settings/devices/scales", description: "Weighing products" }
208 { name: "Security Settings", path: "/pages/technical/security", description: "Access controls and authentication" },
209 { name: "Firewall", path: "/pages/technical/firewall", description: "Network security and traffic control" },
210 { name: "GDPR Compliance", path: "/pages/technical/gdpr", description: "Data protection and privacy settings" },
211 { name: "PCI Requirements", path: "/pages/technical/pci", description: "Payment card industry compliance" },
212 { name: "Network Topology", path: "/pages/technical/network-topology", description: "View and manage network infrastructure" },
213 { name: "Database", path: "/pages/technical/database", description: "Database configuration and maintenance" },
214 { name: "API Settings", path: "/pages/technical/api-settings", description: "External API access and configuration" },
215 { name: "Analytics", path: "/pages/technical/analytics", description: "Business intelligence and reporting" },
216 { name: "Internet Services", path: "/pages/technical/internet-services", description: "Cloud services and external connections" },
217 { name: "Code Updates", path: "/pages/technical/code-updates", description: "Software version management" },
218 { name: "Support & Diagnostics", path: "/pages/technical/support-diagnostics", description: "Troubleshooting and system information" },
219 { name: "Documentation", path: "/pages/technical/documentation", description: "Dev Guide" },
220 { name: "API", path: "/api", description: "Dev" },
221 { name: "Audit Logs", path: "/audit", description: "Staff & System Activity" }
228 { name: "My Profile", path: "/pages/profile", description: "View profile" },
229 { name: "Settings", path: "/pages/settings", description: "Preferences and theme" }
234 // Determine which dashboard to show based on store type
235 const currentDashboard = isManagementPortal ? managementDashboard : storePosDashboard;
237 const toggleSection = (title: string) => {
238 setExpandedSections((prev) => ({ ...prev, [title]: !prev[title] }));
241 const expandAll = () => {
242 const allOpen = currentDashboard.reduce((acc, section) => {
243 acc[section.title] = true;
245 }, {} as Record<string, boolean>);
246 setExpandedSections(allOpen);
249 const collapseAll = () => {
250 setExpandedSections({});
253 // Hide navigation on landing and login pages
254 if (pathname === "/" || pathname === "/login") {
258 // close mobile nav when route changes
259 setMobileOpen(false);
264 {/* overlay for mobile when open */}
265 {mobileOpen && <div className="fixed inset-0 bg-black/30 z-30 md:hidden" onClick={() => setMobileOpen(false)} aria-hidden="true" />}
267 <aside className={`fixed md:relative left-0 top-0 h-screen md:h-screen w-[292px] flex-shrink-0 border-r border-border shadow-lg md:shadow-sm transform md:transform-none transition-transform duration-300 ease-in-out z-40 overflow-y-auto ${mobileOpen ? 'translate-x-0' : '-translate-x-full md:translate-x-0'}`} style={{ backgroundColor: 'var(--surface)' }}>
268 <div className="p-4 border-b" style={{ borderColor: 'var(--border)' }}>
269 <div className="flex items-center gap-3">
270 <div className="w-10 h-10 rounded-md bg-brand-2 text-brand flex items-center justify-center font-bold text-sm">
271 <Icon name={isManagementPortal ? 'business' : 'store'} size={24} />
274 <div className="text-sm font-extrabold">{storeName || session?.store?.name || 'EverydayPOS'}</div>
275 <div className="text-xs text-muted capitalize">
276 {isManagementPortal ? 'Management Portal' : 'Store POS'}
281 <div className="text-xs text-brand mt-2">Connected ✓</div>
285 <nav className="p-4 space-y-2">
286 {currentDashboard.map((section, index) => (
287 <details key={index} open={expandedSections[section.title] || false} className="rounded-lg overflow-hidden">
291 toggleSection(section.title);
293 className="cursor-pointer flex items-center p-2 text-text font-semibold hover:bg-surface-2 rounded-lg list-none transition"
294 style={{ color: 'var(--text)' }}
296 <div className="w-7 h-7 rounded-md bg-brand-2 text-brand mr-3 flex items-center justify-center text-sm font-bold">
297 <Icon name={getIconForSection(section.title)} size={20} />
299 <span>{section.title}</span>
300 <svg className={`ml-auto w-4 h-4 transition-transform ${expandedSections[section.title] ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
301 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 14l-7 7m0 0l-7-7m7 7V3" />
305 <div className="ml-4 mt-1 space-y-1 border-l border-border pl-3">
306 {section.items.map((item, itemIndex) => (
310 aria-label={item.name}
311 aria-current={pathname === item.path ? 'page' : undefined}
312 className={`block p-2 text-sm rounded transition ${
313 pathname === item.path
314 ? "bg-surface-2 text-brand font-semibold border-l-4 border-brand pl-2"
315 : "text-muted hover:bg-surface-2 hover:text-brand"
317 <div className="font-medium">{item.name}</div>
318 {item.description && (
319 <div className="text-xs text-muted">{item.description}</div>
329 <div className="p-4 border-t space-y-3">
330 {/* User Session Info */}
332 <div className="bg-surface-2 rounded-lg p-3 mb-3">
333 <div className="mb-2">
334 <div className="text-sm font-semibold text-text">{session.user.name}</div>
335 <div className="text-xs text-muted capitalize">{session.user.role}</div>
339 className="w-full px-3 py-2 text-sm rounded bg-surface-2 text-danger hover:bg-danger/10 transition font-medium border border-danger"
345 <div className="mt-2 space-y-1">
347 <div className="flex items-center gap-2 text-xs">
348 <span>{session.store.type === 'management' ? '🏢' : '🏪'}</span>
349 <span className="font-medium text-text">{session.store.name}</span>
351 {/* Store Type Badge */}
352 {session.store.type === 'management' ? (
353 <div className="inline-flex items-center px-2 py-1 bg-brand-2 text-brand text-xs font-medium rounded">
357 <div className="inline-flex items-center px-2 py-1 bg-surface-2 text-text text-xs font-medium rounded border border-border">
366 <div className="flex gap-2">
369 className="flex-1 px-3 py-2 text-xs rounded border border-border bg-surface text-text hover:bg-surface-2 transition font-medium">
373 onClick={collapseAll}
374 className="flex-1 px-3 py-2 text-xs rounded border border-border bg-surface text-text hover:bg-surface-2 transition font-medium">