added attandace poject wise overview
This commit is contained in:
parent
363a9c5feb
commit
cd1ae64753
@ -85,7 +85,7 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
{!isAllProjectsSelected &&
|
{!isAllProjectsSelected &&
|
||||||
(canRegularize || canTeamAttendance || canSelfAttendance) && (
|
(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 />
|
<AttendanceOverview />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,82 +1,83 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useProjectName } from "../../hooks/useProjects";
|
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 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) {
|
const selectedDate = useAppWatch({ control, name: "date" });
|
||||||
return (
|
|
||||||
<div className="card h-100 p-3 d-flex align-items-center justify-content-center">
|
|
||||||
<span>Loading...</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isError) {
|
const { data, isLoading, isFetching, isError, error } =
|
||||||
return (
|
useAttendaceProjectWiseOveriew(localToUtc(selectedDate));
|
||||||
<div className="card h-100 p-3 d-flex align-items-center justify-content-center">
|
|
||||||
<span>Error: {Errorr?.message || "Something went wrong"}</span>
|
const percent = (teamCount, attendanceCount) => {
|
||||||
</div>
|
return teamCount > 0 ? Math.round((attendanceCount / teamCount) * 100) : 0;
|
||||||
);
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card h-100 p-2">
|
<div className="card h-100 p-3">
|
||||||
<div className="text-start">
|
{/* Header */}
|
||||||
<h5 class="card-title m-0 me-2"> Project wise Employee</h5>
|
<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>
|
</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">
|
<div className="table-container">
|
||||||
<table className="table table-borderless mb-0">
|
<table className="table table-borderless mb-0">
|
||||||
<thead className="table-header ">
|
<thead className="table-header">
|
||||||
<tr>
|
<tr>
|
||||||
<th style={{ width: 40 }}>No</th>
|
<th style={{ width: 200 }} className="text-start">
|
||||||
<th style={{ width: 200 }}>Project</th>
|
Project
|
||||||
<th style={{ width: 150 }}>Visits</th>
|
</th>
|
||||||
<th>Data In Percentage</th>
|
<th style={{ width: 100 }}>Team Size</th>
|
||||||
|
<th style={{ width: 80 }} className="text-start">
|
||||||
|
Logged In
|
||||||
|
</th>
|
||||||
|
{/* <th>Percentage</th> */}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="table-body-scroll overflow-auto"
|
className="table-body-scroll overflow-auto pe-1"
|
||||||
style={{ maxHeight: "65vh" }}
|
style={{ maxHeight: "60vh" }}
|
||||||
>
|
>
|
||||||
<table className="table table-borderless mb-0">
|
<table className="table table-borderless mb-0">
|
||||||
<tbody>
|
<tbody>
|
||||||
{(projectNames ?? []).map((item, index) => (
|
{(data ?? []).map((item, index) => (
|
||||||
<tr key={item.id || index}>
|
<tr key={item.projectId || index}>
|
||||||
<td>{index + 1}</td>
|
<td style={{ width: 200 }}>
|
||||||
|
|
||||||
<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">
|
|
||||||
<div
|
<div
|
||||||
className="progress flex-grow-1"
|
className="d-flex align-items-center text-wrap my-2 text-start"
|
||||||
style={{ height: 10 }}
|
style={{ width: "180px" }}
|
||||||
>
|
>
|
||||||
<div
|
<span className="text-heading">{item.projectName}</span>
|
||||||
className={`progress-bar ${item.color}`}
|
|
||||||
style={{ width: `${item.percent}%` }}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<small>{item.percent}%</small>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</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>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</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 useAppForm = (config) => useForm(config);
|
||||||
export const AppFormProvider = FormProvider;
|
export const AppFormProvider = FormProvider;
|
||||||
export const AppFormController = Controller;
|
export const AppFormController = Controller;
|
||||||
export const useAppFormContext = useFormContext;
|
export const useAppFormContext = useFormContext;
|
||||||
|
export const useAppWatch = useWatch;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import GlobalRepository from "../repositories/GlobalRepository";
|
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 }) => {
|
export const useDashboard_Data = ({ days, FromDate, projectId }) => {
|
||||||
const [dashboard_data, setDashboard_Data] = useState([]);
|
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";
|
import { api } from "../utils/axiosClient";
|
||||||
|
|
||||||
const GlobalRepository = {
|
const GlobalRepository = {
|
||||||
getDashboardProgressionData: ({ days = '', FromDate = '', projectId = '' }) => {
|
getDashboardProgressionData: ({
|
||||||
|
days = "",
|
||||||
|
FromDate = "",
|
||||||
|
projectId = "",
|
||||||
|
}) => {
|
||||||
let params;
|
let params;
|
||||||
if (projectId == null) {
|
if (projectId == null) {
|
||||||
params = new URLSearchParams({
|
params = new URLSearchParams({
|
||||||
@ -18,12 +22,13 @@ const GlobalRepository = {
|
|||||||
|
|
||||||
return api.get(`/api/Dashboard/Progression?${params.toString()}`);
|
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) => {
|
getDashboardAttendanceData: (date, projectId) => {
|
||||||
|
return api.get(
|
||||||
return api.get(`/api/Dashboard/project-attendance/${projectId}?date=${date}`);
|
`/api/Dashboard/project-attendance/${projectId}?date=${date}`
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getDashboardProjectsCardData: () => {
|
getDashboardProjectsCardData: () => {
|
||||||
return api.get(`/api/Dashboard/projects`);
|
return api.get(`/api/Dashboard/projects`);
|
||||||
@ -44,7 +49,7 @@ const GlobalRepository = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getExpenseData: (projectId, startDate, endDate) => {
|
getExpenseData: (projectId, startDate, endDate) => {
|
||||||
let url = `api/Dashboard/expense/type`
|
let url = `api/Dashboard/expense/type`;
|
||||||
const queryParams = [];
|
const queryParams = [];
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
queryParams.push(`projectId=${projectId}`);
|
queryParams.push(`projectId=${projectId}`);
|
||||||
@ -62,10 +67,15 @@ const GlobalRepository = {
|
|||||||
return api.get(url);
|
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) => {
|
getExpenseDataByProject: (projectId, categoryId, months) => {
|
||||||
let url = `api/Dashboard/expense/monthly`
|
let url = `api/Dashboard/expense/monthly`;
|
||||||
const queryParams = [];
|
const queryParams = [];
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
queryParams.push(`projectId=${projectId}`);
|
queryParams.push(`projectId=${projectId}`);
|
||||||
@ -82,13 +92,21 @@ const GlobalRepository = {
|
|||||||
return api.get(url);
|
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;
|
export default GlobalRepository;
|
||||||
|
|||||||
@ -24,6 +24,11 @@ export const AppColorconfig = {
|
|||||||
borderColor: "#eceef1",
|
borderColor: "#eceef1",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const AppColors = [
|
||||||
|
"primary","secodary","warning","info","danger"
|
||||||
|
]
|
||||||
export const getColorNameFromHex = (hex) => {
|
export const getColorNameFromHex = (hex) => {
|
||||||
const normalizedHex = hex?.replace(/'/g, "").toLowerCase();
|
const normalizedHex = hex?.replace(/'/g, "").toLowerCase();
|
||||||
const colors = AppColorconfig.colors;
|
const colors = AppColorconfig.colors;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user