diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index ef62c8b9..e71a8045 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -1,10 +1,10 @@ import { apiFetch } from '../api'; import { useState, useEffect, useRef } from 'react'; -import { - Bell, - Search, - Settings, - HelpCircle, +import { + Bell, + Search, + Settings, + HelpCircle, Calendar, LayoutDashboard, Store, @@ -52,7 +52,7 @@ const Header = () => { const [notifications, setNotifications] = useState([]); const [selectedIndex, setSelectedIndex] = useState(0); - const filteredPages = PAGES.filter(page => + const filteredPages = PAGES.filter(page => page.label.toLowerCase().includes(searchTerm.toLowerCase()) || page.category.toLowerCase().includes(searchTerm.toLowerCase()) ).slice(0, 8); @@ -115,45 +115,45 @@ const Header = () => { }; const handleNotificationClick = async (notif: any) => { - try { - await apiFetch(`http://${window.location.hostname}:8080/api/notifications/mark-read/${notif.id}`, { method: 'POST' }); - if (notif.link) navigate(notif.link); - setShowNotifications(false); - fetchNotifications(); - } catch (error) { - console.error('Error marking notification as read:', error); - } + try { + await apiFetch(`http://${window.location.hostname}:8080/api/notifications/mark-read/${notif.id}`, { method: 'POST' }); + if (notif.link) navigate(notif.link); + setShowNotifications(false); + fetchNotifications(); + } catch (error) { + console.error('Error marking notification as read:', error); + } }; const markAllAsRead = async () => { - try { - await apiFetch(`http://${window.location.hostname}:8080/api/notifications/mark-all-read`, { method: 'POST' }); - fetchNotifications(); - setShowNotifications(false); - } catch (error) { - console.error('Error marking all as read:', error); - } + try { + await apiFetch(`http://${window.location.hostname}:8080/api/notifications/mark-all-read`, { method: 'POST' }); + fetchNotifications(); + setShowNotifications(false); + } catch (error) { + console.error('Error marking all as read:', error); + } }; const getNotificationIcon = (type: string) => { - switch(type) { - case 'FEEDBACK': return ; - case 'PURCHASE': return ; - case 'PRODUCT': return ; - case 'COUPON': return ; - default: return ; - } + switch (type) { + case 'FEEDBACK': return ; + case 'PURCHASE': return ; + case 'PRODUCT': return ; + case 'COUPON': return ; + default: return ; + } }; return (
- - + { setSearchTerm(e.target.value); @@ -166,7 +166,7 @@ const Header = () => { {showResults && searchTerm && ( - { key={page.path} onClick={() => handleNavigate(page.path)} onMouseEnter={() => setSelectedIndex(index)} - className={`w-full px-4 py-2.5 flex items-center justify-between transition-colors ${index === selectedIndex ? 'bg-[#0f4475]/5' : ''}`} + className={`w-full px-4 py-2.5 flex items-center justify-between transition-colors ${index === selectedIndex ? 'bg-[#003317]/5' : ''}`} >
-
+
{page.icon}
-
+
{page.label}
@@ -219,76 +219,76 @@ const Header = () => {
- - {showNotifications && ( - -
-

Notifications

- {notifications.length > 0 && ( - - )} + {showNotifications && ( + +
+

Notifications

+ {notifications.length > 0 && ( + + )} +
+ +
+ {notifications.length > 0 ? ( + notifications.map((notif) => ( + + )) + ) : ( +
+
+
- -
- {notifications.length > 0 ? ( - notifications.map((notif) => ( - - )) - ) : ( -
-
- -
-

All caught up!

-
- )} -
- - )} +

All caught up!

+
+ )} +
+
+ )}
- - + + - + diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 9c702445..81d6bb3c 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -1,16 +1,16 @@ import React, { useState } from 'react'; import { NavLink, useLocation, useNavigate } from 'react-router-dom'; -import { - BarChart3, - ChevronRight, - CreditCard, - Gauge, - LayoutGrid, - MessageSquare, - ShoppingCart, - Store, - Table2, - Users, +import { + BarChart3, + ChevronRight, + CreditCard, + Gauge, + LayoutGrid, + MessageSquare, + ShoppingCart, + Store, + Table2, + Users, Wallet, ShoppingBag, Receipt, @@ -44,27 +44,27 @@ interface MenuItem { const menuItems: MenuItem[] = [ { title: 'Dashboard', icon: Gauge, path: '/dashboard' }, { title: 'Store Dashboard', icon: LayoutGrid, path: '/store-dashboard' }, - { - title: 'Sale', - icon: Receipt, + { + title: 'Sale', + icon: Receipt, subMenu: [ { title: 'Orders', path: '/sale/orders' }, { title: 'Archived Bills', path: '/sale/archived-bills' } - ] + ] }, - { - title: 'Customers', - icon: Users, + { + title: 'Customers', + icon: Users, subMenu: [ { title: 'Orders', path: '/customers/orders' } - ] + ] }, - { - title: 'Purchases', - icon: ShoppingBag, + { + title: 'Purchases', + icon: ShoppingBag, subMenu: [ - { - title: 'Vendor', + { + title: 'Vendor', nestedMenu: [ { title: 'Dashboard', path: '/purchases/dashboard' }, { title: 'Orders', path: '/purchases/orders' }, @@ -72,10 +72,10 @@ const menuItems: MenuItem[] = [ { title: 'Bills', path: '/purchases/bills' }, { title: 'Vendors', path: '/purchases/vendor' }, { title: 'Purchase Analytics', path: '/purchases/analytics' } - ] + ] }, - { - title: 'Intent', + { + title: 'Intent', nestedMenu: [ { title: 'Orders Dashboard', path: '/purchases/intent/orders-dashboard' }, { title: 'Receives Dashboard', path: '/purchases/intent/receives-dashboard' }, @@ -84,48 +84,48 @@ const menuItems: MenuItem[] = [ { title: 'Receives Summary', path: '/purchases/intent/receives-summary' }, { title: 'Request', path: '/purchases/intent/request' }, { title: 'Stores', path: '/purchases/intent/stores' } - ] + ] } - ] + ] }, - { - title: 'Inventory', - icon: ShoppingCart, + { + title: 'Inventory', + icon: ShoppingCart, subMenu: [ { title: 'New Arrivals', path: '/inventory/new-arrivals' }, { title: 'Base Items', path: '/inventory/base' }, { title: 'Products', path: '/inventory/products' } - ] + ] }, - { - title: 'Expense', - icon: Wallet, + { + title: 'Expense', + icon: Wallet, subMenu: [ { title: 'Overview', path: '/expense/overview' }, { title: 'Category', path: '/expense/category' } - ] + ] }, { title: 'Reports', icon: BarChart3, path: '/reports' }, - { - title: 'Stores', - icon: Store, + { + title: 'Stores', + icon: Store, subMenu: [ { title: 'Terminals', path: '/stores/terminals' }, { title: 'Managers', path: '/stores/managers' }, { title: 'Staffs', path: '/stores/staffs' }, { title: 'Stalls', path: '/stores/stalls' } - ] + ] }, { title: 'Feedback', icon: MessageSquare, path: '/feedback' }, - { - title: 'Ritz', - icon: CircleDollarSign, + { + title: 'Ritz', + icon: CircleDollarSign, subMenu: [ { title: 'Overview', path: '/ritz/overview' }, { title: 'Ritz in Circulation', path: '/ritz/circulation' }, { title: 'Manage Wallets', path: '/ritz/wallets' }, { title: 'Coupon Codes', path: '/ritz/coupons' } - ] + ] }, ]; @@ -141,7 +141,7 @@ const Sidebar = () => { const filteredMenuItems = menuItems.filter(item => { if (userRole === 'master') return true; - + // Map title to permission ID const permissionMap: Record = { 'Dashboard': 'dashboard', @@ -156,7 +156,7 @@ const Sidebar = () => { 'Feedback': 'feedback', 'Ritz': 'ritz' }; - + return userPermissions.includes(permissionMap[item.title]); }); @@ -169,24 +169,24 @@ const Sidebar = () => { }; const toggleMenu = (title: string) => { - setOpenMenus((prev) => - prev.includes(title) - ? prev.filter((item) => item !== title) + setOpenMenus((prev) => + prev.includes(title) + ? prev.filter((item) => item !== title) : [...prev, title] ); }; const toggleNestedMenu = (title: string) => { - setOpenNestedMenus((prev) => - prev.includes(title) - ? prev.filter((item) => item !== title) + setOpenNestedMenus((prev) => + prev.includes(title) + ? prev.filter((item) => item !== title) : [...prev, title] ); }; // Check if a sub-menu should be open based on the current location React.useEffect(() => { - const currentMenu = menuItems.find(item => + const currentMenu = menuItems.find(item => item.subMenu?.some(sub => location.pathname.startsWith(sub.path || '')) ); if (currentMenu && !openMenus.includes(currentMenu.title)) { @@ -206,9 +206,9 @@ const Sidebar = () => {
- @@ -216,27 +216,27 @@ const Sidebar = () => {
{/* The main dashboard list starts here */} - +
    {filteredMenuItems.map((item) => (
  • {item.subMenu ? (
    -
    - + {openMenus.includes(item.title) && ( { onClick={() => toggleNestedMenu(sub.title)} className={cn( "w-full flex items-center justify-between py-2 px-4 rounded-lg text-[13px] font-medium transition-all group", - openNestedMenus.includes(sub.title) ? "text-[#0f4475] bg-[#0f4475]/5" : "text-[#64748b] hover:text-[#1e293b]" + openNestedMenus.includes(sub.title) ? "text-[#003317] bg-[#003317]/5" : "text-[#64748b] hover:text-[#1e293b]" )} >
    {sub.title}
    @@ -280,7 +280,7 @@ const Sidebar = () => { openNestedMenus.includes(sub.title) ? "rotate-90" : "" )} /> - + {openNestedMenus.includes(sub.title) && ( { to={nested.path!} className={({ isActive }) => cn( "flex items-center gap-3 py-1.5 px-4 rounded-lg text-[12px] font-medium transition-all", - isActive ? "text-[#0f4475] bg-white shadow-sm" : "text-[#64748b] hover:text-[#1e293b]" + isActive ? "text-[#003317] bg-white shadow-sm" : "text-[#64748b] hover:text-[#1e293b]" )} >
    @@ -313,8 +313,8 @@ const Sidebar = () => { title={`Go to ${sub.title}`} className={({ isActive }) => cn( "flex items-center gap-3 py-2 px-4 rounded-lg text-[13px] font-medium transition-all group relative", - isActive - ? "text-white bg-[#0f4475] shadow-md shadow-[#0f4475]/10" + isActive + ? "text-white bg-[#003317] shadow-md shadow-[#003317]/10" : "text-[#64748b] hover:text-[#1e293b] hover:translate-x-1" )} > @@ -322,13 +322,13 @@ const Sidebar = () => { <>
    {sub.title} {isActive && ( - )} @@ -347,8 +347,8 @@ const Sidebar = () => { title={`Go to ${item.title}`} className={({ isActive }) => cn( "flex items-center gap-3 p-2.5 rounded-xl text-sm font-semibold transition-all duration-200 group", - isActive - ? "bg-[#0f4475] text-white shadow-lg shadow-[#0f4475]/15" + isActive + ? "bg-[#003317] text-white shadow-lg shadow-[#003317]/15" : "text-[#475569] hover:bg-gray-50 hover:text-[#1e293b]" )} > @@ -376,9 +376,9 @@ const Sidebar = () => { ))}
- +
-
-