342 lines
12 KiB
JavaScript

import { useCallback, useEffect, useState, useMemo } from "react";
import getGreetingMessage from "../../utils/greetingHandler";
import {
cacheData,
clearAllCache,
getCachedData,
useSelectedProject,
} from "../../slices/apiDataManager";
import AuthRepository from "../../repositories/AuthRepository";
import { useDispatch, useSelector } from "react-redux";
import { changeMaster, setProjectId } from "../../slices/localVariablesSlice";
import useMaster from "../../hooks/masterHook/useMaster";
import { useProfile } from "../../hooks/useProfile";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import Avatar from "../../components/common/Avatar";
import { useChangePassword } from "../Context/ChangePasswordContext";
import { useProjects } from "../../hooks/useProjects";
import { useProjectName } from "../../hooks/useProjects";
import eventBus from "../../services/eventBus";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { ALLOW_PROJECTSTATUS_ID, MANAGE_PROJECT, UUID_REGEX } from "../../utils/constants";
import { useAuthModal, useLogout } from "../../hooks/useAuth";
const Header = () => {
const { profile } = useProfile();
const { data: masterData } = useMaster();
const location = useLocation();
const dispatch = useDispatch();
const navigate = useNavigate();
const { mutate: logout, isPending: logouting } = useLogout();
const { onOpen } = useAuthModal();
const { openChangePassword } = useChangePassword();
const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT);
const pathname = location.pathname;
// ======= MEMO CHECKS =======
const isDashboardPath = pathname === "/" || pathname === "/dashboard";
const isProjectPath = pathname === "/projects";
const isDirectory = pathname === "/directory";
const isEmployeeList = pathname === "/employees";
const isEmployeeProfile = UUID_REGEX.test(pathname);
const isMasters = pathname === "/masters";
const isExpensePath = pathname.startsWith("/expenses");
const hideDropPaths =
isDirectory || isEmployeeList || isMasters || isEmployeeProfile || isExpensePath;
const showProjectDropdown = !hideDropPaths;
// ===== Project Names & Selected Project =====
const { projectNames, loading: projectLoading, fetchData } = useProjectName();
const selectedProject = useSelectedProject();
const projectsForDropdown = useMemo(
() =>
isDashboardPath
? projectNames
: projectNames?.filter((project) =>
ALLOW_PROJECTSTATUS_ID.includes(project.projectStatusId)
),
[projectNames, isDashboardPath]
);
const currentProjectDisplayName = useMemo(() => {
if (projectLoading) return "Loading...";
if (!projectNames?.length) return "No Projects Assigned";
if (projectNames.length === 1) return projectNames[0].name;
if (selectedProject === null) return "All Projects";
const selectedObj = projectNames.find((p) => p.id === selectedProject);
return selectedObj
? selectedObj.name
: projectNames[0]?.name || "No Projects Assigned";
}, [projectLoading, projectNames, selectedProject]);
// ===== Role Helper =====
const getRole = (roles, joRoleId) => {
if (!Array.isArray(roles)) return "User";
return roles.find((r) => r.id === joRoleId)?.name || "User";
};
// ===== Navigate to Profile =====
const handleProfilePage = () =>
navigate(`/employee/${profile?.employeeInfo?.id}`);
// ===== Set default project on load =====
useEffect(() => {
if (
projectNames?.length &&
selectedProject === undefined &&
!getCachedData("hasReceived")
) {
if (projectNames.length === 1) {
dispatch(setProjectId(projectNames[0].id || null));
} else {
if (isDashboardPath) {
dispatch(setProjectId(null));
} else {
const firstAllowed = projectNames.find((project) =>
ALLOW_PROJECTSTATUS_ID.includes(project.projectStatusId)
);
dispatch(setProjectId(firstAllowed?.id || null));
}
}
}
}, [projectNames, selectedProject, dispatch, isDashboardPath]);
// ===== Event Handlers =====
const handler = useCallback(
async (data) => {
if (!HasManageProjectPermission) {
await fetchData();
if (data.projectIds?.includes(selectedProject)) {
cacheData("hasReceived", false);
}
}
},
[fetchData, selectedProject, HasManageProjectPermission]
);
const newProjectHandler = useCallback(
async (msg) => {
if (
msg.keyword === "Create_Project" ||
projectNames?.some((p) => p.id === msg.response?.id)
) {
await fetchData();
cacheData("hasReceived", false);
}
},
[projectNames, fetchData]
);
useEffect(() => {
dispatch(changeMaster("Job Role"));
}, [dispatch]);
useEffect(() => {
eventBus.on("assign_project_one", handler);
eventBus.on("project", newProjectHandler);
return () => {
eventBus.off("assign_project_one", handler);
eventBus.off("project", newProjectHandler);
};
}, [handler, newProjectHandler]);
// ===== Project Change =====
const handleProjectChange = (projectId) => {
dispatch(setProjectId(projectId));
if (isProjectPath && projectId !== null) {
navigate("/projects/details");
}
};
const shouldShowDropdown = projectNames && projectNames.length > 1;
return (
<nav
className="layout-navbar container-fluid mb-3 navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar"
>
<div className="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
<a
aria-label="toggle for sidebar"
className="nav-item nav-link px-0 me-xl-4"
>
<i className="bx bx-menu bx-sm"></i>
</a>
</div>
<div
className="navbar-nav-right d-flex align-items-center justify-content-between"
id="navbar-collapse"
>
{showProjectDropdown && (
<div className="align-items-center">
<i className="rounded-circle bx bx-building-house bx-sm-lg bx-md me-2"></i>
<div className="btn-group">
{shouldShowDropdown ? (
<button
className={`btn btn-sm-sm btn-xl dropdown-toggle px-1`}
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{currentProjectDisplayName}
</button>
) : (
<span className="btn btn-sm-sm btn-xl px-1">
{currentProjectDisplayName}
</span>
)}
{shouldShowDropdown &&
projectsForDropdown &&
projectsForDropdown.length > 0 && (
<ul
className="dropdown-menu"
style={{ overflow: "auto", maxHeight: "300px" }}
>
<li>
<button
className="dropdown-item"
onClick={() => handleProjectChange(null)}
>All Project</button>
</li>
{[...projectsForDropdown]
.sort((a, b) => a?.name?.localeCompare(b.name))
.map((project) => (
<li key={project?.id}>
<button
className="dropdown-item"
onClick={() => handleProjectChange(project?.id)}
>
{project?.name}
{project?.shortName && (
<span className="text-primary fw-semibold ms-1">
({project?.shortName})
</span>
)}
</button>
</li>
))}
</ul>
)}
</div>
</div>
)}
<ul className="navbar-nav flex-row align-items-center ms-md-auto">
{/* {HasManageProjectPermission && ( */}
{/* )} */}
<li className="nav-item navbar-dropdown dropdown-user dropdown">
<a
aria-label="dropdown profile avatar"
className="nav-link dropdown-toggle hide-arrow"
href="#"
data-bs-toggle="dropdown"
>
<div className="avatar avatar-online">
<Avatar
firstName={`${profile?.employeeInfo?.firstName}`}
lastName={`${profile?.employeeInfo?.lastName}`}
/>
</div>
</a>
<ul className="dropdown-menu dropdown-menu-end">
<li onClick={handleProfilePage}>
<a aria-label="go to profile" className="dropdown-item">
<div className="d-flex">
<div className="flex-shrink-0 me-3">
<div className="avatar avatar-online">
<Avatar
firstName={`${profile?.employeeInfo?.firstName}`}
lastName={`${profile?.employeeInfo?.lastName}`}
/>
</div>
</div>
<div className="flex-grow-1">
<span className="fw-medium d-block">
{profile?.employeeInfo?.firstName}
</span>
<small className="text-muted">
{getRole(masterData, profile?.employeeInfo?.joRoleId)}
</small>
</div>
</div>
</a>
</li>
<li>
<div className="dropdown-divider"></div>
</li>
{/* <li onClick={() => onOpen()}>
{" "}
<a className="dropdown-item cusor-pointer">
<i className="bx bx-transfer-alt me-2"></i>
<span className="align-middle">Switch Workspace</span>
</a>
</li> */}
<li onClick={handleProfilePage}>
<a
aria-label="go to profile"
className="dropdown-item cusor-pointer"
>
<i className="bx bx-user me-2"></i>
<span className="align-middle">My Profile</span>
</a>
</li>
<li onClick={handleProfilePage}>
<a
aria-label="go to setting "
className="dropdown-item cusor-pointer"
>
<i className="bx bx-cog me-2"></i>
<span className="align-middle">Settings</span>
</a>
</li>
<li onClick={openChangePassword}>
{" "}
<a
aria-label="go to profile"
className="dropdown-item cusor-pointer"
>
<i className="bx bx-lock-alt me-2"></i>
<span className="align-middle">Change Password</span>
</a>
</li>
<li>
<div className="dropdown-divider"></div>
</li>
<li>
<a
aria-label="click to log out"
className="dropdown-item cusor-pointer"
onClick={() => logout()}
>
{logouting ? (
"Please Wait"
) : (
<>
{" "}
<i className="bx bx-log-out me-2"></i>
<span className="align-middle">SignOut</span>
</>
)}
</a>
</li>
</ul>
</li>
</ul>
</div>
</nav>
);
};
export default Header;