added attandace poject wise overview
This commit is contained in:
parent
363a9c5feb
commit
cd1ae64753
@ -85,7 +85,7 @@ const Dashboard = () => {
|
||||
</div>
|
||||
{!isAllProjectsSelected &&
|
||||
(canRegularize || canTeamAttendance || canSelfAttendance) && (
|
||||
<div className="col-12 col-md-8 mb-sm-0 mb-4">
|
||||
<div className="col-12 col-md-6 mb-sm-0 mb-4">
|
||||
<AttendanceOverview />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -1,82 +1,83 @@
|
||||
import React from "react";
|
||||
import { useProjectName } from "../../hooks/useProjects";
|
||||
import { BUCKET_BG_CLASSES } from "../../utils/constants";
|
||||
import { useAttendaceProjectWiseOveriew } from "../../hooks/useDashboard_Data";
|
||||
import { AppColors, localToUtc } from "../../utils/appUtils";
|
||||
import DatePicker from "../common/DatePicker";
|
||||
import { useAppForm, useAppWatch } from "../../hooks/appHooks/useAppForm";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
const ProjectWiseTeamCount = () => {
|
||||
const { projectNames, loading, isError, Errorr } = useProjectName();
|
||||
const { control } = useAppForm({
|
||||
resolver: zodResolver(
|
||||
z.object({
|
||||
date: z.string().optional(),
|
||||
})
|
||||
),
|
||||
defaultValues: {
|
||||
date: new Date().toISOString().slice(0, 10),
|
||||
},
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="card h-100 p-3 d-flex align-items-center justify-content-center">
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const selectedDate = useAppWatch({ control, name: "date" });
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="card h-100 p-3 d-flex align-items-center justify-content-center">
|
||||
<span>Error: {Errorr?.message || "Something went wrong"}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const { data, isLoading, isFetching, isError, error } =
|
||||
useAttendaceProjectWiseOveriew(localToUtc(selectedDate));
|
||||
|
||||
const percent = (teamCount, attendanceCount) => {
|
||||
return teamCount > 0 ? Math.round((attendanceCount / teamCount) * 100) : 0;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card h-100 p-2">
|
||||
<div className="text-start">
|
||||
<h5 class="card-title m-0 me-2"> Project wise Employee</h5>
|
||||
<div className="card h-100 p-3">
|
||||
{/* Header */}
|
||||
<div className="d-flex justify-content-between text-start mb-2">
|
||||
<h5 className="card-title m-0 me-2">Project wise Employee</h5>
|
||||
<DatePicker name="date" control={control} maxDate={new Date()} />
|
||||
</div>
|
||||
|
||||
{/* Only show spinner for new data, not full component */}
|
||||
{isFetching && !isLoading && (
|
||||
<div className="small text-end text-muted">Updating…</div>
|
||||
)}
|
||||
|
||||
{/* Table */}
|
||||
<div className="table-container">
|
||||
<table className="table table-borderless mb-0">
|
||||
<thead className="table-header ">
|
||||
<thead className="table-header">
|
||||
<tr>
|
||||
<th style={{ width: 40 }}>No</th>
|
||||
<th style={{ width: 200 }}>Project</th>
|
||||
<th style={{ width: 150 }}>Visits</th>
|
||||
<th>Data In Percentage</th>
|
||||
<th style={{ width: 200 }} className="text-start">
|
||||
Project
|
||||
</th>
|
||||
<th style={{ width: 100 }}>Team Size</th>
|
||||
<th style={{ width: 80 }} className="text-start">
|
||||
Logged In
|
||||
</th>
|
||||
{/* <th>Percentage</th> */}
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
<div
|
||||
className="table-body-scroll overflow-auto"
|
||||
style={{ maxHeight: "65vh" }}
|
||||
className="table-body-scroll overflow-auto pe-1"
|
||||
style={{ maxHeight: "60vh" }}
|
||||
>
|
||||
<table className="table table-borderless mb-0">
|
||||
<tbody>
|
||||
{(projectNames ?? []).map((item, index) => (
|
||||
<tr key={item.id || index}>
|
||||
<td>{index + 1}</td>
|
||||
|
||||
<td>
|
||||
<div className="d-flex align-items-center my-2">
|
||||
{item.img && (
|
||||
<img
|
||||
src={item.img}
|
||||
alt={item.shortName}
|
||||
height="24"
|
||||
className="me-3"
|
||||
/>
|
||||
)}
|
||||
<span className="text-heading">{item.name}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>{item.visits}</td>
|
||||
|
||||
<td>
|
||||
<div className="d-flex justify-content-between align-items-center gap-3">
|
||||
{(data ?? []).map((item, index) => (
|
||||
<tr key={item.projectId || index}>
|
||||
<td style={{ width: 200 }}>
|
||||
<div
|
||||
className="progress flex-grow-1"
|
||||
style={{ height: 10 }}
|
||||
className="d-flex align-items-center text-wrap my-2 text-start"
|
||||
style={{ width: "180px" }}
|
||||
>
|
||||
<div
|
||||
className={`progress-bar ${item.color}`}
|
||||
style={{ width: `${item.percent}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<small>{item.percent}%</small>
|
||||
<span className="text-heading">{item.projectName}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-center" style={{ width: 80 }}>{item.teamCount}</td>
|
||||
<td className="text-center" style={{ width: 80 }} >{item.attendanceCount}</td>
|
||||
{/* <td>{percent(item.teamCount, item.attendanceCount)}%</td> */}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { useForm, Controller, FormProvider, useFormContext } from "react-hook-form";
|
||||
import { useForm, Controller, FormProvider, useFormContext, useWatch } from "react-hook-form";
|
||||
|
||||
export const useAppForm = (config) => useForm(config);
|
||||
export const AppFormProvider = FormProvider;
|
||||
export const AppFormController = Controller;
|
||||
export const useAppFormContext = useFormContext;
|
||||
export const useAppWatch = useWatch;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import GlobalRepository from "../repositories/GlobalRepository";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
|
||||
export const useDashboard_Data = ({ days, FromDate, projectId }) => {
|
||||
const [dashboard_data, setDashboard_Data] = useState([]);
|
||||
@ -212,3 +212,16 @@ export const useJobsProgression = (projectId) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useAttendaceProjectWiseOveriew = (date) => {
|
||||
return useQuery({
|
||||
queryKey: ["attendaceOverview", date],
|
||||
queryFn: async () => {
|
||||
const resp = await GlobalRepository.getAttendanceProjectWiseOverview(
|
||||
date
|
||||
);
|
||||
return resp.data;
|
||||
},
|
||||
keepPreviousData: true,
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { api } from "../utils/axiosClient";
|
||||
|
||||
const GlobalRepository = {
|
||||
getDashboardProgressionData: ({ days = '', FromDate = '', projectId = '' }) => {
|
||||
getDashboardProgressionData: ({
|
||||
days = "",
|
||||
FromDate = "",
|
||||
projectId = "",
|
||||
}) => {
|
||||
let params;
|
||||
if (projectId == null) {
|
||||
params = new URLSearchParams({
|
||||
@ -18,12 +22,13 @@ const GlobalRepository = {
|
||||
|
||||
return api.get(`/api/Dashboard/Progression?${params.toString()}`);
|
||||
},
|
||||
getProjectCompletionStatus:()=>api.get(`/api/Dashboard/project-completion-status`),
|
||||
|
||||
getProjectCompletionStatus: () =>
|
||||
api.get(`/api/Dashboard/project-completion-status`),
|
||||
|
||||
getDashboardAttendanceData: (date, projectId) => {
|
||||
|
||||
return api.get(`/api/Dashboard/project-attendance/${projectId}?date=${date}`);
|
||||
return api.get(
|
||||
`/api/Dashboard/project-attendance/${projectId}?date=${date}`
|
||||
);
|
||||
},
|
||||
getDashboardProjectsCardData: () => {
|
||||
return api.get(`/api/Dashboard/projects`);
|
||||
@ -44,7 +49,7 @@ const GlobalRepository = {
|
||||
},
|
||||
|
||||
getExpenseData: (projectId, startDate, endDate) => {
|
||||
let url = `api/Dashboard/expense/type`
|
||||
let url = `api/Dashboard/expense/type`;
|
||||
const queryParams = [];
|
||||
if (projectId) {
|
||||
queryParams.push(`projectId=${projectId}`);
|
||||
@ -62,10 +67,15 @@ const GlobalRepository = {
|
||||
return api.get(url);
|
||||
},
|
||||
|
||||
getExpenseStatus: (projectId) => api.get(`/api/Dashboard/expense/pendings${projectId ? `?projectId=${projectId}` : ""}`),
|
||||
getExpenseStatus: (projectId) =>
|
||||
api.get(
|
||||
`/api/Dashboard/expense/pendings${
|
||||
projectId ? `?projectId=${projectId}` : ""
|
||||
}`
|
||||
),
|
||||
|
||||
getExpenseDataByProject: (projectId, categoryId, months) => {
|
||||
let url = `api/Dashboard/expense/monthly`
|
||||
let url = `api/Dashboard/expense/monthly`;
|
||||
const queryParams = [];
|
||||
if (projectId) {
|
||||
queryParams.push(`projectId=${projectId}`);
|
||||
@ -82,13 +92,21 @@ const GlobalRepository = {
|
||||
return api.get(url);
|
||||
},
|
||||
|
||||
getAttendanceOverview: (projectId, days) => api.get(`/api/dashboard/attendance-overview/${projectId}?days=${days}`),
|
||||
getAttendanceOverview: (projectId, days) =>
|
||||
api.get(`/api/dashboard/attendance-overview/${projectId}?days=${days}`),
|
||||
|
||||
getCollectionOverview:(projectId) =>api.get(`/api/Dashboard/collection-overview`),
|
||||
getCollectionOverview: (projectId) =>
|
||||
api.get(`/api/Dashboard/collection-overview`),
|
||||
|
||||
getJobsProgression: (projectId) => api.get(`/api/Dashboard/job/progression${projectId ? `?projectId=${projectId}` : ""}`),
|
||||
getJobsProgression: (projectId) =>
|
||||
api.get(
|
||||
`/api/Dashboard/job/progression${
|
||||
projectId ? `?projectId=${projectId}` : ""
|
||||
}`
|
||||
),
|
||||
|
||||
getAttendanceProjectWiseOverview: (date) =>
|
||||
api.get(`/api/dashboard/project/attendance-overview?date=${date}`),
|
||||
};
|
||||
|
||||
|
||||
export default GlobalRepository;
|
||||
|
||||
@ -24,6 +24,11 @@ export const AppColorconfig = {
|
||||
borderColor: "#eceef1",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
export const AppColors = [
|
||||
"primary","secodary","warning","info","danger"
|
||||
]
|
||||
export const getColorNameFromHex = (hex) => {
|
||||
const normalizedHex = hex?.replace(/'/g, "").toLowerCase();
|
||||
const colors = AppColorconfig.colors;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user