fixed dashboard graph
This commit is contained in:
parent
49b597c833
commit
2531d91209
@ -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;
|
@ -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>
|
||||||
|
@ -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({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user