501 lines
20 KiB
JavaScript

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, useProjectName } from "../../hooks/useProjects"; // Make sure useProjects is imported if needed elsewhere
import { useCallback, useEffect, useState } from "react";
import eventBus from "../../services/eventBus";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { MANAGE_PROJECT } from "../../utils/constants";
import { useMemo } from "react";
const Header = () => {
const { profile } = useProfile();
const location = useLocation();
const dispatch = useDispatch();
const { data, loading: masterLoading } = useMaster(); // Renamed loading to masterLoading for clarity
const navigate = useNavigate();
const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT);
const isDirectoryPath = /^\/directory$/.test(location.pathname);
const isProjectPath = /^\/projects$/.test(location.pathname);
const isDashboard =
/^\/dashboard$/.test(location.pathname) || /^\/$/.test(location.pathname);
// Define the specific project status IDs you want to filter by
const allowedProjectStatusIds = [
"603e994b-a27f-4e5d-a251-f3d69b0498ba", // On Hold
"cdad86aa-8a56-4ff4-b633-9c629057dfef", // In Progress
"b74da4c2-d07e-46f2-9919-e75e49b12731", // Active
];
const getRole = (roles, joRoleId) => {
if (!Array.isArray(roles)) return "User";
let role = roles.find((role) => role.id === joRoleId);
return role ? role.name : "User";
};
const handleLogout = (e) => {
e.preventDefault();
logout();
};
const logout = async () => {
try {
let data = {
refreshToken: localStorage.getItem("refreshToken"),
};
AuthRepository.logout(data)
.then((response) => {
localStorage.removeItem("jwtToken");
localStorage.removeItem("refreshToken");
localStorage.removeItem("user");
localStorage.clear();
clearAllCache();
window.location.href = "/auth/login";
})
.catch((error) => {
localStorage.removeItem("jwtToken");
localStorage.removeItem("refreshToken");
localStorage.removeItem("user");
clearAllCache();
window.location.href = "/auth/login";
});
} catch (error) {
console.error(
"Error during logout:",
error?.response?.data || error.message
);
}
};
const handleProfilePage = () => {
navigate(`/employee/${profile?.employeeInfo?.id}?for=attendance`);
};
const { projectNames, loading: projectLoading, fetchData } = useProjectName(); // Renamed loading to projectLoading
const selectedProject = useSelectedproject();
// Filter projects for the dropdown based on the current path and allowed statuses
const projectsForDropdown = useMemo(() => {
if (!projectNames) return []; // Return empty array if projectNames is not yet fetched
return isDashboard
? projectNames
: projectNames?.filter(project => allowedProjectStatusIds.includes(project.projectStatusId));
}, [projectNames, isDashboard, allowedProjectStatusIds]);
// Determine the display text for the project section
let currentProjectDisplayName = "Loading...";
if (!projectLoading && projectNames) {
if (projectNames.length === 0) {
// If no projects are assigned at all
currentProjectDisplayName = "No Projects Assigned";
} else if (projectNames.length === 1) {
// If there's exactly one project overall (unfiltered list)
currentProjectDisplayName = projectNames[0].name;
} else { // projectNames.length > 1 (multiple projects)
if (selectedProject === null) {
currentProjectDisplayName = "All Projects";
} else {
const selectedProjectObj = projectNames.find(
(p) => p?.id === selectedProject
);
currentProjectDisplayName = selectedProjectObj ? selectedProjectObj.name : "Unknown Project";
}
}
// Determine the display text for the project dropdown
let displayText = "Loading..."; // Default to loading
if (!projectLoading && projectNames) { // Only update if not loading and projectNames is available
if (selectedProject === null) {
displayText = "All Projects";
} else {
// Find the selected project from the full projectNames list
const selectedProjectObj = projectNames.find( // Use projectNames directly here
(p) => p?.id === selectedProject
);
displayText = selectedProjectObj ? selectedProjectObj.name : "All Projects"; // Fallback to "All Projects" if selected project is not found
}
}
const { openChangePassword } = useChangePassword();
// Effect to set initial projectId based on scenarios
useEffect(() => {
if (
projectNames &&
projectNames.length > 0 &&
selectedProject === undefined && // Only set if no project is explicitly selected yet
!getCachedData("hasReceived") // To avoid re-setting on every render
selectedProject === undefined && // Only set default if no project is currently selected
!getCachedData("hasReceived") // Check if this flag is still relevant for your caching strategy
) {
if (projectNames.length === 1) {
// If only one project exists, automatically select it
dispatch(setProjectId(projectNames[0]?.id || null));
} else {
// If multiple projects, default to "All Projects" on dashboard, or first allowed for others
if (isDashboard) {
dispatch(setProjectId(null)); // Default to "All Projects" for dashboard when multiple projects
} else {
const firstAllowedProject = projectNames.find(project => allowedProjectStatusIds.includes(project.projectStatusId));
dispatch(setProjectId(firstAllowedProject?.id || null));
}
const firstAllowedProject = projectNames.find(project => allowedProjectStatusIds.includes(project.projectStatusId));
dispatch(setProjectId(firstAllowedProject?.id || null)); // Fallback to null if no allowed projects
}
}
}, [projectNames, selectedProject, dispatch, isDashboard, allowedProjectStatusIds]);
const handler = useCallback(
async (data) => {
if (!HasManageProjectPermission) {
await fetchData();
const projectExist = data.projectIds.some(
(item) => item === selectedProject
);
if (projectExist) {
cacheData("hasReceived", false);
}
}
},
[fetchData, selectedProject, HasManageProjectPermission]
);
const newProjectHandler = useCallback(
async (msg) => {
if (HasManageProjectPermission && msg.keyword === "Create_Project") {
await fetchData();
} else if (projectNames?.some((item) => item.id === msg.response.id)) {
await fetchData();
}
cacheData("hasReceived", false);
},
[HasManageProjectPermission, 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]);
const handleProjectChange = (projectId) => {
dispatch(setProjectId(projectId));
if (isProjectPath && projectId !== null) {
navigate(`/projects/details?resetDates=true`);
} else if (isProjectPath && projectId === null) {
navigate("/projects");
} else if (isDashboard) {
}
};
const shouldShowDropdown =
isDashboard || (projectsForDropdown && projectsForDropdown.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"
>
{projectNames && !isDirectoryPath && (
<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">
{/* Conditionally render as a dropdown toggle button or a plain span */}
{shouldShowDropdown ? (
<button
className={`btn btn-sm-sm btn-xl dropdown-toggle px-1`}
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{currentProjectDisplayName}
</button>
) : (
// If no dropdown (single or zero projects), just display the text
// If only one project or no projects (and not dashboard), just display its name/message without a dropdown
<span className="btn btn-sm-sm btn-xl px-1">
{currentProjectDisplayName}
{projectLoading ? "Loading..." :
(projectsForDropdown && projectsForDropdown.length === 1
? projectsForDropdown[0].name
: (isDashboard ? "All Projects" : "No Projects")) // Handle "No Projects" for non-dashboard views
}
</span>
)}
{/* Only render the dropdown menu if shouldShowDropdown is true AND there are projects to display in it */}
{/* Only render the dropdown menu if shouldShowDropdown is true AND there are projects to show */}
{shouldShowDropdown && projectsForDropdown && projectsForDropdown.length > 0 && (
<ul
className="dropdown-menu"
style={{ overflow: "auto", maxHeight: "300px" }}
>
{isDashboard && ( // "All Projects" option only appears in the dropdown if on dashboard and multiple projects
<li>
<button
className="dropdown-item"
onClick={() => handleProjectChange(null)} // Set projectId to null for "All Projects"
>
All Projects
</button>
</li>
)}
{[...projectsForDropdown] // Sort the conditionally filtered list
.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">
<li className="nav-item dropdown-shortcuts navbar-dropdown dropdown me-2 me-xl-0">
<a
className="nav-link dropdown-toggle hide-arrow"
data-bs-toggle="dropdown"
data-bs-auto-close="true"
aria-expanded="false"
>
<i className="icon-base bx bx-grid-alt icon-md"></i>
</a>
<div className="dropdown-menu dropdown-menu-end p-0">
<div className="dropdown-menu-header border-bottom">
<div className="dropdown-header d-flex align-items-center py-3">
<h6 className="mb-0 me-auto">Shortcuts</h6>
<a
className="dropdown-shortcuts-add py-2 cusror-pointer"
data-bs-toggle="tooltip"
data-bs-placement="top"
aria-label="Add shortcuts"
data-bs-original-title="Add shortcuts"
>
<i className="icon-base bx bx-plus-circle text-heading"></i>
</a>
</div>
</div>
<div className="dropdown-shortcuts-list scrollable-container ps">
<div className="row row-bordered overflow-visible g-0">
<div className="dropdown-shortcuts-item col">
<a
onClick={() => navigate(`/dashboard`)}
className="text-heading text-truncate cursor-pointer"
>
<span className="dropdown-shortcuts-icon rounded-circle mb-3">
<i className="icon-base bx bx-home icon-26px text-heading"></i>
</span>
Dashboard
</a>
<small>User Dashboard</small>
</div>
<div className="dropdown-shortcuts-item col">
<a
onClick={() => navigate(`/projects`)}
className="text-heading text-truncate cursor-pointer"
>
<span className="dropdown-shortcuts-icon rounded-circle mb-3">
<i className="icon-base bx bx-building-house icon-26px text-heading"></i>
</span>
Projects
</a>
<small>Projects List</small>
</div>
</div>
<div className="row row-bordered overflow-visible g-0">
<div className="dropdown-shortcuts-item col">
<a
onClick={() => navigate(`/employees`)}
className="text-heading text-truncate cursor-pointer"
>
<span className="dropdown-shortcuts-icon rounded-circle mb-3">
<i className="icon-base bx bxs-user-account icon-26px text-heading"></i>
</span>
Employees
</a>
<small>Manage Employees</small>
</div>
<div className="dropdown-shortcuts-item col">
<a
onClick={() => navigate(`/activities/attendance`)}
className="text-heading text-truncate cursor-pointer"
>
<span className="dropdown-shortcuts-icon rounded-circle mb-3">
<i className="icon-base bx bx-user-check icon-26px text-heading"></i>
</span>
Attendance
</a>
<small>Manage Attendance</small>
</div>
</div>
<div className="row row-bordered overflow-visible g-0">
<div className="dropdown-shortcuts-item col">
<a
onClick={() => navigate(`/activities/task`)}
className="text-heading text-truncate cursor-pointer"
>
<span className="dropdown-shortcuts-icon rounded-circle mb-3">
<i className="icon-base bx bxs-wrench icon-26px text-heading"></i>
</span>
Allocate Work
</a>
<small>Work Allocations</small>
</div>
<div className="dropdown-shortcuts-item col">
<a
onClick={() => navigate(`/activities/records`)}
className="text-heading text-truncate cursor-pointer"
>
<span className="dropdown-shortcuts-icon rounded-circle mb-3">
<i className="icon-base bx bx-list-ul icon-26px text-heading"></i>
</span>
Daily Work Log
</a>
<small>Daily Work Allocations</small>
</div>
</div>
</div>
</div>
</li>
<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(data, profile?.employeeInfo?.joRoleId)}
</small>
</div>
</div>
</a>
</li>
<li>
<div className="dropdown-divider"></div>
</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"
href="/logout"
onClick={handleLogout}
>
<i className="bx bx-power-off me-2"></i>
<span className="align-middle">Log Out</span>
</a>
</li>
</ul>
</li>
</ul>
</div>
</nav>
);
};
export default Header;