added expense graph
This commit is contained in:
parent
3559b0a01c
commit
e26c87fd3d
@ -17,28 +17,30 @@ const formatDate = (dateStr) => {
|
||||
const AttendanceOverview = () => {
|
||||
const [dayRange, setDayRange] = useState(7);
|
||||
const [view, setView] = useState("chart");
|
||||
const selectedProject = useSelectedProject()
|
||||
const selectedProject = useSelectedProject();
|
||||
|
||||
const { data: attendanceOverviewData, isLoading, isError, error } = useAttendanceOverviewData(
|
||||
selectedProject,
|
||||
dayRange
|
||||
);
|
||||
|
||||
// Use empty array while loading
|
||||
const attendanceData = attendanceOverviewData || [];
|
||||
|
||||
const { tableData, roles, dates } = useMemo(() => {
|
||||
if (!attendanceOverviewData || attendanceOverviewData.length === 0) {
|
||||
if (!attendanceData || attendanceData.length === 0) {
|
||||
return { tableData: [], roles: [], dates: [] };
|
||||
}
|
||||
|
||||
const map = new Map();
|
||||
|
||||
attendanceOverviewData.forEach((entry) => {
|
||||
attendanceData.forEach((entry) => {
|
||||
const date = formatDate(entry.date);
|
||||
if (!map.has(date)) map.set(date, {});
|
||||
map.get(date)[entry.role.trim()] = entry.present;
|
||||
});
|
||||
|
||||
const uniqueRoles = [
|
||||
...new Set(attendanceOverviewData.map((e) => e.role.trim())),
|
||||
];
|
||||
const uniqueRoles = [...new Set(attendanceData.map((e) => e.role.trim()))];
|
||||
const sortedDates = [...map.keys()];
|
||||
|
||||
const tableData = sortedDates.map((date) => {
|
||||
@ -50,7 +52,7 @@ const AttendanceOverview = () => {
|
||||
});
|
||||
|
||||
return { tableData, roles: uniqueRoles, dates: sortedDates };
|
||||
}, [attendanceOverviewData,isLoading,selectedProject,dayRange]);
|
||||
}, [attendanceData]);
|
||||
|
||||
const chartSeries = roles.map((role) => ({
|
||||
name: role,
|
||||
@ -58,29 +60,24 @@ const AttendanceOverview = () => {
|
||||
}));
|
||||
|
||||
const chartOptions = {
|
||||
chart: {
|
||||
type: "bar",
|
||||
stacked: true,
|
||||
height: 400,
|
||||
toolbar: { show: false },
|
||||
},
|
||||
chart: { type: "bar", stacked: true, height: 400, toolbar: { show: false } },
|
||||
plotOptions: { bar: { borderRadius: 2, columnWidth: "60%" } },
|
||||
xaxis: { categories: tableData.map((row) => row.date) },
|
||||
yaxis: {
|
||||
show: true,
|
||||
axisBorder: { show: true, color: "#78909C" },
|
||||
axisTicks: { show: true, color: "#78909C", width: 6 },
|
||||
},
|
||||
yaxis: { show: true, axisBorder: { show: true, color: "#78909C" }, axisTicks: { show: true, color: "#78909C", width: 6 } },
|
||||
legend: { position: "bottom" },
|
||||
fill: { opacity: 1 },
|
||||
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 (
|
||||
<div className="bg-white p-4 rounded shadow d-flex flex-column">
|
||||
<div className="bg-white p-4 rounded shadow d-flex flex-column position-relative">
|
||||
{/* Optional subtle loading overlay */}
|
||||
{isLoading && (
|
||||
<div className="position-absolute w-100 h-100 d-flex align-items-center justify-content-center bg-white bg-opacity-50 z-index-1">
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Header */}
|
||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||
<div className="card-title mb-0 text-start">
|
||||
@ -88,27 +85,15 @@ const AttendanceOverview = () => {
|
||||
<p className="card-subtitle">Role-wise present count</p>
|
||||
</div>
|
||||
<div className="d-flex gap-2">
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
value={dayRange}
|
||||
onChange={(e) => setDayRange(Number(e.target.value))}
|
||||
>
|
||||
<select className="form-select form-select-sm" value={dayRange} onChange={(e) => setDayRange(Number(e.target.value))}>
|
||||
<option value={7}>Last 7 Days</option>
|
||||
<option value={15}>Last 15 Days</option>
|
||||
<option value={30}>Last 30 Days</option>
|
||||
</select>
|
||||
<button
|
||||
className={`btn btn-sm p-1 ${view === "chart" ? "btn-primary" : "btn-outline-primary"}`}
|
||||
onClick={() => setView("chart")}
|
||||
title="Chart View"
|
||||
>
|
||||
<button className={`btn btn-sm p-1 ${view === "chart" ? "btn-primary" : "btn-outline-primary"}`} onClick={() => setView("chart")} title="Chart View">
|
||||
<i className="bx bx-bar-chart-alt-2"></i>
|
||||
</button>
|
||||
<button
|
||||
className={`btn btn-sm p-1 ${view === "table" ? "btn-primary" : "btn-outline-primary"}`}
|
||||
onClick={() => setView("table")}
|
||||
title="Table View"
|
||||
>
|
||||
<button className={`btn btn-sm p-1 ${view === "table" ? "btn-primary" : "btn-outline-primary"}`} onClick={() => setView("table")} title="Table View">
|
||||
<i className="bx bx-list-ul fs-5"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -127,9 +112,7 @@ const AttendanceOverview = () => {
|
||||
<tr>
|
||||
<th style={{ background: "#f8f9fa", textTransform: "none" }}>Role</th>
|
||||
{dates.map((date, idx) => (
|
||||
<th key={idx} style={{ background: "#f8f9fa", textTransform: "none" }}>
|
||||
{date}
|
||||
</th>
|
||||
<th key={idx} style={{ background: "#f8f9fa", textTransform: "none" }}>{date}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
@ -152,4 +135,4 @@ const AttendanceOverview = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AttendanceOverview;
|
||||
export default AttendanceOverview;
|
||||
|
@ -14,34 +14,32 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
// import ProjectProgressChart from "./ProjectProgressChart";
|
||||
// import ProjectOverview from "../Project/ProjectOverview";
|
||||
import AttendanceOverview from "./AttendanceChart";
|
||||
import ExpenseChartDesign2 from "./ExpenseChartDesign2";
|
||||
import ExpenseChartDesign from "./ExpenseChartDesign";
|
||||
|
||||
const Dashboard = () => {
|
||||
|
||||
// const { projectsCardData } = useDashboardProjectsCardData();
|
||||
// const { teamsCardData } = useDashboardTeamsCardData();
|
||||
// const { tasksCardData } = useDashboardTasksCardData();
|
||||
|
||||
// Get the selected project ID from Redux store
|
||||
const projectId = useSelector((store) => store.localVariables.projectId);
|
||||
const isAllProjectsSelected = projectId === null;
|
||||
// Get the selected project ID from Redux store
|
||||
const projectId = useSelector((store) => store.localVariables.projectId);
|
||||
const isAllProjectsSelected = projectId === null;
|
||||
|
||||
return (
|
||||
<div className="container-fluid mt-5">
|
||||
<div className="row gy-4">
|
||||
|
||||
<div className="col-xxl-6 col-lg-6">
|
||||
<ExpenseChartDesign2 />
|
||||
</div>
|
||||
|
||||
{!isAllProjectsSelected && (
|
||||
<div className="col-xxl-6 col-lg-6">
|
||||
<AttendanceOverview /> {/* Removed unnecessary projectId prop */}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
return (
|
||||
<div className="container-fluid mt-5">
|
||||
<div className="row gy-4">
|
||||
<div className="col-xxl-6 col-lg-6">
|
||||
<ExpenseChartDesign />
|
||||
</div>
|
||||
);
|
||||
|
||||
{!isAllProjectsSelected && (
|
||||
<div className="col-xxl-6 col-lg-6">
|
||||
<AttendanceOverview /> {/* Removed unnecessary projectId prop */}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
|
142
src/components/Dashboard/ExpenseChartDesign.jsx
Normal file
142
src/components/Dashboard/ExpenseChartDesign.jsx
Normal file
@ -0,0 +1,142 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Chart from "react-apexcharts";
|
||||
import { useDashboard_ExpenseData } from "../../hooks/useDashboard_Data";
|
||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
import { DateRangePicker1 } from "../common/DateRangePicker";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
import { localToUtc } from "../../utils/appUtils";
|
||||
|
||||
const ExpenseChartDesign = () => {
|
||||
const projectId = useSelectedProject();
|
||||
|
||||
const methods = useForm({
|
||||
defaultValues: {
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
},
|
||||
});
|
||||
|
||||
const { watch } = methods;
|
||||
|
||||
const [startDate, endDate] = watch(["startDate", "endDate"]);
|
||||
console.log(startDate,endDate)
|
||||
const { data, isLoading, isError, error, isFetching } = useDashboard_ExpenseData(
|
||||
projectId,
|
||||
localToUtc(startDate),
|
||||
localToUtc(endDate)
|
||||
);
|
||||
|
||||
if (isError) return <div>{error.message}</div>;
|
||||
|
||||
const report = data?.report || [];
|
||||
|
||||
const labels = report.map((item) => item.projectName);
|
||||
const series = report.map((item) => item.totalApprovedAmount || 0);
|
||||
const total = data?.totalAmount || 0;
|
||||
|
||||
const donutOptions = {
|
||||
chart: { type: "donut" },
|
||||
labels,
|
||||
legend: { show: false },
|
||||
dataLabels: { enabled: true, formatter: (val) => `${val.toFixed(0)}%` },
|
||||
colors: ["#7367F0", "#28C76F", "#FF9F43", "#EA5455", "#00CFE8", "#FF78B8"],
|
||||
plotOptions: {
|
||||
pie: {
|
||||
donut: {
|
||||
size: "70%",
|
||||
labels: {
|
||||
show: true,
|
||||
total: {
|
||||
show: true,
|
||||
label: "Total",
|
||||
fontSize: "16px",
|
||||
formatter: () => `${total}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
if (data?.report === 0) {
|
||||
return <div>No data found</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="card shadow-sm" style={{ minHeight: "500px" }}>
|
||||
<div className="card-header d-flex justify-content-between align-items-center mt-3">
|
||||
<div>
|
||||
<h5 className="mb-1 fw-bold">Expense Breakdown</h5>
|
||||
<p className="card-subtitle me-3">Detailed project expenses</p>
|
||||
</div>
|
||||
|
||||
<div className="text-end">
|
||||
<FormProvider {...methods}>
|
||||
<DateRangePicker1 />
|
||||
</FormProvider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-body position-relative">
|
||||
{/* Initial loading: show full loader */}
|
||||
{isLoading && (
|
||||
<div className="d-flex justify-content-center align-items-center" style={{ height: "200px" }}>
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Data display */}
|
||||
{!isLoading && report.length === 0 && (
|
||||
<div className="text-center text-muted py-5">No data found</div>
|
||||
)}
|
||||
|
||||
{!isLoading && report.length > 0 && (
|
||||
<>
|
||||
{/* Overlay spinner for refetch */}
|
||||
{isFetching && (
|
||||
<div className="position-absolute top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center bg-white bg-opacity-75">
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="d-flex justify-content-center mb-8">
|
||||
<Chart
|
||||
options={donutOptions}
|
||||
series={report.map((item) => item.totalApprovedAmount || 0)}
|
||||
type="donut"
|
||||
width="320"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-2 w-100">
|
||||
<div className="row">
|
||||
{report.map((item, idx) => (
|
||||
<div className="col-6 d-flex justify-content-start align-items-start mb-2" key={idx}>
|
||||
<div className="avatar me-2">
|
||||
<span
|
||||
className="avatar-initial rounded-2"
|
||||
style={{
|
||||
backgroundColor: donutOptions.colors[idx % donutOptions.colors.length],
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-receipt fs-4"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div className="d-flex flex-column gap-1 text-start">
|
||||
<small className="fw-bold fs-6">{item.projectName}</small>
|
||||
<span className="fw-bold text-muted ms-1">{item.totalApprovedAmount}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default ExpenseChartDesign;
|
@ -1,119 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import Chart from "react-apexcharts";
|
||||
import DateRangePicker from "../common/DateRangePicker";
|
||||
import { useDashboard_ExpenseData } from "../../hooks/useDashboard_Data";
|
||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
|
||||
const ExpenseChartDesign2 = () => {
|
||||
const projectId = useSelectedProject()
|
||||
const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" });
|
||||
|
||||
const { data, isLoading, isError, error } = useDashboard_ExpenseData(
|
||||
projectId,
|
||||
dateRange.startDate,
|
||||
dateRange.endDate
|
||||
);
|
||||
|
||||
if (isLoading) return <div>Loading....</div>
|
||||
if (isError) return <div>{error.message}</div>;
|
||||
|
||||
const report = data?.report || [];
|
||||
|
||||
// Map the API data to chart labels and series
|
||||
const labels = report.map((item) => item.projectName);
|
||||
const series = report.map((item) => item.totalApprovedAmount || 0);
|
||||
const total = data?.totalAmount || 0;
|
||||
|
||||
const donutOptions = {
|
||||
chart: { type: "donut" },
|
||||
labels,
|
||||
legend: { show: false },
|
||||
dataLabels: { enabled: true, formatter: (val) => `${val.toFixed(0)}%` },
|
||||
colors: ["#7367F0", "#28C76F", "#FF9F43", "#EA5455", "#00CFE8", "#FF78B8"],
|
||||
plotOptions: {
|
||||
pie: {
|
||||
donut: {
|
||||
size: "70%",
|
||||
labels: {
|
||||
show: true,
|
||||
total: {
|
||||
show: true,
|
||||
label: "Total",
|
||||
fontSize: "16px",
|
||||
formatter: () => `${total}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (data?.report === 0) {
|
||||
return <div>
|
||||
No data found
|
||||
</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="card shadow-sm" style={{ minHeight: "500px" }}>
|
||||
<div className="card-header d-flex justify-content-between align-items-center mt-3">
|
||||
<div>
|
||||
<h5 className="mb-1 fw-bold">Expense Breakdown</h5>
|
||||
<p className="card-subtitle me-3">Detailed project expenses</p>
|
||||
</div>
|
||||
<div className="text-end">
|
||||
<DateRangePicker
|
||||
DateDifference="7"
|
||||
onRangeChange={setDateRange}
|
||||
endDateMode="today"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-body">
|
||||
{report.length === 0 ? (
|
||||
<div className="text-center text-muted py-5">
|
||||
No data found
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="d-flex justify-content-center mb-8">
|
||||
<Chart
|
||||
options={donutOptions}
|
||||
series={report.map((item) => item.totalApprovedAmount || 0)}
|
||||
type="donut"
|
||||
width="320"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-2 w-100">
|
||||
<div className="row">
|
||||
{report.map((item, idx) => (
|
||||
<div
|
||||
className="col-6 d-flex justify-content-start align-items-start mb-2"
|
||||
key={idx}
|
||||
>
|
||||
<div className="avatar me-2">
|
||||
<span
|
||||
className="avatar-initial rounded-2"
|
||||
style={{ backgroundColor: donutOptions.colors[idx % donutOptions.colors.length] }}
|
||||
>
|
||||
<i className="bx bx-receipt fs-4"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div className="d-flex flex-column gap-1 text-start">
|
||||
<small className="fw-bold fs-6">{item.projectName}</small>
|
||||
<span className="fw-bold text-muted ms-1">{item.totalApprovedAmount}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExpenseChartDesign2;
|
@ -67,7 +67,7 @@ const Header = () => {
|
||||
if (projectLoading) return "Loading...";
|
||||
if (!projectNames?.length) return "No Projects Assigned";
|
||||
if (projectNames.length === 1) return projectNames[0].name;
|
||||
|
||||
if (selectedProject === null) return "All Projects";
|
||||
const selectedObj = projectNames.find((p) => p.id === selectedProject);
|
||||
return selectedObj
|
||||
? selectedObj.name
|
||||
@ -199,6 +199,14 @@ const Header = () => {
|
||||
className="dropdown-menu"
|
||||
style={{ overflow: "auto", maxHeight: "300px" }}
|
||||
>
|
||||
|
||||
<li>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
onClick={() => handleProjectChange(null)}
|
||||
>All Project</button>
|
||||
</li>
|
||||
|
||||
{[...projectsForDropdown]
|
||||
.sort((a, b) => a?.name?.localeCompare(b.name))
|
||||
.map((project) => (
|
||||
|
@ -14,7 +14,6 @@ const DateRangePicker = ({
|
||||
if (endDateMode === "yesterday") {
|
||||
endDate.setDate(endDate.getDate() - 1);
|
||||
}
|
||||
|
||||
endDate.setHours(0, 0, 0, 0);
|
||||
|
||||
const startDate = new Date(endDate);
|
||||
@ -30,9 +29,14 @@ const DateRangePicker = ({
|
||||
static: true,
|
||||
clickOpens: true,
|
||||
maxDate: endDate,
|
||||
onChange: (selectedDates, dateStr) => {
|
||||
const [startDateString, endDateString] = dateStr.split(" to ");
|
||||
onRangeChange?.({ startDate: startDateString, endDate: endDateString });
|
||||
onChange: (selectedDates) => {
|
||||
if (selectedDates.length === 2) {
|
||||
const [start, end] = selectedDates;
|
||||
onRangeChange?.({
|
||||
startDate: start.toLocaleDateString("en-CA"),
|
||||
endDate: end.toLocaleDateString("en-CA"),
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -47,9 +51,7 @@ const DateRangePicker = ({
|
||||
}, [onRangeChange, DateDifference, endDateMode]);
|
||||
|
||||
const handleIconClick = () => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current._flatpickr.open(); // directly opens flatpickr
|
||||
}
|
||||
inputRef.current?._flatpickr?.open();
|
||||
};
|
||||
|
||||
return (
|
||||
@ -61,9 +63,9 @@ const DateRangePicker = ({
|
||||
id="flatpickr-range"
|
||||
ref={inputRef}
|
||||
/>
|
||||
|
||||
<i
|
||||
className="bx bx-calendar calendar-icon cursor-pointer position-relative top-50 translate-middle-y " onClick={handleIconClick}
|
||||
className="bx bx-calendar calendar-icon cursor-pointer position-relative top-50 translate-middle-y"
|
||||
onClick={handleIconClick}
|
||||
style={{ right: "22px", bottom: "-8px" }}
|
||||
></i>
|
||||
</div>
|
||||
@ -72,10 +74,6 @@ const DateRangePicker = ({
|
||||
|
||||
export default DateRangePicker;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const DateRangePicker1 = ({
|
||||
startField = "startDate",
|
||||
endField = "endDate",
|
||||
@ -85,6 +83,8 @@ export const DateRangePicker1 = ({
|
||||
resetSignal,
|
||||
defaultRange = true,
|
||||
maxDate = null,
|
||||
sm,
|
||||
md,
|
||||
...rest
|
||||
}) => {
|
||||
const inputRef = useRef(null);
|
||||
@ -118,7 +118,7 @@ export const DateRangePicker1 = ({
|
||||
mode: "range",
|
||||
dateFormat: "d-m-Y",
|
||||
allowInput: allowText,
|
||||
maxDate ,
|
||||
maxDate,
|
||||
onChange: (selectedDates) => {
|
||||
if (selectedDates.length === 2) {
|
||||
const [start, end] = selectedDates;
|
||||
@ -148,31 +148,31 @@ export const DateRangePicker1 = ({
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (resetSignal !== undefined) {
|
||||
if (defaultRange) {
|
||||
applyDefaultDates();
|
||||
} else {
|
||||
setValue(startField, "", { shouldValidate: true });
|
||||
setValue(endField, "", { shouldValidate: true });
|
||||
if (resetSignal !== undefined) {
|
||||
if (defaultRange) {
|
||||
applyDefaultDates();
|
||||
} else {
|
||||
setValue(startField, "", { shouldValidate: true });
|
||||
setValue(endField, "", { shouldValidate: true });
|
||||
|
||||
if (inputRef.current?._flatpickr) {
|
||||
inputRef.current._flatpickr.clear();
|
||||
if (inputRef.current?._flatpickr) {
|
||||
inputRef.current._flatpickr.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [resetSignal, defaultRange, setValue, startField, endField]);
|
||||
|
||||
}, [resetSignal, defaultRange, setValue, startField, endField]);
|
||||
|
||||
const start = getValues(startField);
|
||||
const end = getValues(endField);
|
||||
const formattedValue = start && end ? `${start} To ${end}` : "";
|
||||
|
||||
return (
|
||||
<div className={`position-relative ${className}`}>
|
||||
<div className={`col-${sm} col-sm-${md} px-1 position-relative`}>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
placeholder={placeholder}
|
||||
className="form-control form-control-sm ps-2 pe-5 me-4 cursor-pointer"
|
||||
placeholder="From to End"
|
||||
id="flatpickr-range"
|
||||
defaultValue={formattedValue}
|
||||
ref={(el) => {
|
||||
inputRef.current = el;
|
||||
@ -181,13 +181,10 @@ export const DateRangePicker1 = ({
|
||||
readOnly={!allowText}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<span
|
||||
className="position-absolute top-50 end-0 pe-1 translate-middle-y cursor-pointer"
|
||||
<i
|
||||
className="bx bx-calendar calendar-icon cursor-pointer position-absolute top-50 end-0 translate-middle-y me-2"
|
||||
onClick={() => inputRef.current?._flatpickr?.open()}
|
||||
>
|
||||
<i className="bx bx-calendar bx-sm fs-5 text-muted"></i>
|
||||
</span>
|
||||
></i>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -212,12 +212,19 @@ export const useDashboard_AttendanceData = (date, projectId) => {
|
||||
}
|
||||
|
||||
export const useDashboard_ExpenseData = (projectId, startDate, endDate) => {
|
||||
const hasBothDates = !!startDate && !!endDate;
|
||||
const noDatesSelected = !startDate && !endDate;
|
||||
|
||||
const shouldFetch =
|
||||
noDatesSelected ||
|
||||
hasBothDates;
|
||||
return useQuery({
|
||||
queryKey: ["dashboardExpenses", projectId, startDate, endDate],
|
||||
queryFn: async () => {
|
||||
const resp = await GlobalRepository.getExpenseData(projectId, startDate, endDate);
|
||||
return resp.data; // this will return the "data" object from API response
|
||||
return resp.data;
|
||||
},
|
||||
enabled:shouldFetch
|
||||
});
|
||||
};
|
||||
|
||||
@ -242,16 +249,17 @@ export const useDashboardTasksCardData = (projectId) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
// export const useAttendanceOverviewData = (projectId, days) => {
|
||||
// return useQuery({
|
||||
// queryKey:["dashboardAttendanceOverView",projectId],
|
||||
// queryFn:async()=> {
|
||||
export const useAttendanceOverviewData = (projectId, days) => {
|
||||
return useQuery({
|
||||
queryKey: ["dashboardAttendanceOverView", projectId, days],
|
||||
queryFn: async () => {
|
||||
const resp = await GlobalRepository.getAttendanceOverview(projectId, days);
|
||||
return resp.data;
|
||||
},
|
||||
enabled: !!projectId,
|
||||
});
|
||||
};
|
||||
|
||||
// const resp = await GlobalRepository.getAttendanceOverview(projectId, days);
|
||||
// return resp.data;
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
export const useDashboardProjectsCardData = () => {
|
||||
return useQuery({
|
||||
|
@ -72,11 +72,11 @@ export const normalizeAllowedContentTypes = (allowedContentType) => {
|
||||
export function localToUtc(localDateString) {
|
||||
if (!localDateString || typeof localDateString !== "string") return null;
|
||||
|
||||
|
||||
const [year, month, day] = localDateString.trim().split("-");
|
||||
if (!year || !month || !day) return null;
|
||||
const [day, month, year] = localDateString.trim().split("-");
|
||||
if (!day || !month || !year) return null;
|
||||
|
||||
// Create date in UTC
|
||||
const date = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day), 0, 0, 0));
|
||||
|
||||
return isNaN(date.getTime()) ? null : date.toISOString();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user