444 lines
16 KiB
React
444 lines
16 KiB
React
|
|
import React, { useState, useEffect, useRef } from 'react';
|
||
|
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||
|
|
import { useSelector } from 'react-redux';
|
||
|
|
import {
|
||
|
|
FaBars, FaThLarge, FaHome, FaCog, FaChartBar, FaFileAlt,
|
||
|
|
FaMobileAlt, FaSignOutAlt, FaSignInAlt, FaArrowRight,
|
||
|
|
FaCaretRight
|
||
|
|
} from 'react-icons/fa';
|
||
|
|
import { MdSettings, MdOutlineReport, MdOutlineHome, MdOutlineKeyboardArrowDown, MdOutlineKeyboardArrowRight, } from 'react-icons/md';
|
||
|
|
import { BiLogOut, BiLogIn } from 'react-icons/bi';
|
||
|
|
import { AiOutlineAppstore, AiOutlineSetting, AiOutlineBarChart, AiOutlineFileText } from 'react-icons/ai';
|
||
|
|
import { GiHamburgerMenu } from 'react-icons/gi';
|
||
|
|
import './SideMenuPozo.scss';
|
||
|
|
import { FaCaretDown } from "react-icons/fa";
|
||
|
|
import { useDispatch } from 'react-redux';
|
||
|
|
import { useContext } from 'react';
|
||
|
|
import { AuthContext } from '../../AuthContext';
|
||
|
|
import PozoMenu from './PozoMenu';
|
||
|
|
import { getSession } from '../../Services/Others';
|
||
|
|
import {
|
||
|
|
changePreviewComponents,
|
||
|
|
changeSelectedPrintdummyData,
|
||
|
|
getTemplate,
|
||
|
|
} from '../../Features/ThemeChange/ThemeChange';
|
||
|
|
import {
|
||
|
|
changeBookingType,
|
||
|
|
changeOrderCardDetails,
|
||
|
|
changeReorderHoldDetails,
|
||
|
|
changeProductCategorie,
|
||
|
|
changeCustomerID,
|
||
|
|
changeSelectedCustId,
|
||
|
|
changeSelectedOption,
|
||
|
|
ChangeOverAllDiscSales,
|
||
|
|
ChangeOverAllDiscEstimate,
|
||
|
|
changeSummeryTotalAmount,
|
||
|
|
changeSearchedData,
|
||
|
|
changeBtoBQuickProductTransfer,
|
||
|
|
changeTipAmount,
|
||
|
|
GlobalDineinDefault,
|
||
|
|
} from '../../Features/BookingScreen/BookingData/BookingData';
|
||
|
|
import {
|
||
|
|
changeWholeSaleAuctionSelectedData,
|
||
|
|
changeWholeSaleGradeListData,
|
||
|
|
} from '../../Features/WholeSale/WholesaleData';
|
||
|
|
import { ChangeTotalAmount } from '../../Features/ExteraCharges/ExtraCharges';
|
||
|
|
import * as Offer from '../../Features/Offer/Offer';
|
||
|
|
import { FiMenu } from 'react-icons/fi';
|
||
|
|
import POZOMIND from "../../Images/PozomindWLogo.png";
|
||
|
|
import { ChangeFullFreeProductList, changeFullOfferAppliedProducts, changeLoyaltyConsumedQuantities } from '../../Features/Offer/Offernew/BookingOffernew';
|
||
|
|
|
||
|
|
const subDirectory = import.meta.env.BASE_URL;
|
||
|
|
|
||
|
|
// Filter functions (copied from AppPage.jsx)
|
||
|
|
function filterMenuItems(menu, configList) {
|
||
|
|
if (!Array.isArray(menu)) return [];
|
||
|
|
return menu.filter((item) => {
|
||
|
|
if (!item) return false;
|
||
|
|
if (item.label === "Sign Out") return true;
|
||
|
|
// For nested children, filter recursively
|
||
|
|
let filteredChildren = item.children ? filterMenuItems(item.children, configList) : undefined;
|
||
|
|
// Employee access: ConfigName
|
||
|
|
const config = configList?.find(
|
||
|
|
(config) => config.ConfigName === item.label && config.ReadAccess === 'Y'
|
||
|
|
);
|
||
|
|
// If this item or any of its children are accessible, include it
|
||
|
|
return (config || (filteredChildren && filteredChildren.length > 0));
|
||
|
|
}).map(item => ({
|
||
|
|
...item,
|
||
|
|
children: item.children ? filterMenuItems(item.children, configList) : undefined
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
function filterSadminUserMenuItems(menu, configList) {
|
||
|
|
if (!Array.isArray(menu)) return [];
|
||
|
|
return menu.filter((item) => {
|
||
|
|
if (!item) return false;
|
||
|
|
if (item.label === "Sign Out") return true;
|
||
|
|
|
||
|
|
let filteredChildren = item.children ? filterSadminUserMenuItems(item.children, configList) : undefined;
|
||
|
|
// Super admin user access: MenuName
|
||
|
|
const config = configList?.find(
|
||
|
|
(config) => config.MenuName === item.label && config.ReadAccess === 'Y'
|
||
|
|
);
|
||
|
|
return (config || (filteredChildren && filteredChildren.length > 0));
|
||
|
|
}).map(item => ({
|
||
|
|
...item,
|
||
|
|
children: item.children ? filterSadminUserMenuItems(item.children, configList) : undefined
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Capitalize helper
|
||
|
|
function capitalizeWords(str) {
|
||
|
|
if (typeof str !== 'string') return '';
|
||
|
|
return str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
||
|
|
}
|
||
|
|
|
||
|
|
// Recursively capitalize all menu item labels, filtering out falsy items and items without a label
|
||
|
|
function capitalizeMenuLabels(items) {
|
||
|
|
return (items || [])
|
||
|
|
.filter(item => item && typeof item.label === 'string' && item.label.trim() !== '')
|
||
|
|
.map(item => ({
|
||
|
|
...item,
|
||
|
|
label: capitalizeWords(item.label),
|
||
|
|
children: item.children ? capitalizeMenuLabels(item.children) : undefined
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
const SideMenuPozo = ({ items = [] }) => {
|
||
|
|
const { freeProductsCount, setFreeProductsCount, UserType, Access, SadminuserAccess } = useContext(AuthContext);
|
||
|
|
const dispatch = useDispatch();
|
||
|
|
const navigate = useNavigate();
|
||
|
|
const location = useLocation();
|
||
|
|
const [openKeys, setOpenKeys] = useState([]);
|
||
|
|
const [collapsed, setCollapsed] = useState(window.innerWidth < 768);
|
||
|
|
const [dropdownPosition, setDropdownPosition] = useState({});
|
||
|
|
const menuRef = useRef();
|
||
|
|
const CompId = getSession('CompId');
|
||
|
|
const BranchId = getSession('BranchId');
|
||
|
|
const AppId = getSession('AppId');
|
||
|
|
const DineinDefault = useSelector(GlobalDineinDefault);
|
||
|
|
const [collapse, setCollapse] = useState(false);
|
||
|
|
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const handleResize = () => {
|
||
|
|
setCollapse(window.innerWidth < 768);
|
||
|
|
};
|
||
|
|
window.addEventListener('resize', handleResize);
|
||
|
|
return () => window.removeEventListener('resize', handleResize);
|
||
|
|
}, []);
|
||
|
|
useEffect(() => {
|
||
|
|
const handleResize = () => {
|
||
|
|
setCollapsed(window.innerWidth < 768);
|
||
|
|
};
|
||
|
|
window.addEventListener('resize', handleResize);
|
||
|
|
return () => window.removeEventListener('resize', handleResize);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const handleClickOutside = (e) => {
|
||
|
|
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
||
|
|
setOpenKeys([]);
|
||
|
|
setDropdownPosition({});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
document.addEventListener('mousedown', handleClickOutside);
|
||
|
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const handleClickOutside = (event) => {
|
||
|
|
if (window.innerWidth < 768) {
|
||
|
|
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
||
|
|
setCollapse(true);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
document.addEventListener("mousedown", handleClickOutside);
|
||
|
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
// Debug current pathname
|
||
|
|
useEffect(() => {
|
||
|
|
console.log('Current pathname:', location.pathname);
|
||
|
|
}, [location.pathname]);
|
||
|
|
|
||
|
|
const isSelected = (key) => location.pathname === key;
|
||
|
|
const isOpen = (key) => openKeys.includes(key);
|
||
|
|
|
||
|
|
// Check if any child of this item is selected (for parent menu highlighting)
|
||
|
|
const hasSelectedChild = (item) => {
|
||
|
|
if (!item.children) return false;
|
||
|
|
return item.children.some(child => {
|
||
|
|
if (child.children) {
|
||
|
|
return hasSelectedChild(child);
|
||
|
|
}
|
||
|
|
return location.pathname === child.key;
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// Check if a dropdown item is currently selected
|
||
|
|
const isDropdownItemSelected = (item) => {
|
||
|
|
return location.pathname === item.key;
|
||
|
|
};
|
||
|
|
|
||
|
|
const navigatefun = async (e) => {
|
||
|
|
dispatch(changePreviewComponents({}));
|
||
|
|
dispatch(changeSelectedPrintdummyData(false));
|
||
|
|
dispatch(changeWholeSaleGradeListData([]));
|
||
|
|
dispatch(changeWholeSaleAuctionSelectedData([]));
|
||
|
|
dispatch(
|
||
|
|
changeBtoBQuickProductTransfer({
|
||
|
|
action: false,
|
||
|
|
selectedProducts: [],
|
||
|
|
})
|
||
|
|
);
|
||
|
|
if (e == `${subDirectory}sales/dine-in`) {
|
||
|
|
dispatch(changeTipAmount(0));
|
||
|
|
dispatch(changeBookingType('Dine In'));
|
||
|
|
dispatch(changeOrderCardDetails([]));
|
||
|
|
dispatch(changeReorderHoldDetails({}));
|
||
|
|
dispatch(changeProductCategorie(null));
|
||
|
|
dispatch(changeCustomerID(null));
|
||
|
|
dispatch(changeSelectedCustId(null));
|
||
|
|
dispatch(changeSelectedOption(null));
|
||
|
|
dispatch(ChangeOverAllDiscSales(0));
|
||
|
|
dispatch(ChangeOverAllDiscEstimate(0));
|
||
|
|
dispatch(Offer.changeSalesWiseOfferAmount(0));
|
||
|
|
dispatch(Offer.changeOverallOfferAmount(0));
|
||
|
|
dispatch(changeSummeryTotalAmount(0));
|
||
|
|
dispatch(Offer.changeOrderOfferDetail([]));
|
||
|
|
setFreeProductsCount(0);
|
||
|
|
dispatch(Offer.changeloyaltyPointsDiscountAmount(0));
|
||
|
|
dispatch(ChangeTotalAmount([]));
|
||
|
|
dispatch(ChangeFullFreeProductList([]));
|
||
|
|
dispatch(changeFullOfferAppliedProducts([]));
|
||
|
|
dispatch(changeLoyaltyConsumedQuantities({}))
|
||
|
|
}
|
||
|
|
if (e == `${subDirectory}sales`) {
|
||
|
|
dispatch(changeTipAmount(0));
|
||
|
|
dispatch(changeSearchedData(''));
|
||
|
|
dispatch(changeBookingType('TakeAway'));
|
||
|
|
dispatch(changeOrderCardDetails([]));
|
||
|
|
dispatch(changeReorderHoldDetails({}));
|
||
|
|
dispatch(changeProductCategorie(null));
|
||
|
|
dispatch(changeCustomerID(null));
|
||
|
|
dispatch(changeSelectedCustId(null));
|
||
|
|
dispatch(changeSelectedOption(null));
|
||
|
|
dispatch(ChangeOverAllDiscSales(0));
|
||
|
|
dispatch(ChangeOverAllDiscEstimate(0));
|
||
|
|
dispatch(Offer.changeSalesWiseOfferAmount(0));
|
||
|
|
dispatch(Offer.changeOverallOfferAmount(0));
|
||
|
|
dispatch(changeSummeryTotalAmount(0));
|
||
|
|
dispatch(Offer.changeOrderOfferDetail([]));
|
||
|
|
setFreeProductsCount(0);
|
||
|
|
dispatch(Offer.changeloyaltyPointsDiscountAmount(0));
|
||
|
|
dispatch(Offer.changeCoupenCodeAmount(0));
|
||
|
|
dispatch(ChangeFullFreeProductList([]));
|
||
|
|
dispatch(changeFullOfferAppliedProducts([]));
|
||
|
|
dispatch(ChangeTotalAmount([]));
|
||
|
|
dispatch(changeLoyaltyConsumedQuantities({}))
|
||
|
|
}
|
||
|
|
if (e == `${subDirectory}saleslayouts/print-selection`) {
|
||
|
|
dispatch(changeSelectedPrintdummyData(true));
|
||
|
|
}
|
||
|
|
navigate(e);
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleMenuClick = (item, parentKeys = [], event, level = 0) => {
|
||
|
|
if (item.children) {
|
||
|
|
if (openKeys.includes(item.key)) {
|
||
|
|
setOpenKeys(openKeys.filter(k => k !== item.key));
|
||
|
|
const newPositions = { ...dropdownPosition };
|
||
|
|
delete newPositions[item.key];
|
||
|
|
setDropdownPosition(newPositions);
|
||
|
|
} else {
|
||
|
|
// For top level (level 0), close all other open menus
|
||
|
|
// For nested levels, keep parent chain but close siblings
|
||
|
|
let newOpenKeys;
|
||
|
|
if (level === 0) {
|
||
|
|
// Close all other top-level menus
|
||
|
|
newOpenKeys = [];
|
||
|
|
} else {
|
||
|
|
// Keep only the parent chain, remove siblings
|
||
|
|
newOpenKeys = openKeys.filter(key => {
|
||
|
|
// Keep if it's in the parent chain
|
||
|
|
return parentKeys.includes(key);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Calculate position for dropdown
|
||
|
|
const rect = event.currentTarget.getBoundingClientRect();
|
||
|
|
let position;
|
||
|
|
|
||
|
|
if (level === 0) {
|
||
|
|
position = {
|
||
|
|
top: rect.top,
|
||
|
|
left: collapsed ? rect.right + 1 : 10
|
||
|
|
};
|
||
|
|
} else {
|
||
|
|
position = {
|
||
|
|
top: rect.top,
|
||
|
|
left: rect.right + 1
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
setDropdownPosition(prev => ({ ...prev, [item.key]: position }));
|
||
|
|
setOpenKeys([...newOpenKeys, item.key]);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (DineinDefault && item.key == `${subDirectory}sales`) {
|
||
|
|
navigatefun(`${subDirectory}sales/dine-in`);
|
||
|
|
setOpenKeys([]);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
navigatefun(item.key);
|
||
|
|
setOpenKeys([]);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
};
|
||
|
|
console.log(items, Access, "Employee items")
|
||
|
|
// Filter menu items based on access
|
||
|
|
let filteredMenuItems = (items || []).filter(Boolean);
|
||
|
|
if (UserType === 'Employee' && Array.isArray(Access) && Access.length > 0) {
|
||
|
|
filteredMenuItems = filterMenuItems(filteredMenuItems, Access);
|
||
|
|
} else if (UserType === 'Super Admin User' && Array.isArray(SadminuserAccess) && SadminuserAccess.length > 0) {
|
||
|
|
filteredMenuItems = filterSadminUserMenuItems(filteredMenuItems, SadminuserAccess);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Capitalize all labels, filter out items without a label
|
||
|
|
const menuItemsWithCapitalizedLabels = capitalizeMenuLabels(filteredMenuItems);
|
||
|
|
const bottomKeys = ['Support', 'Settings', 'Download', 'Sign Out'];
|
||
|
|
const bottomMenuItems = menuItemsWithCapitalizedLabels.filter(item =>
|
||
|
|
bottomKeys.includes(item.label)
|
||
|
|
);
|
||
|
|
const mainMenuItems = menuItemsWithCapitalizedLabels.filter(item =>
|
||
|
|
!bottomKeys.includes(item.label)
|
||
|
|
);
|
||
|
|
// Recursive render for menu and submenus
|
||
|
|
const renderMenuItems = (items, parentKeys = [], level = 0) => (
|
||
|
|
<div className={`side-menu-list${level > 0 ? ' nested-list' : ''}`}>
|
||
|
|
{(items || []).filter(Boolean).map((item) => (
|
||
|
|
<div key={item.key}>
|
||
|
|
<div
|
||
|
|
className={`${level > 0 ? 'dropdown-submenu-item' : 'menu-item'}${isSelected(item.key) || (level === 0 && hasSelectedChild(item)) ? ' selecteded' : ''}${isOpen(item.key) ? ' open' : ''}${item.children ? ' has-children' : ''}`}
|
||
|
|
onClick={(e) => { e.stopPropagation(); handleMenuClick(item, parentKeys, e, level); }}
|
||
|
|
// style={{ paddingLeft: `${level * 10 + 1}px` }}
|
||
|
|
>
|
||
|
|
{item.icon && level === 0 && <span className='menu-icon' style={{
|
||
|
|
fontSize: collapse ? "1.5rem" : ""
|
||
|
|
}}>{item.icon}</span>}
|
||
|
|
{!collapse && <span className='menu-label'>{item.label}</span>}
|
||
|
|
{!collapse && <span className='menu-labelDot'>{isDropdownItemSelected(item) ? '●' : ''}</span>}
|
||
|
|
{item.children && !collapse && (
|
||
|
|
<span className='menu-arrow'>
|
||
|
|
{level === 0 ? <FaCaretRight size={14} /> : <MdOutlineKeyboardArrowRight />}
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
{item.children && isOpen(item.key) && !collapse && (
|
||
|
|
<div
|
||
|
|
className={`dropdown-submenu${level > 0 ? ' nested' : ''}`}
|
||
|
|
|
||
|
|
>
|
||
|
|
{renderMenuItems(item.children, [...parentKeys, item.key], level + 1)}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
|
||
|
|
// Custom onClick handler for PozoMenu
|
||
|
|
const handlePozoMenuClick = (e) => {
|
||
|
|
console.log('PozoMenu clicked:', e);
|
||
|
|
navigatefun(e.key);
|
||
|
|
};
|
||
|
|
|
||
|
|
// Get current selected key based on location
|
||
|
|
const getCurrentSelectedKey = () => {
|
||
|
|
return location.pathname;
|
||
|
|
};
|
||
|
|
|
||
|
|
const isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
||
|
|
const isAndroid = /Android/i.test(navigator.userAgent);
|
||
|
|
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className={`SideMenuPozo-Master${collapse ? ' collapsed' : ''}`} ref={menuRef}
|
||
|
|
style={{ height: isMobile ? "88%" : "" }}
|
||
|
|
>
|
||
|
|
<div style={{
|
||
|
|
width: "100%", display: "flex", flexDirection: "column", gap: "10px",
|
||
|
|
height: "68vh", overflow: "auto"
|
||
|
|
}}>
|
||
|
|
<div className="paypreLogoDiv">
|
||
|
|
<div className="shopNameNav" style={{ width: "100%" }}>
|
||
|
|
<div style={{
|
||
|
|
display: "flex", flexDirection: "row",
|
||
|
|
alignItems: "center", gap: "4px", padding: "0px 10px", justifyContent: "space-between"
|
||
|
|
}}>
|
||
|
|
<FiMenu
|
||
|
|
size={26}
|
||
|
|
onClick={() => setCollapse((prev) => !prev)}
|
||
|
|
style={{ cursor: 'pointer' }}
|
||
|
|
/>
|
||
|
|
{collapse ? "" :
|
||
|
|
// <img src={POZOMIND} alt="PozoMindLogo" width="80px" height={"45px"} className='PozoMindLogo' />
|
||
|
|
<div>
|
||
|
|
PozoApp
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
</div>
|
||
|
|
{/* <div> */}
|
||
|
|
{/* <div>{branchName}</div> */}
|
||
|
|
<p>{/* {'Palacode'} , {'Tamilnadu'} , {'India'} */}</p>
|
||
|
|
{/* </div> */}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{/* Top Menu using PozoMenu */}
|
||
|
|
<div className='top-menu'>
|
||
|
|
<PozoMenu
|
||
|
|
items={mainMenuItems}
|
||
|
|
mode="vertical"
|
||
|
|
collapse={collapse}
|
||
|
|
selectedKey={getCurrentSelectedKey()}
|
||
|
|
style={{
|
||
|
|
width: collapse ? '60px' : '200px',
|
||
|
|
background: 'transparent',
|
||
|
|
boxShadow: 'none'
|
||
|
|
}}
|
||
|
|
onClick={handlePozoMenuClick}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{/* Bottom Menu using PozoMenu */}
|
||
|
|
<div className='bottom-menu' style={{
|
||
|
|
height: isMobile ? "30vh" : ""
|
||
|
|
}}>
|
||
|
|
<PozoMenu
|
||
|
|
items={bottomMenuItems}
|
||
|
|
mode="vertical"
|
||
|
|
collapse={collapse}
|
||
|
|
selectedKey={getCurrentSelectedKey()}
|
||
|
|
isBottomMenu={true}
|
||
|
|
style={{
|
||
|
|
width: collapse ? '60px' : '200px',
|
||
|
|
background: 'transparent',
|
||
|
|
boxShadow: 'none'
|
||
|
|
}}
|
||
|
|
onClick={handlePozoMenuClick}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default SideMenuPozo;
|