handle one tenant have , directly move to dashboard once logged
This commit is contained in:
parent
b9b3788dda
commit
4afe43d116
@ -1,21 +1,16 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import LineChart from "../Charts/LineChart";
|
||||
import React, { useState, useMemo } from "react";
|
||||
import ApexChart from "../Charts/Circle";
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import { useDashboard_AttendanceData } from "../../hooks/useDashboard_Data";
|
||||
import ApexChart from "../Charts/Circle";
|
||||
|
||||
const LOCAL_STORAGE_PROJECT_KEY = "selectedAttendanceProjectId";
|
||||
import { useSelectedProject } from "../../hooks/useSelectedProject"; // ✅ your custom hook
|
||||
|
||||
const Attendance = () => {
|
||||
const { projects } = useProjects();
|
||||
const today = new Date().toISOString().split("T")[0]; // Format: YYYY-MM-DD
|
||||
const today = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
|
||||
const [selectedDate, setSelectedDate] = useState(today);
|
||||
const storedProjectId = localStorage.getItem(LOCAL_STORAGE_PROJECT_KEY);
|
||||
const initialProjectId = storedProjectId || "all";
|
||||
const [selectedProjectId, setSelectedProjectId] = useState(initialProjectId);
|
||||
const [displayedProjectName, setDisplayedProjectName] =
|
||||
useState("Select Project");
|
||||
const [activeTab, setActiveTab] = useState("Summary");
|
||||
|
||||
// central project selection hook
|
||||
const selectedProjectId = useSelectedProject()
|
||||
|
||||
const {
|
||||
dashboard_Attendancedata: AttendanceData,
|
||||
@ -23,38 +18,24 @@ const Attendance = () => {
|
||||
error: isError,
|
||||
} = useDashboard_AttendanceData(selectedDate, selectedProjectId);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProjectId === "all") {
|
||||
setDisplayedProjectName("All Projects");
|
||||
} else if (projects) {
|
||||
const foundProject = projects.find((p) => p.id === selectedProjectId);
|
||||
setDisplayedProjectName(
|
||||
foundProject ? foundProject.name : "Select Project"
|
||||
);
|
||||
} else {
|
||||
setDisplayedProjectName("Select Project");
|
||||
}
|
||||
|
||||
localStorage.setItem(LOCAL_STORAGE_PROJECT_KEY, selectedProjectId);
|
||||
// project name derived once
|
||||
const displayedProjectName = useMemo(() => {
|
||||
if (selectedProjectId === "all") return "All Projects";
|
||||
const found = projects?.find((p) => p.id === selectedProjectId);
|
||||
return found?.name || "Select Project";
|
||||
}, [selectedProjectId, projects]);
|
||||
|
||||
const handleProjectSelect = (projectId) => {
|
||||
setSelectedProjectId(projectId);
|
||||
};
|
||||
|
||||
const handleDateChange = (e) => {
|
||||
setSelectedDate(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card h-100">
|
||||
<div className="card-header mb-1 pb-0 ">
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center mb-0 pb-0 ">
|
||||
{/* Header */}
|
||||
<div className="card-header mb-1 pb-0">
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<div className="card-title mb-0 text-start">
|
||||
<h5 className="mb-1">Attendance</h5>
|
||||
<p className="card-subtitle">Daily Attendance Data</p>
|
||||
</div>
|
||||
|
||||
{/* Project Dropdown */}
|
||||
<div className="btn-group">
|
||||
<button
|
||||
className="btn btn-outline-primary btn-sm dropdown-toggle"
|
||||
@ -68,7 +49,7 @@ const Attendance = () => {
|
||||
<li>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
onClick={() => handleProjectSelect("all")}
|
||||
onClick={() => setSelectedProjectId("all")}
|
||||
>
|
||||
All Projects
|
||||
</button>
|
||||
@ -77,7 +58,7 @@ const Attendance = () => {
|
||||
<li key={project.id}>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
onClick={() => handleProjectSelect(project.id)}
|
||||
onClick={() => setSelectedProjectId(project.id)}
|
||||
>
|
||||
{project.name}
|
||||
</button>
|
||||
@ -88,52 +69,43 @@ const Attendance = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center mb-0 mt-0 me-5 ms-5">
|
||||
{/* Tabs */}
|
||||
<div>
|
||||
<ul className="nav nav-tabs " role="tablist">
|
||||
<li className="nav-item">
|
||||
<button
|
||||
type="button"
|
||||
className={`nav-link ${
|
||||
activeTab === "Summary" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("Summary")}
|
||||
data-bs-toggle="tab"
|
||||
>
|
||||
Summary
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<button
|
||||
type="button"
|
||||
className={`nav-link ${
|
||||
activeTab === "Details" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("Details")}
|
||||
data-bs-toggle="tab"
|
||||
>
|
||||
Details
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{/* ✅ Date Picker Aligned Left with Padding */}
|
||||
<div className="ps-6 mb-3 mt-0">
|
||||
<div style={{ width: "120px" }}>
|
||||
<input
|
||||
type="date"
|
||||
className="form-control p-1"
|
||||
// style={{ fontSize: "1rem" }}
|
||||
value={selectedDate}
|
||||
onChange={handleDateChange}
|
||||
/>
|
||||
</div>
|
||||
{/* Tabs + Date Picker */}
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center me-5 ms-5">
|
||||
<ul className="nav nav-tabs">
|
||||
<li className="nav-item">
|
||||
<button
|
||||
type="button"
|
||||
className={`nav-link ${AttendanceData?.activeTab === "Summary" ? "active" : ""}`}
|
||||
onClick={() => (AttendanceData.activeTab = "Summary")}
|
||||
>
|
||||
Summary
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<button
|
||||
type="button"
|
||||
className={`nav-link ${AttendanceData?.activeTab === "Details" ? "active" : ""}`}
|
||||
onClick={() => (AttendanceData.activeTab = "Details")}
|
||||
>
|
||||
Details
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="ps-6 mb-3">
|
||||
<input
|
||||
type="date"
|
||||
className="form-control p-1"
|
||||
style={{ width: "120px" }}
|
||||
value={selectedDate}
|
||||
onChange={(e) => setSelectedDate(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Body */}
|
||||
<div className="card-body">
|
||||
{activeTab === "Summary" && (
|
||||
{/* Summary */}
|
||||
{AttendanceData?.activeTab === "Summary" && (
|
||||
<div className="row justify-content-center">
|
||||
<div className="col-12 col-md-6 d-flex flex-column align-items-center text-center mb-4">
|
||||
{isLoading ? (
|
||||
@ -143,7 +115,7 @@ const Attendance = () => {
|
||||
) : (
|
||||
AttendanceData && (
|
||||
<>
|
||||
<h5 className="fw-bold mb-0 text-center w-100">
|
||||
<h5 className="fw-bold mb-0">
|
||||
<i className="bx bx-task text-info"></i> Attendance
|
||||
</h5>
|
||||
<h4 className="mb-0 fw-bold">
|
||||
@ -164,11 +136,9 @@ const Attendance = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "Details" && (
|
||||
<div
|
||||
className="table-responsive"
|
||||
style={{ maxHeight: "300px", overflowY: "auto" }}
|
||||
>
|
||||
{/* Details */}
|
||||
{AttendanceData?.activeTab === "Details" && (
|
||||
<div className="table-responsive" style={{ maxHeight: "300px" }}>
|
||||
<table className="table table-hover mb-0 text-start">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -178,32 +148,17 @@ const Attendance = () => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{AttendanceData?.attendanceTable &&
|
||||
AttendanceData.attendanceTable.length > 0 ? (
|
||||
AttendanceData.attendanceTable.map((record, index) => (
|
||||
<tr key={index}>
|
||||
<td>
|
||||
{record.firstName} {record.lastName}
|
||||
</td>
|
||||
<td>
|
||||
{new Date(record.inTime).toLocaleTimeString([], {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})}
|
||||
</td>
|
||||
<td>
|
||||
{new Date(record.outTime).toLocaleTimeString([], {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})}
|
||||
</td>
|
||||
{AttendanceData?.attendanceTable?.length ? (
|
||||
AttendanceData.attendanceTable.map((r, i) => (
|
||||
<tr key={i}>
|
||||
<td>{r.firstName} {r.lastName}</td>
|
||||
<td>{r.inTime ? new Date(r.inTime).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "-"}</td>
|
||||
<td>{r.outTime ? new Date(r.outTime).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "-"}</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="3" className="text-center">
|
||||
No attendance data available
|
||||
</td>
|
||||
<td colSpan="3" className="text-center">No attendance data available</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
|
@ -132,7 +132,7 @@ const AttendanceOverview = () => {
|
||||
onClick={() => setView("table")}
|
||||
title="Table View"
|
||||
>
|
||||
<i class="bx bx-list-ul fs-5"></i>
|
||||
<i className="bx bx-list-ul fs-5"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,33 +1,28 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { useDashboardProjectsCardData } from "../../hooks/useDashboard_Data";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import GlobalRepository from "../../repositories/GlobalRepository";
|
||||
|
||||
const Projects = () => {
|
||||
const { projectsCardData } = useDashboardProjectsCardData();
|
||||
const [projectData, setProjectsData] = useState(projectsCardData);
|
||||
const {
|
||||
data: projectsCardData,
|
||||
isLoading,
|
||||
isError,
|
||||
error,
|
||||
refetch,
|
||||
} = useDashboardProjectsCardData();
|
||||
|
||||
useEffect(() => {
|
||||
setProjectsData(projectsCardData);
|
||||
}, [projectsCardData]);
|
||||
// When "project" event happens, just refetch
|
||||
const handler = () => {
|
||||
refetch();
|
||||
};
|
||||
|
||||
const handler = useCallback(
|
||||
async (msg) => {
|
||||
try {
|
||||
const response =
|
||||
await GlobalRepository.getDashboardProjectsCardData();
|
||||
setProjectsData(response.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
},
|
||||
[GlobalRepository]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
eventBus.on("project", handler);
|
||||
return () => eventBus.off("project", handler);
|
||||
}, [handler]);
|
||||
}, [refetch]);
|
||||
|
||||
const totalProjects = projectsCardData?.totalProjects ?? 0;
|
||||
const ongoingProjects = projectsCardData?.ongoingProjects ?? 0;
|
||||
|
||||
return (
|
||||
<div className="card p-3 h-100 text-center d-flex justify-content-between">
|
||||
@ -37,20 +32,29 @@ const Projects = () => {
|
||||
Projects
|
||||
</h5>
|
||||
</div>
|
||||
<div className="d-flex justify-content-around align-items-start mt-n2">
|
||||
<div>
|
||||
<h4 className="mb-0 fw-bold">
|
||||
{projectData.totalProjects?.toLocaleString()}
|
||||
</h4>
|
||||
<small className="text-muted">Total</small>
|
||||
|
||||
{isLoading ? (
|
||||
<div className="d-flex justify-content-center align-items-center flex-grow-1">
|
||||
<div className="spinner-border text-primary" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="mb-0 fw-bold">
|
||||
{projectData.ongoingProjects?.toLocaleString()}
|
||||
</h4>
|
||||
<small className="text-muted">Ongoing</small>
|
||||
) : isError ? (
|
||||
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center">
|
||||
{error?.message || "Error loading data"}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="d-flex justify-content-around align-items-start mt-n2">
|
||||
<div>
|
||||
<h4 className="mb-0 fw-bold">{totalProjects.toLocaleString()}</h4>
|
||||
<small className="text-muted">Total</small>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="mb-0 fw-bold">{ongoingProjects.toLocaleString()}</h4>
|
||||
<small className="text-muted">Ongoing</small>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,10 +1,16 @@
|
||||
import React from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
import { useDashboardTasksCardData } from "../../hooks/useDashboard_Data";
|
||||
|
||||
const TasksCard = () => {
|
||||
const projectId = useSelector((store) => store.localVariables?.projectId);
|
||||
const { tasksCardData, loading, error } = useDashboardTasksCardData(projectId);
|
||||
const projectId = useSelectedProject();
|
||||
|
||||
const {
|
||||
data: tasksCardData,
|
||||
isLoading,
|
||||
isError,
|
||||
error,
|
||||
} = useDashboardTasksCardData(projectId);
|
||||
|
||||
return (
|
||||
<div className="card p-3 h-100 text-center d-flex justify-content-between">
|
||||
@ -14,28 +20,30 @@ const TasksCard = () => {
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
// Loader will be displayed when loading is true
|
||||
{isLoading ? (
|
||||
// Loader while fetching
|
||||
<div className="d-flex justify-content-center align-items-center flex-grow-1">
|
||||
<div className="spinner-border text-primary" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
) : error ? (
|
||||
// Error message if there's an error
|
||||
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center">{error}</div>
|
||||
) : isError ? (
|
||||
// Show error
|
||||
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center">
|
||||
{error?.message || "Error loading data"}
|
||||
</div>
|
||||
) : (
|
||||
// Actual data when loaded successfully
|
||||
// Show data
|
||||
<div className="d-flex justify-content-around align-items-start mt-n2">
|
||||
<div>
|
||||
<h4 className="mb-0 fw-bold">
|
||||
{tasksCardData?.totalTasks?.toLocaleString()}
|
||||
{tasksCardData?.totalTasks?.toLocaleString() ?? 0}
|
||||
</h4>
|
||||
<small className="text-muted">Total</small>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="mb-0 fw-bold">
|
||||
{tasksCardData?.completedTasks?.toLocaleString()}
|
||||
{tasksCardData?.completedTasks?.toLocaleString() ?? 0}
|
||||
</h4>
|
||||
<small className="text-muted">Completed</small>
|
||||
</div>
|
||||
@ -45,4 +53,4 @@ const TasksCard = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default TasksCard;
|
||||
export default TasksCard;
|
||||
|
@ -1,33 +1,45 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useDashboardTeamsCardData } from "../../hooks/useDashboard_Data";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
|
||||
const Teams = () => {
|
||||
const projectId = useSelector((store) => store.localVariables?.projectId);
|
||||
const { teamsCardData, loading, error } = useDashboardTeamsCardData(projectId);
|
||||
const queryClient = useQueryClient();
|
||||
const projectId = useSelectedProject()
|
||||
|
||||
const [totalEmployees, setTotalEmployee] = useState(0);
|
||||
const [inToday, setInToday] = useState(0);
|
||||
|
||||
// Update state when API data arrives
|
||||
useEffect(() => {
|
||||
setTotalEmployee(teamsCardData?.totalEmployees || 0);
|
||||
setInToday(teamsCardData?.inToday || 0);
|
||||
}, [teamsCardData]);
|
||||
const {
|
||||
data: teamsCardData,
|
||||
isLoading,
|
||||
isError,
|
||||
error,
|
||||
} = useDashboardTeamsCardData(projectId);
|
||||
|
||||
// Handle real-time updates via eventBus
|
||||
const handler = useCallback((msg) => {
|
||||
if (msg.activity === 1) {
|
||||
setInToday((prev) => prev + 1);
|
||||
}
|
||||
}, []);
|
||||
const handler = useCallback(
|
||||
(msg) => {
|
||||
if (msg.activity === 1) {
|
||||
queryClient.setQueryData(["dashboardTeams", projectId], (old) => {
|
||||
if (!old) return old;
|
||||
return {
|
||||
...old,
|
||||
inToday: (old.inToday || 0) + 1,
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
[queryClient, projectId]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
eventBus.on("attendance", handler);
|
||||
return () => eventBus.off("attendance", handler);
|
||||
}, [handler]);
|
||||
|
||||
const inToday = teamsCardData?.inToday ?? 0;
|
||||
const totalEmployees = teamsCardData?.totalEmployees ?? 0;
|
||||
|
||||
return (
|
||||
<div className="card p-3 h-100 text-center d-flex justify-content-between">
|
||||
<div className="d-flex justify-content-start align-items-center mb-3">
|
||||
@ -36,18 +48,17 @@ const Teams = () => {
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
// Blue spinner loader
|
||||
{isLoading ? (
|
||||
<div className="d-flex justify-content-center align-items-center flex-grow-1">
|
||||
<div className="spinner-border text-primary" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
) : error ? (
|
||||
// Error message if data fetching fails
|
||||
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center">{error}</div>
|
||||
) : isError ? (
|
||||
<div className="text-danger flex-grow-1 d-flex justify-content-center align-items-center">
|
||||
{error?.message || "Error loading data"}
|
||||
</div>
|
||||
) : (
|
||||
// Display data once loaded
|
||||
<div className="d-flex justify-content-around align-items-start mt-n2">
|
||||
<div>
|
||||
<h4 className="mb-0 fw-bold">{totalEmployees.toLocaleString()}</h4>
|
||||
@ -63,4 +74,4 @@ const Teams = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Teams;
|
||||
export default Teams;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React from "react";
|
||||
import { hasUserPermission } from "../../utils/authUtils";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import {
|
||||
DIRECTORY_ADMIN,
|
||||
|
@ -5,12 +5,12 @@ import GlobalModel from "../common/GlobalModel";
|
||||
import { useTenantContext } from "../../pages/Tenant/TenantPage";
|
||||
import { useTenantDetailsContext } from "../../pages/Tenant/TenantDetails";
|
||||
import IconButton from "../common/IconButton";
|
||||
import { hasUserPermission } from "../../utils/authUtils";
|
||||
import { MANAGE_TENANTS } from "../../utils/constants";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
|
||||
const Profile = ({ data }) => {
|
||||
const {setEditTenant} = useTenantDetailsContext()
|
||||
const canUpdateTenant = hasUserPermission(MANAGE_TENANTS)
|
||||
const canUpdateTenant = useHasUserPermission(MANAGE_TENANTS)
|
||||
return (
|
||||
<>
|
||||
<div className="container-fuid">
|
||||
|
@ -28,13 +28,18 @@ export const useSelectTenant = (onSuccessCallBack) => {
|
||||
const res = await AuthRepository.selectTenant(tenantId);
|
||||
return res.data;
|
||||
},
|
||||
|
||||
onSuccess: (data) => {
|
||||
localStorage.setItem("ltkn", data.token);
|
||||
localStorage.setItem("rtkn", data.refreshToken);
|
||||
localStorage.setItem("jwtToken", data.token);
|
||||
localStorage.setItem("refreshToken", data.refreshToken);
|
||||
if (onSuccessCallBack) onSuccessCallBack();
|
||||
},
|
||||
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Error while creating project", "error");
|
||||
localStorage.removeItem("jwtToken");
|
||||
localStorage.removeItem("refreshToken")
|
||||
localStorage.removeItem("ctnt")
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import GlobalRepository from "../repositories/GlobalRepository";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
|
||||
// 🔹 Dashboard Progression Data Hook
|
||||
export const useDashboard_Data = ({ days, FromDate, projectId }) => {
|
||||
const [dashboard_data, setDashboard_Data] = useState([]);
|
||||
const [isLineChartLoading, setLoading] = useState(false);
|
||||
@ -38,120 +39,120 @@ export const useDashboard_Data = ({ days, FromDate, projectId }) => {
|
||||
};
|
||||
|
||||
|
||||
export const useDashboard_AttendanceData = (date, projectId) => {
|
||||
const [dashboard_Attendancedata, setDashboard_AttendanceData] = useState([]);
|
||||
const [isLineChartLoading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
// export const useDashboard_AttendanceData = (date, projectId) => {
|
||||
// const [dashboard_Attendancedata, setDashboard_AttendanceData] = useState([]);
|
||||
// const [isLineChartLoading, setLoading] = useState(false);
|
||||
// const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
// useEffect(() => {
|
||||
// const fetchData = async () => {
|
||||
// setLoading(true);
|
||||
// setError("");
|
||||
|
||||
try {
|
||||
const response = await GlobalRepository.getDashboardAttendanceData(date, projectId); // date in 2nd param
|
||||
setDashboard_AttendanceData(response.data);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch dashboard data.");
|
||||
console.error(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// try {
|
||||
// const response = await GlobalRepository.getDashboardAttendanceData(date, projectId); // date in 2nd param
|
||||
// setDashboard_AttendanceData(response.data);
|
||||
// } catch (err) {
|
||||
// setError("Failed to fetch dashboard data.");
|
||||
// console.error(err);
|
||||
// } finally {
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
if (date && projectId !== null) {
|
||||
fetchData();
|
||||
}
|
||||
}, [date, projectId]);
|
||||
// if (date && projectId !== null) {
|
||||
// fetchData();
|
||||
// }
|
||||
// }, [date, projectId]);
|
||||
|
||||
return { dashboard_Attendancedata, isLineChartLoading: isLineChartLoading, error };
|
||||
};
|
||||
// return { dashboard_Attendancedata, isLineChartLoading: isLineChartLoading, error };
|
||||
// };
|
||||
|
||||
|
||||
// 🔹 Dashboard Projects Card Data Hook
|
||||
export const useDashboardProjectsCardData = () => {
|
||||
const [projectsCardData, setProjectsData] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
// export const useDashboardProjectsCardData = () => {
|
||||
// const [projectsCardData, setProjectsData] = useState([]);
|
||||
// const [loading, setLoading] = useState(false);
|
||||
// const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchProjectsData = async () => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
// useEffect(() => {
|
||||
// const fetchProjectsData = async () => {
|
||||
// setLoading(true);
|
||||
// setError("");
|
||||
|
||||
try {
|
||||
const response = await GlobalRepository.getDashboardProjectsCardData();
|
||||
setProjectsData(response.data);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch projects card data.");
|
||||
console.error(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// try {
|
||||
// const response = await GlobalRepository.getDashboardProjectsCardData();
|
||||
// setProjectsData(response.data);
|
||||
// } catch (err) {
|
||||
// setError("Failed to fetch projects card data.");
|
||||
// console.error(err);
|
||||
// } finally {
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
fetchProjectsData();
|
||||
}, []);
|
||||
// fetchProjectsData();
|
||||
// }, []);
|
||||
|
||||
return { projectsCardData, loading, error };
|
||||
};
|
||||
// return { projectsCardData, loading, error };
|
||||
// };
|
||||
|
||||
// 🔹 Dashboard Teams Card Data Hook
|
||||
export const useDashboardTeamsCardData = (projectId) => {
|
||||
const [teamsCardData, setTeamsData] = useState({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
// export const useDashboardTeamsCardData = (projectId) => {
|
||||
// const [teamsCardData, setTeamsData] = useState({});
|
||||
// const [loading, setLoading] = useState(false);
|
||||
// const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchTeamsData = async () => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
// useEffect(() => {
|
||||
// const fetchTeamsData = async () => {
|
||||
// setLoading(true);
|
||||
// setError("");
|
||||
|
||||
try {
|
||||
const response = await GlobalRepository.getDashboardTeamsCardData(projectId);
|
||||
setTeamsData(response.data || {});
|
||||
} catch (err) {
|
||||
setError("Failed to fetch teams card data.");
|
||||
console.error("Error fetching teams card data:", err);
|
||||
setTeamsData({});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// try {
|
||||
// const response = await GlobalRepository.getDashboardTeamsCardData(projectId);
|
||||
// setTeamsData(response.data || {});
|
||||
// } catch (err) {
|
||||
// setError("Failed to fetch teams card data.");
|
||||
// console.error("Error fetching teams card data:", err);
|
||||
// setTeamsData({});
|
||||
// } finally {
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
fetchTeamsData();
|
||||
}, [projectId]);
|
||||
// fetchTeamsData();
|
||||
// }, [projectId]);
|
||||
|
||||
return { teamsCardData, loading, error };
|
||||
};
|
||||
// return { teamsCardData, loading, error };
|
||||
// };
|
||||
|
||||
export const useDashboardTasksCardData = (projectId) => {
|
||||
const [tasksCardData, setTasksData] = useState({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
// export const useDashboardTasksCardData = (projectId) => {
|
||||
// const [tasksCardData, setTasksData] = useState({});
|
||||
// const [loading, setLoading] = useState(false);
|
||||
// const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchTasksData = async () => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
// useEffect(() => {
|
||||
// const fetchTasksData = async () => {
|
||||
// setLoading(true);
|
||||
// setError("");
|
||||
|
||||
try {
|
||||
const response = await GlobalRepository.getDashboardTasksCardData(projectId);
|
||||
setTasksData(response.data);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch tasks card data.");
|
||||
console.error(err);
|
||||
setTasksData({});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// try {
|
||||
// const response = await GlobalRepository.getDashboardTasksCardData(projectId);
|
||||
// setTasksData(response.data);
|
||||
// } catch (err) {
|
||||
// setError("Failed to fetch tasks card data.");
|
||||
// console.error(err);
|
||||
// setTasksData({});
|
||||
// } finally {
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
fetchTasksData();
|
||||
}, [projectId]);
|
||||
// fetchTasksData();
|
||||
// }, [projectId]);
|
||||
|
||||
return { tasksCardData, loading, error };
|
||||
};
|
||||
// return { tasksCardData, loading, error };
|
||||
// };
|
||||
|
||||
|
||||
export const useAttendanceOverviewData = (projectId, days) => {
|
||||
@ -180,3 +181,75 @@ export const useAttendanceOverviewData = (projectId, days) => {
|
||||
|
||||
return { attendanceOverviewData, loading, error };
|
||||
};
|
||||
|
||||
|
||||
// -------------------Query----------------------------
|
||||
|
||||
// export const useDashboard_Data = (days, FromDate, projectId)=>{
|
||||
// return useQuery({
|
||||
// queryKey:["dashboardProjectProgress"],
|
||||
// queryFn:async()=> {
|
||||
// const payload = {
|
||||
// days,
|
||||
// FromDate: FromDate || '',
|
||||
// projectId: projectId || null,
|
||||
// };
|
||||
// const resp = await GlobalRepository.getDashboardProgressionData(payload);
|
||||
// return resp.data;
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
export const useDashboard_AttendanceData = (date,projectId)=>{
|
||||
return useQuery({
|
||||
queryKey:["dashboardAttendances",date,projectId],
|
||||
queryFn:async()=> {
|
||||
|
||||
const resp = await await GlobalRepository.getDashboardAttendanceData(date, projectId)
|
||||
return resp.data;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const useDashboardTeamsCardData =(projectId)=>{
|
||||
return useQuery({
|
||||
queryKey:["dashboardTeams",projectId],
|
||||
queryFn:async()=> {
|
||||
|
||||
const resp = await GlobalRepository.getDashboardTeamsCardData(projectId)
|
||||
return resp.data;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const useDashboardTasksCardData = (projectId) => {
|
||||
return useQuery({
|
||||
queryKey:["dashboardTasks",projectId],
|
||||
queryFn:async()=> {
|
||||
|
||||
const resp = await GlobalRepository.getDashboardTasksCardData(projectId)
|
||||
return resp.data;
|
||||
}
|
||||
})
|
||||
}
|
||||
// export const useAttendanceOverviewData = (projectId, days) => {
|
||||
// return useQuery({
|
||||
// queryKey:["dashboardAttendanceOverView",projectId],
|
||||
// queryFn:async()=> {
|
||||
|
||||
// const resp = await GlobalRepository.getAttendanceOverview(projectId, days);
|
||||
// return resp.data;
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
export const useDashboardProjectsCardData = () => {
|
||||
return useQuery({
|
||||
queryKey:["dashboardProjects"],
|
||||
queryFn:async()=> {
|
||||
|
||||
const resp = await GlobalRepository.getDashboardProjectsCardData();
|
||||
return resp.data;
|
||||
}
|
||||
})
|
||||
}
|
@ -13,7 +13,6 @@ import Regularization from "../../components/Activities/Regularization";
|
||||
import { useAttendance } from "../../hooks/useAttendance";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setProjectId } from "../../slices/localVariablesSlice";
|
||||
import { hasUserPermission } from "../../utils/authUtils";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import { REGULARIZE_ATTENDANCE } from "../../utils/constants";
|
||||
import eventBus from "../../services/eventBus";
|
||||
|
@ -1,16 +1,16 @@
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import { useProfile } from "../../hooks/useProfile";
|
||||
import TenantDetails from "./TenantDetails";
|
||||
import { hasUserPermission } from "../../utils/authUtils";
|
||||
import { VIEW_TENANTS } from "../../utils/constants";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Loader from "../../components/common/Loader";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
|
||||
const SelfTenantDetails = () => {
|
||||
const { profile, loading } = useProfile();
|
||||
const tenantId = profile?.employeeInfo?.tenantId;
|
||||
const navigate = useNavigate();
|
||||
const isSelfTenantView = hasUserPermission(VIEW_TENANTS);
|
||||
const isSelfTenantView = useHasUserPermission(VIEW_TENANTS);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSelfTenantView) {
|
||||
|
@ -20,7 +20,6 @@ import TenantFilterPanel from "../../components/Tenant/TenantFilterPanel";
|
||||
import { useDebounce } from "../../utils/appUtils";
|
||||
import { useFab } from "../../Context/FabContext";
|
||||
import { setCurrentTenant } from "../../slices/globalVariablesSlice";
|
||||
import { hasUserPermission } from "../../utils/authUtils";
|
||||
|
||||
// ------ Schema -------
|
||||
import {
|
||||
@ -35,6 +34,7 @@ import {
|
||||
VIEW_TENANTS,
|
||||
} from "../../utils/constants";
|
||||
import { useProfile } from "../../hooks/useProfile";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
|
||||
// ---------- Context ----------
|
||||
export const TenantContext = createContext();
|
||||
@ -63,9 +63,9 @@ const TenantPage = () => {
|
||||
const debouncedSearch = useDebounce(searchText, 500);
|
||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||
|
||||
const isSuperTenant = hasUserPermission(SUPPER_TENANT);
|
||||
const canManageTenants = hasUserPermission(MANAGE_TENANTS);
|
||||
const isSelfTenant = hasUserPermission(VIEW_TENANTS);
|
||||
const isSuperTenant = useHasUserPermission(SUPPER_TENANT);
|
||||
const canManageTenants = useHasUserPermission(MANAGE_TENANTS);
|
||||
const isSelfTenant = useHasUserPermission(VIEW_TENANTS);
|
||||
|
||||
const methods = useForm({
|
||||
resolver: zodResolver(filterSchema),
|
||||
|
@ -4,6 +4,7 @@ import { useAuthModal, useSelectTenant, useTenants } from "../../hooks/useAuth";
|
||||
import { useProfile } from "../../hooks/useProfile";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import AuthRepository from "../../repositories/AuthRepository";
|
||||
import Loader from "../../components/common/Loader";
|
||||
|
||||
const SwitchTenant = () => {
|
||||
const queryClient = useQueryClient();
|
||||
@ -27,6 +28,8 @@ const SwitchTenant = () => {
|
||||
localStorage.setItem("ctnt", tenantId);
|
||||
chooseTenant(tenantId);
|
||||
};
|
||||
|
||||
|
||||
const contentBody = (
|
||||
<div className="container text-black">
|
||||
<p className=" fs-5">Switch Workplace</p>
|
||||
@ -82,7 +85,7 @@ const SwitchTenant = () => {
|
||||
<button
|
||||
className="btn btn-primary btn-sm mt-2 align-self-start"
|
||||
onClick={() => handleTenantselection(tenant?.id)}
|
||||
disabled={isPending && pendingTenant === tenant.id}
|
||||
disabled={isPending && pendingTenant === tenant.id || currentTenant === tenant.id }
|
||||
>
|
||||
{isPending && pendingTenant === tenant.id
|
||||
? "Please Wait.."
|
||||
@ -95,7 +98,7 @@ const SwitchTenant = () => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <Modal isOpen={isOpen} onClose={onClose} body={contentBody} />;
|
||||
return <Modal isOpen={isOpen} onClose={onClose} body={isLoading ? <Loader/>:contentBody} />;
|
||||
};
|
||||
|
||||
export default SwitchTenant;
|
@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
|
||||
import { useTenants, useSelectTenant } from "../../hooks/useAuth.jsx";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import Dashboard from "../../components/Dashboard/Dashboard.jsx";
|
||||
import Loader from "../../components/common/Loader.jsx";
|
||||
|
||||
const TenantSelectionPage = () => {
|
||||
const [pendingTenant, setPendingTenant] = useState(null);
|
||||
@ -19,26 +20,48 @@ const TenantSelectionPage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem("ctnt")) {
|
||||
return navigate("/dashboard");
|
||||
navigate("/dashboard");
|
||||
}
|
||||
}, []);
|
||||
}, [navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && data?.data?.length === 1) {
|
||||
const tenant = data.data[0];
|
||||
handleTenantselection(tenant.id);
|
||||
}
|
||||
}, [isLoading, data]);
|
||||
|
||||
if (isLoading) return <Loader />;
|
||||
|
||||
if (isLoading) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
if (!data?.data?.length) {
|
||||
return (
|
||||
<div className="container-fluid py-5 text-center">
|
||||
<div className="spinner-border text-primary" role="status">
|
||||
<span className="visually-hidden">Loading tenants...</span>
|
||||
</div>
|
||||
<div className="text-center py-5">
|
||||
<p>No tenant assigned to your account.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="container-fluid py-4">
|
||||
{/* Header */}
|
||||
{/* Logo */}
|
||||
<div className="text-center">
|
||||
<img
|
||||
src="/img/brand/marco.png"
|
||||
alt="marco-logo"
|
||||
className="app-brand-logo-login img-fluid"
|
||||
style={{ maxHeight: "80px" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Heading */}
|
||||
<div className="text-center mb-4">
|
||||
<p className="fs-4 mb-1">Welcome</p>
|
||||
<p className="fs-5">
|
||||
<p className="fs-4 fw-bold mb-1">Welcome</p>
|
||||
<p className="fs-6 fs-md-5">
|
||||
Please select which dashboard you want to explore!!!
|
||||
</p>
|
||||
</div>
|
||||
@ -47,40 +70,45 @@ const TenantSelectionPage = () => {
|
||||
<div className="row justify-content-center g-4 m-auto">
|
||||
{data?.data.map((tenant) => (
|
||||
<div key={tenant.id} className="col-12 col-md-10 col-lg-8">
|
||||
<div className="d-flex flex-column flex-md-row gap-5 align-items-center align-items-md-start p-3 border rounded shadow-sm bg-white h-100">
|
||||
<div className="d-flex flex-column flex-md-row gap-4 align-items-center align-items-md-start p-3 border rounded shadow-sm bg-white h-100">
|
||||
|
||||
{/* Image */}
|
||||
<div className="flex-shrink-0 text-center">
|
||||
<img
|
||||
src={tenant?.logoImage || "/assets/img/SP-Placeholdeer.svg"}
|
||||
alt={tenant.name}
|
||||
className="img-fluid"
|
||||
className="img-fluid rounded"
|
||||
style={{
|
||||
maxWidth: "140px",
|
||||
height: "auto",
|
||||
aspectRatio: "3 / 2",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="d-flex flex-column text-start gap-2">
|
||||
<p className="fs-5 text-muted fw-semibold mb-1">
|
||||
<div className="d-flex flex-column text-start gap-2 w-100">
|
||||
{/* Title */}
|
||||
<p className="fs-5 fs-md-4 text-dark fw-semibold mb-1">
|
||||
{tenant?.name}
|
||||
</p>
|
||||
|
||||
{/* Industry */}
|
||||
<div className="d-flex flex-wrap gap-2 align-items-center">
|
||||
<p className="fw-semibold m-0">Industry:</p>
|
||||
<p className="m-0">
|
||||
<p className="m-0 text-muted">
|
||||
{tenant?.industry?.name || "Not Available"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
{tenant?.description && (
|
||||
<p className="text-start text-wrap m-0">
|
||||
<p className="text-start text-wrap text-muted small m-0">
|
||||
{tenant?.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Button */}
|
||||
<button
|
||||
className="btn btn-primary btn-sm mt-2 align-self-start"
|
||||
onClick={() => handleTenantselection(tenant?.id)}
|
||||
@ -96,6 +124,7 @@ const TenantSelectionPage = () => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
} from "../../hooks/useEmployees";
|
||||
import { useProjectName, useProjects } from "../../hooks/useProjects";
|
||||
import { useProfile } from "../../hooks/useProfile";
|
||||
import { hasUserPermission } from "../../utils/authUtils";
|
||||
|
||||
import {
|
||||
ITEMS_PER_PAGE,
|
||||
MANAGE_EMPLOYEES,
|
||||
@ -19,7 +19,6 @@ import {
|
||||
VIEW_TEAM_MEMBERS,
|
||||
} from "../../utils/constants";
|
||||
import { clearCacheKey } from "../../slices/apiDataManager";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import SuspendEmp from "../../components/Employee/SuspendEmp"; // Keep if you use SuspendEmp
|
||||
import {
|
||||
exportToCSV,
|
||||
@ -36,6 +35,7 @@ import { newlineChars } from "pdf-lib";
|
||||
import GlobalModel from "../../components/common/GlobalModel";
|
||||
import usePagination from "../../hooks/usePagination";
|
||||
import { setProjectId } from "../../slices/localVariablesSlice";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
|
||||
const EmployeeList = () => {
|
||||
const selectedProjectId = useSelector(
|
||||
|
@ -1,12 +0,0 @@
|
||||
import { useProfile } from "../hooks/useProfile";
|
||||
|
||||
export const hasUserPermission = (permission) => {
|
||||
const { profile } = useProfile();
|
||||
if (profile) {
|
||||
if (!permission || typeof permission !== "string") {
|
||||
return false;
|
||||
}
|
||||
return profile?.featurePermissions.includes(permission);
|
||||
}
|
||||
return false;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user