Merge pull request 'AppMenu : Dynamic Menus' (#356) from AppMenu into Tenant_Manag

Reviewed-on: #356
Merged
This commit is contained in:
pramod.mahajan 2025-08-23 11:57:43 +00:00
commit b00fc04a82
5 changed files with 89 additions and 21 deletions

View File

@ -22,6 +22,7 @@ const EmpAttendance = ({ employee }) => {
data = [],
isLoading: loading,
isFetching,
isError,
error,
refetch,
} = useAttendanceByEmployee(employee, dateRange.startDate, dateRange.endDate);
@ -145,7 +146,7 @@ const EmpAttendance = ({ employee }) => {
</div>
<div className="table-responsive text-nowrap">
{!loading && data.length === 0 && <span>No employee logs</span>}
{error && <div className="text-center">{error}</div>}
{isError && <div className="text-center">{error.message}</div>}
{loading && !data && <div className="text-center">Loading...</div>}
{data && data.length > 0 && (
<table className="table mb-0">

View File

@ -0,0 +1,36 @@
const SkeletonLine = ({ height = 50, width = "100%", className = "" }) => (
<div
className={`skeleton mb-2 ${className}`}
style={{
height,
width,
}}
></div>
);
export const MenuItemSkeleton = ({ hasSubmenu = false, submenuCount = 3 }) => {
return (
<li className="menu-item">
<div className="menu-link d-flex align-items-center gap-2 ">
{/* icon placeholder */}
<SkeletonLine height={25} width="25px" className="rounded" />
{/* text placeholder */}
<SkeletonLine height={25} width="100%" />
</div>
{/* Submenu skeletons */}
{hasSubmenu && (
<ul className="menu-sub mt-1 ms-4">
{[...Array(submenuCount)].map((_, idx) => (
<li key={idx} className="menu-item">
<div className="menu-link d-flex align-items-center gap-2">
<SkeletonLine height={20} width="20px" className="rounded" />
<SkeletonLine height={24} width="100px" />
</div>
</li>
))}
</ul>
)}
</li>
);
};

View File

@ -2,10 +2,13 @@ import React from "react";
import { Link, NavLink, useLocation, useNavigate } from "react-router-dom";
import menuData from "../../data/menuData.json";
import { getCachedProfileData } from "../../slices/apiDataManager";
import { useSidBarMenu } from "../../hooks/useProfile";
import { MenuItemSkeleton } from "./MenuItemSkeleton";
const Sidebar = () => {
const navigate = useNavigate();
const { data, isError, isLoading, isFetched,error } = useSidBarMenu();
console.log(data)
return (
<aside
id="layout-menu"
@ -33,33 +36,46 @@ const Sidebar = () => {
<div className="menu-inner-shadow"></div>
<ul className="menu-inner py-1">
{menuData.map((section) => (
<React.Fragment key={(Math.random() + 1).toString(36)}>
{section.header && (
{isError && (
<div className="text-center text-small">{error.message}</div>
)}
{isLoading && (
<>
{[...Array(7)].map((_, idx) => (
<MenuItemSkeleton
key={idx}
hasSubmenu={idx % 2 === 1}
submenuCount={Math.floor(Math.random() * 3) + 2}
/>
))}
</>
)}
{data &&
data?.data.map((section) => (
<React.Fragment key={(Math.random() + 1).toString(36)}>
{/* {section.header && (
<li className="menu-header small text-uppercase">
<span className="menu-header-text">{section.header}</span>
</li>
)}
{section.items.map(MenuItem)}
</React.Fragment>
))}
)} */}
{section.items.map(MenuItem)}
</React.Fragment>
))}
</ul>
</aside>
);
};
const MenuItem = (item) => {
item.id = Math.random();
const location = useLocation();
const isActive = location.pathname === item.link;
const hasSubmenu = item.submenu && item.submenu.length > 0;
const hasSubmenu = Array.isArray(item.submenu) && item.submenu.length > 0;
const isSubmenuActive =
hasSubmenu &&
item.submenu.some((subitem) => location.pathname === subitem.link);
hasSubmenu && item.submenu.some((sub) => location.pathname === sub.link);
return (
<li
key={(Math.random() + 1).toString(36)}
className={`menu-item ${isActive || isSubmenuActive ? "active" : ""} ${
hasSubmenu && isSubmenuActive ? "open" : ""
}`}
@ -67,21 +83,24 @@ const MenuItem = (item) => {
<NavLink
aria-label={`Navigate to ${item.text} ${!item.available ? "Pro" : ""}`}
to={item.link}
className={`menu-link ${item.submenu ? "menu-toggle" : ""}`}
key={(Math.random() + 1).toString(36)}
target={item.link.includes("http") ? "_blank" : undefined}
className={`menu-link ${hasSubmenu ? "menu-toggle" : ""}`}
target={item.link?.includes("http") ? "_blank" : undefined}
>
<i className={`menu-icon tf-icons ${item.icon}`}></i>
<div>{item.text}</div>{" "}
<div>{item.text}</div>
{item.available === false && (
<div className="badge bg-label-primary fs-tiny rounded-pill ms-auto">
Pro
</div>
)}
</NavLink>
{item.submenu && (
<ul className="menu-sub" key={(Math.random() + 1).toString(36)}>
{item.submenu.map(MenuItem)}
{/* Only render submenu if exists */}
{hasSubmenu && (
<ul className="menu-sub">
{item.submenu.map((sub) => (
<MenuItem key={sub.id || sub.link} {...sub} />
))}
</ul>
)}
</li>

View File

@ -99,3 +99,13 @@ export const useProfile = () => {
refetch,
};
};
export const useSidBarMenu = ()=>{
const userLogged = useSelector((store)=>store.globalVariables.loginUser);
return useQuery({
queryKey:["AppMenu"],
queryFn:async()=> await AuthRepository.appmenu(),
enabled: !!userLogged
})
}

View File

@ -15,6 +15,8 @@ const AuthRepository = {
logout: (data) => api.post("/api/auth/logout", data),
profile: () => api.get("/api/user/profile"),
changepassword: (data) => api.post("/api/auth/change-password", data),
appmenu:()=>api.get('/api/AppMenu/sidebar/menu-section')
};
export default AuthRepository;