fixed dashboard graph

This commit is contained in:
pramod.mahajan 2025-10-01 16:21:08 +05:30
parent 49b597c833
commit 2531d91209
3 changed files with 67 additions and 125 deletions

View File

@ -4,6 +4,7 @@ import ReactApexChart from "react-apexcharts";
import { useAttendanceOverviewData } from "../../hooks/useDashboard_Data"; import { useAttendanceOverviewData } from "../../hooks/useDashboard_Data";
import flatColors from "../Charts/flatColor"; import flatColors from "../Charts/flatColor";
import ChartSkeleton from "../Charts/Skelton"; import ChartSkeleton from "../Charts/Skelton";
import { useSelectedProject } from "../../slices/apiDataManager";
const formatDate = (dateStr) => { const formatDate = (dateStr) => {
const date = new Date(dateStr); const date = new Date(dateStr);
@ -13,16 +14,20 @@ const formatDate = (dateStr) => {
}); });
}; };
const AttendanceOverview = ({projectId}) => { const AttendanceOverview = () => {
const [dayRange, setDayRange] = useState(7); const [dayRange, setDayRange] = useState(7);
const [view, setView] = useState("chart"); const [view, setView] = useState("chart");
const selectedProject = useSelectedProject()
const { attendanceOverviewData, loading, error } = useAttendanceOverviewData( const { data: attendanceOverviewData, isLoading, isError, error } = useAttendanceOverviewData(
projectId, selectedProject,
dayRange dayRange
); );
const { tableData, roles, dates } = useMemo(() => { const { tableData, roles, dates } = useMemo(() => {
if (!attendanceOverviewData || attendanceOverviewData.length === 0) {
return { tableData: [], roles: [], dates: [] };
}
const map = new Map(); const map = new Map();
attendanceOverviewData.forEach((entry) => { attendanceOverviewData.forEach((entry) => {
@ -35,7 +40,8 @@ const AttendanceOverview = ({projectId}) => {
...new Set(attendanceOverviewData.map((e) => e.role.trim())), ...new Set(attendanceOverviewData.map((e) => e.role.trim())),
]; ];
const sortedDates = [...map.keys()]; const sortedDates = [...map.keys()];
const data = sortedDates.map((date) => {
const tableData = sortedDates.map((date) => {
const row = { date }; const row = { date };
uniqueRoles.forEach((role) => { uniqueRoles.forEach((role) => {
row[role] = map.get(date)?.[role] ?? 0; row[role] = map.get(date)?.[role] ?? 0;
@ -43,12 +49,8 @@ const AttendanceOverview = ({projectId}) => {
return row; return row;
}); });
return { return { tableData, roles: uniqueRoles, dates: sortedDates };
tableData: data, }, [attendanceOverviewData,isLoading,selectedProject,dayRange]);
roles: uniqueRoles,
dates: sortedDates,
};
}, [attendanceOverviewData]);
const chartSeries = roles.map((role) => ({ const chartSeries = roles.map((role) => ({
name: role, name: role,
@ -62,41 +64,21 @@ const AttendanceOverview = ({projectId}) => {
height: 400, height: 400,
toolbar: { show: false }, toolbar: { show: false },
}, },
plotOptions: { plotOptions: { bar: { borderRadius: 2, columnWidth: "60%" } },
bar: { xaxis: { categories: tableData.map((row) => row.date) },
borderRadius: 2,
columnWidth: "60%",
},
},
xaxis: {
categories: tableData.map((row) => row.date),
},
yaxis: { yaxis: {
show: true, show: true,
axisBorder: { axisBorder: { show: true, color: "#78909C" },
show: true, axisTicks: { show: true, color: "#78909C", width: 6 },
color: "#78909C",
offsetX: 0,
offsetY: 0,
},
axisTicks: {
show: true,
borderType: "solid",
color: "#78909C",
width: 6,
offsetX: 0,
offsetY: 0,
},
},
legend: {
position: "bottom",
},
fill: {
opacity: 1,
}, },
legend: { position: "bottom" },
fill: { opacity: 1 },
colors: roles.map((_, i) => flatColors[i % flatColors.length]), colors: roles.map((_, i) => flatColors[i % flatColors.length]),
}; };
if (isLoading) return <div>Loading...</div>;
if (isError) return <p className="text-danger">{error.message}</p>;
return ( return (
<div className="bg-white p-4 rounded shadow d-flex flex-column"> <div className="bg-white p-4 rounded shadow d-flex flex-column">
{/* Header */} {/* Header */}
@ -116,18 +98,14 @@ const AttendanceOverview = ({projectId}) => {
<option value={30}>Last 30 Days</option> <option value={30}>Last 30 Days</option>
</select> </select>
<button <button
className={`btn btn-sm p-1 ${ className={`btn btn-sm p-1 ${view === "chart" ? "btn-primary" : "btn-outline-primary"}`}
view === "chart" ? "btn-primary" : "btn-outline-primary"
}`}
onClick={() => setView("chart")} onClick={() => setView("chart")}
title="Chart View" title="Chart View"
> >
<i className="bx bx-bar-chart-alt-2"></i> <i className="bx bx-bar-chart-alt-2"></i>
</button> </button>
<button <button
className={`btn btn-sm p-1 ${ className={`btn btn-sm p-1 ${view === "table" ? "btn-primary" : "btn-outline-primary"}`}
view === "table" ? "btn-primary" : "btn-outline-primary"
}`}
onClick={() => setView("table")} onClick={() => setView("table")}
title="Table View" title="Table View"
> >
@ -138,57 +116,30 @@ const AttendanceOverview = ({projectId}) => {
{/* Content */} {/* Content */}
<div className="flex-grow-1 d-flex align-items-center justify-content-center"> <div className="flex-grow-1 d-flex align-items-center justify-content-center">
{loading ? ( {view === "chart" ? (
<ChartSkeleton />
) : error ? (
<p className="text-danger">{error}</p>
) : view === "chart" ? (
<div className="w-100"> <div className="w-100">
<ReactApexChart <ReactApexChart options={chartOptions} series={chartSeries} type="bar" height={400} />
options={chartOptions}
series={chartSeries}
type="bar"
height={400}
/>
</div> </div>
) : ( ) : (
<div <div className="table-responsive w-100" style={{ maxHeight: "350px", overflowY: "auto" }}>
className="table-responsive w-100"
style={{ maxHeight: "350px", overflowY: "auto" }}
>
<table className="table table-bordered table-sm text-start align-middle mb-0"> <table className="table table-bordered table-sm text-start align-middle mb-0">
<thead <thead className="table-light" style={{ position: "sticky", top: 0, zIndex: 1 }}>
className="table-light"
style={{ position: "sticky", top: 0, zIndex: 1 }}
>
<tr> <tr>
<th style={{ background: "#f8f9fa", textTransform: "none" }}> <th style={{ background: "#f8f9fa", textTransform: "none" }}>Role</th>
Role
</th>
{dates.map((date, idx) => ( {dates.map((date, idx) => (
<th <th key={idx} style={{ background: "#f8f9fa", textTransform: "none" }}>
key={idx}
style={{ background: "#f8f9fa", textTransform: "none" }}
>
{date} {date}
</th> </th>
))} ))}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{roles.map((role) => ( {roles.map((role) => (
<tr key={role}> <tr key={role}>
<td>{role}</td> <td>{role}</td>
{tableData.map((row, idx) => { {tableData.map((row, idx) => {
const value = row[role]; const value = row[role];
const cellStyle = return <td key={idx} style={value > 0 ? { backgroundColor: "#d5d5d5" } : {}}>{value}</td>;
value > 0 ? { backgroundColor: "#d5d5d5" } : {};
return (
<td key={idx} style={cellStyle}>
{value}
</td>
);
})} })}
</tr> </tr>
))} ))}
@ -201,4 +152,4 @@ const AttendanceOverview = ({projectId}) => {
); );
}; };
export default AttendanceOverview; export default AttendanceOverview;

View File

@ -18,30 +18,20 @@ import { useSelectedProject } from "../../slices/apiDataManager";
import { useProjectName } from "../../hooks/useProjects"; import { useProjectName } from "../../hooks/useProjects";
const Dashboard = () => { const Dashboard = () => {
const { projectNames, loading: projectLoading } = useProjectName();
const selectedProject = useSelectedProject();
const dispatch = useDispatch();
// const { projectsCardData } = useDashboardProjectsCardData(); // const { projectsCardData } = useDashboardProjectsCardData();
// const { teamsCardData } = useDashboardTeamsCardData(); // const { teamsCardData } = useDashboardTeamsCardData();
// const { tasksCardData } = useDashboardTasksCardData(); // const { tasksCardData } = useDashboardTasksCardData();
// Get the selected project ID from Redux store // Get the selected project ID from Redux store
useEffect(() => {
if (!projectLoading && projectNames.length === 1 && !selectedProject) {
dispatch(setProjectId(projectNames[0].id));
}
}, [projectNames, projectLoading, selectedProject, dispatch]);
console.log(projectNames)
// Show attendance if project selected or single project exists
const shouldShowAttendance =
!projectLoading && (selectedProject || projectNames.length === 1);
return ( return (
<div className="container-fluid mt-5"> <div className="container-fluid mt-5">
<div className="row gy-4"> <div className="row gy-4">
{/* {shouldShowAttendance && ( */} {/* {shouldShowAttendance && ( */}
<div className="col-xxl-6 col-lg-6"> <div className="col-xxl-6 col-lg-6">
<AttendanceOverview projectId={selectedProject} /> <AttendanceOverview />
</div> </div>
</div> </div>
</div> </div>

View File

@ -155,32 +155,32 @@ export const useDashboard_Data = ({ days, FromDate, projectId }) => {
// }; // };
export const useAttendanceOverviewData = (projectId, days) => { // export const useAttendanceOverviewData = (projectId, days) => {
const [attendanceOverviewData, setAttendanceOverviewData] = useState([]); // const [attendanceOverviewData, setAttendanceOverviewData] = useState([]);
const [loading, setLoading] = useState(false); // const [loading, setLoading] = useState(false);
const [error, setError] = useState(""); // const [error, setError] = useState("");
useEffect(() => { // useEffect(() => {
if (!projectId || !days) return; // if (!projectId || !days) return;
const fetchAttendanceOverview = async () => { // const fetchAttendanceOverview = async () => {
setLoading(true); // setLoading(true);
setError(""); // setError("");
try { // try {
const response = await GlobalRepository.getAttendanceOverview(projectId, days); // const response = await GlobalRepository.getAttendanceOverview(projectId, days);
setAttendanceOverviewData(response.data); // setAttendanceOverviewData(response.data);
} catch (err) { // } catch (err) {
setError("Failed to fetch attendance overview data."); // setError("Failed to fetch attendance overview data.");
} finally { // } finally {
setLoading(false); // setLoading(false);
} // }
}; // };
fetchAttendanceOverview(); // fetchAttendanceOverview();
}, [projectId, days]); // }, [projectId, days]);
return { attendanceOverviewData, loading, error }; // return { attendanceOverviewData, loading, error };
}; // };
// -------------------Query---------------------------- // -------------------Query----------------------------
@ -232,16 +232,17 @@ export const useDashboardTasksCardData = (projectId) => {
} }
}) })
} }
// export const useAttendanceOverviewData = (projectId, days) => { export const useAttendanceOverviewData = (projectId, days) => {
// return useQuery({ return useQuery({
// queryKey:["dashboardAttendanceOverView",projectId], queryKey:["dashboardAttendanceOverView",projectId,days],
// queryFn:async()=> { queryFn:async()=> {
// const resp = await GlobalRepository.getAttendanceOverview(projectId, days); const resp = await GlobalRepository.getAttendanceOverview(projectId, days);
// return resp.data; return resp.data;
// } },
// }) enabled:!!projectId
// } })
}
export const useDashboardProjectsCardData = () => { export const useDashboardProjectsCardData = () => {
return useQuery({ return useQuery({