issues_Oct_4W #498
@ -127,3 +127,11 @@
|
||||
.fs-md-xlarge { font-size: 170% !important; }
|
||||
.fs-md-xxlarge { font-size: calc(1.725rem + 5.7vw) !important; }
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.table th.actions-col,
|
||||
.table td.actions-col {
|
||||
width: 1%;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ const AttendLogs = ({ Id }) => {
|
||||
return (
|
||||
<div className="table-responsive">
|
||||
<div className="mb-3">
|
||||
<h5 className="fw-bold mb-4">Attendance Logs</h5>
|
||||
<h5 className="mb-4">Attendance Logs</h5>
|
||||
{logs && !loading && (
|
||||
<p className="mb-0 text-start">
|
||||
Showing logs for{" "}
|
||||
|
||||
@ -12,8 +12,17 @@ import { useQueryClient } from "@tanstack/react-query";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
import Pagination from "../common/Pagination";
|
||||
import { SpinnerLoader } from "../common/Loader";
|
||||
|
||||
const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizationId, includeInactive, date }) => {
|
||||
const Attendance = ({
|
||||
getRole,
|
||||
handleModalData,
|
||||
searchTerm,
|
||||
projectId,
|
||||
organizationId,
|
||||
includeInactive,
|
||||
date,
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
@ -24,7 +33,7 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
|
||||
attendance,
|
||||
loading: attLoading,
|
||||
recall: attrecall,
|
||||
isFetching
|
||||
isFetching,
|
||||
} = useAttendance(selectedProject, organizationId, includeInactive, date);
|
||||
const filteredAttendance = ShowPending
|
||||
? attendance?.filter(
|
||||
@ -71,19 +80,19 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
|
||||
);
|
||||
|
||||
// Reset pagination when the filter or search term changes
|
||||
useEffect(() => {
|
||||
}, [finalFilteredData]);
|
||||
|
||||
useEffect(() => {}, [finalFilteredData]);
|
||||
|
||||
const handler = useCallback(
|
||||
(msg) => {
|
||||
if (selectedProject == msg.projectId) {
|
||||
queryClient.setQueryData(["attendance", selectedProject], (oldData) => {
|
||||
if (!oldData) {
|
||||
queryClient.invalidateQueries({ queryKey: ["attendance"] })
|
||||
};
|
||||
queryClient.invalidateQueries({ queryKey: ["attendance"] });
|
||||
}
|
||||
return oldData.map((record) =>
|
||||
record.employeeId === msg.response.employeeId ? { ...record, ...msg.response } : record
|
||||
record.employeeId === msg.response.employeeId
|
||||
? { ...record, ...msg.response }
|
||||
: record
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -109,16 +118,11 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
|
||||
return () => eventBus.off("employee", employeeHandler);
|
||||
}, [employeeHandler]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div
|
||||
className="table-responsive text-nowrap h-100"
|
||||
style={{ minHeight: "200px" }} // Ensures fixed height
|
||||
>
|
||||
<div className="d-flex text-start align-items-center py-2">
|
||||
<div className="table-responsive text-nowrap ">
|
||||
<div className="d-flex justify-content-between align-items-center py-2">
|
||||
<strong>Date : {formatUTCToLocalTime(todayDate)}</strong>
|
||||
<div className="form-check form-switch text-start m-0 ms-5">
|
||||
<input
|
||||
@ -134,23 +138,29 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
|
||||
</div>
|
||||
</div>
|
||||
{attLoading ? (
|
||||
<div>Loading...</div>
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ minHeight: "70vh" }}
|
||||
>
|
||||
<SpinnerLoader />
|
||||
</div>
|
||||
) : currentItems?.length > 0 ? (
|
||||
<>
|
||||
<table className="table ">
|
||||
<table className="table table-hover ">
|
||||
<thead>
|
||||
<tr className="border-top-1">
|
||||
<th colSpan={2}>Name</th>
|
||||
<th>Role</th>
|
||||
<th className="text-start actions-col text-center">Role</th>
|
||||
{/* <th>Organization</th> */}
|
||||
<th>
|
||||
<i className="bx bxs-down-arrow-alt text-success"></i>
|
||||
Check-In
|
||||
</th>
|
||||
<th>
|
||||
<i className="bx bxs-up-arrow-alt text-danger"></i>Check-Out
|
||||
<i className="bx bxs-up-arrow-alt text-danger"></i>
|
||||
Check-Out
|
||||
</th>
|
||||
<th>Actions</th>
|
||||
<th className="actions-col">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="table-border-bottom-0 ">
|
||||
@ -190,7 +200,7 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>{item.jobRoleName}</td>
|
||||
<td className="text-start action-col">{item.jobRoleName}</td>
|
||||
{/* <td>{item.organizationName || "--"}</td> */}
|
||||
|
||||
<td>
|
||||
@ -204,7 +214,7 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
|
||||
: "--"}
|
||||
</td>
|
||||
|
||||
<td className="text-center">
|
||||
<td className="text-center actions-col">
|
||||
<RenderAttendanceStatus
|
||||
attendanceData={item}
|
||||
handleModalData={handleModalData}
|
||||
|
||||
@ -5,7 +5,11 @@ import { convertShortTime, formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||
import RenderAttendanceStatus from "./RenderAttendanceStatus";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import DateRangePicker from "../common/DateRangePicker";
|
||||
import { clearCacheKey, getCachedData, useSelectedProject } from "../../slices/apiDataManager";
|
||||
import {
|
||||
clearCacheKey,
|
||||
getCachedData,
|
||||
useSelectedProject,
|
||||
} from "../../slices/apiDataManager";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import AttendanceRepository from "../../repositories/AttendanceRepository";
|
||||
import { useAttendancesLogs } from "../../hooks/useAttendance";
|
||||
@ -13,6 +17,7 @@ import { queryClient } from "../../layouts/AuthLayout";
|
||||
import { ITEMS_PER_PAGE } from "../../utils/constants";
|
||||
import Pagination from "../common/Pagination";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { SpinnerLoader } from "../common/Loader";
|
||||
|
||||
const usePagination = (data, itemsPerPage) => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
@ -36,14 +41,11 @@ const usePagination = (data, itemsPerPage) => {
|
||||
};
|
||||
|
||||
const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
// const selectedProject = useSelector(
|
||||
// (store) => store.localVariables.projectId
|
||||
// );
|
||||
const selectedProject = useSelectedProject();
|
||||
const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" });
|
||||
const dispatch = useDispatch();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [showPending, setShowPending] = useState(false)
|
||||
const [showPending, setShowPending] = useState(false);
|
||||
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [processedData, setProcessedData] = useState([]);
|
||||
@ -87,7 +89,8 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
dateRange.endDate,
|
||||
organizationId
|
||||
);
|
||||
const filtering = useCallback((dataToFilter) => {
|
||||
const filtering = useCallback(
|
||||
(dataToFilter) => {
|
||||
const filteredData = showPending
|
||||
? dataToFilter.filter((item) => item.checkOutTime === null)
|
||||
: dataToFilter;
|
||||
@ -111,7 +114,14 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
.filter((d) => d.activity === 5)
|
||||
.sort(sortByName);
|
||||
|
||||
const sortedList = [...group1, ...group2, ...group3, ...group4, ...group5, ...group6];
|
||||
const sortedList = [
|
||||
...group1,
|
||||
...group2,
|
||||
...group3,
|
||||
...group4,
|
||||
...group5,
|
||||
...group6,
|
||||
];
|
||||
|
||||
// Group by date
|
||||
const groupedByDate = sortedList.reduce((acc, item) => {
|
||||
@ -129,15 +139,15 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
|
||||
const finalData = sortedDates.flatMap((date) => groupedByDate[date]);
|
||||
setProcessedData(finalData);
|
||||
}, [showPending]);
|
||||
|
||||
},
|
||||
[showPending]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.length) {
|
||||
filtering(data);
|
||||
}
|
||||
}, [data, showPending]);
|
||||
|
||||
}, [data, showPending]);
|
||||
|
||||
// New useEffect to handle search filtering
|
||||
const filteredSearchData = useMemo(() => {
|
||||
@ -151,8 +161,6 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
});
|
||||
}, [processedData, searchTerm]);
|
||||
|
||||
|
||||
|
||||
const {
|
||||
currentPage,
|
||||
totalPages,
|
||||
@ -210,7 +218,7 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
// })
|
||||
// );
|
||||
|
||||
refetch()
|
||||
refetch();
|
||||
}
|
||||
},
|
||||
[selectedProject, dateRange, data, refetch]
|
||||
@ -221,11 +229,10 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
return () => eventBus.off("employee", employeeHandler);
|
||||
}, [employeeHandler]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="dataTables_length text-start py-2 d-flex justify-content-between"
|
||||
className="dataTables_length text-start py-2 d-flex justify-content-between "
|
||||
id="DataTables_Table_0_length"
|
||||
>
|
||||
<div className=" col-12">
|
||||
@ -233,17 +240,18 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
onRangeChange={setDateRange}
|
||||
defaultStartDate={yesterday}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className="table-responsive text-nowrap " >
|
||||
<div className="table-responsive text-nowrap ">
|
||||
{isLoading ? (
|
||||
<div className="d-flex justify-content-center align-items-center">
|
||||
<p className="text-secondary">Loading...</p>
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ minHeight: "70vh" }}
|
||||
>
|
||||
<SpinnerLoader/>
|
||||
</div>
|
||||
) : filteredSearchData?.length > 0 ? (
|
||||
<table className="table mb-0">
|
||||
<table className="table mb-0 table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border-top-1" colSpan={2}>
|
||||
@ -253,12 +261,13 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
{/* <th>Organization</th> */}
|
||||
|
||||
<th>
|
||||
<i className="bx bxs-down-arrow-alt text-success"></i> Check-In
|
||||
<i className="bx bxs-down-arrow-alt text-success"></i>{" "}
|
||||
Check-In
|
||||
</th>
|
||||
<th>
|
||||
<i className="bx bxs-up-arrow-alt text-danger"></i> Check-Out
|
||||
</th>
|
||||
<th>Action</th>
|
||||
<th className="actions-col">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -281,7 +290,7 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
className="table-row-header"
|
||||
>
|
||||
<td colSpan={8} className="text-start">
|
||||
<strong className="mx-2">
|
||||
<strong className="d-inline-block my-1 ms-2">
|
||||
{formatUTCToLocalTime(currentDate)}
|
||||
</strong>
|
||||
</td>
|
||||
@ -299,7 +308,9 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
<div className="d-flex flex-column">
|
||||
<a
|
||||
onClick={() =>
|
||||
navigate(`/employee/${attendance.employeeId}?for=attendance`)
|
||||
navigate(
|
||||
`/employee/${attendance.employeeId}?for=attendance`
|
||||
)
|
||||
}
|
||||
className="text-heading text-truncate cursor-pointer"
|
||||
>
|
||||
@ -322,7 +333,7 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
? convertShortTime(attendance.checkOutTime)
|
||||
: "--"}
|
||||
</td>
|
||||
<td className="text-center">
|
||||
<td className="text-center actions-col">
|
||||
<RenderAttendanceStatus
|
||||
attendanceData={attendance}
|
||||
handleModalData={handleModalData}
|
||||
@ -337,7 +348,14 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<div className="my-12"><span className="text-secondary">No data for this date range. Please choose another.</span></div>
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ minHeight: "70vh" }}
|
||||
>
|
||||
<p className="text-secondary mb-0">
|
||||
No data for this date range. Please choose another.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{paginatedAttendances?.length == 0 && filteredSearchData?.length > 0 && (
|
||||
|
||||
@ -12,6 +12,7 @@ import { useQueryClient } from "@tanstack/react-query";
|
||||
import Pagination from "../common/Pagination";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { employee } from "../../data/masters";
|
||||
import { SpinnerLoader } from "../common/Loader";
|
||||
|
||||
const Regularization = ({ handleRequest, searchTerm, projectId, organizationId, IncludeInActive }) => {
|
||||
const queryClient = useQueryClient();
|
||||
@ -135,11 +136,14 @@ const Regularization = ({ handleRequest, searchTerm, projectId, organizationId,
|
||||
<div>
|
||||
<div className="table-responsive text-nowrap pb-4" style={{ minHeight: "200px" }}>
|
||||
{loading ? (
|
||||
<div className="d-flex justify-content-center align-items-center" style={{ height: "200px" }}>
|
||||
<p className="text-secondary">Loading...</p>
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ minHeight: "70vh" }}
|
||||
>
|
||||
<SpinnerLoader/>
|
||||
</div>
|
||||
) : currentItems?.length > 0 ? (
|
||||
<table className="table mb-0">
|
||||
<table className="table mb-0 table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={2}>Name</th>
|
||||
@ -154,7 +158,7 @@ const Regularization = ({ handleRequest, searchTerm, projectId, organizationId,
|
||||
|
||||
<th>Request By</th>
|
||||
<th>Requested At</th>
|
||||
<th>Action</th>
|
||||
<th className="actions-col">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import React, { useState, useMemo } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import ReactApexChart from "react-apexcharts";
|
||||
import moment from "moment";
|
||||
import { useAttendanceOverviewData } from "../../hooks/useDashboard_Data";
|
||||
import flatColors from "../Charts/flatColor";
|
||||
import ChartSkeleton from "../Charts/Skelton";
|
||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
import { formatDate_DayMonth } from "../../utils/dateUtils";
|
||||
|
||||
const formatDate_DayMonth = (dateStr) => moment(dateStr).format("DD MMM YY");
|
||||
|
||||
const AttendanceOverview = () => {
|
||||
const [dayRange, setDayRange] = useState(7);
|
||||
@ -22,6 +24,7 @@ const AttendanceOverview = () => {
|
||||
// Use empty array while loading
|
||||
const attendanceData = attendanceOverviewData || [];
|
||||
|
||||
// Prepare data for chart and table
|
||||
const { tableData, roles, dates } = useMemo(() => {
|
||||
if (!attendanceData || attendanceData.length === 0) {
|
||||
return { tableData: [], roles: [], dates: [] };
|
||||
@ -49,32 +52,58 @@ const AttendanceOverview = () => {
|
||||
return { tableData, roles: uniqueRoles, dates: sortedDates };
|
||||
}, [attendanceData]);
|
||||
|
||||
// Chart data
|
||||
const chartSeries = roles.map((role) => ({
|
||||
name: role,
|
||||
data: tableData.map((row) => row[role]),
|
||||
}));
|
||||
|
||||
// Chart options
|
||||
const chartOptions = {
|
||||
chart: {
|
||||
type: "bar",
|
||||
stacked: true,
|
||||
stacked: true, // make false if you want side-by-side bars
|
||||
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 },
|
||||
plotOptions: {
|
||||
bar: {
|
||||
borderRadius: 4,
|
||||
columnWidth: "55%",
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
categories: tableData.map((row) => row.date),
|
||||
labels: {
|
||||
rotate: -45,
|
||||
style: { fontSize: "12px" },
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
axisBorder: { show: true, color: "#78909C" },
|
||||
axisTicks: { show: true, color: "#78909C" },
|
||||
},
|
||||
legend: {
|
||||
position: "bottom",
|
||||
horizontalAlign: "center",
|
||||
fontSize: "12px",
|
||||
},
|
||||
grid: {
|
||||
borderColor: "#e0e0e0",
|
||||
strokeDashArray: 3,
|
||||
},
|
||||
legend: { position: "bottom" },
|
||||
fill: { opacity: 1 },
|
||||
colors: roles.map((_, i) => flatColors[i % flatColors.length]),
|
||||
tooltip: {
|
||||
y: {
|
||||
formatter: (val) => `${val} present`,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white p-4 rounded shadow d-flex flex-column position-relative">
|
||||
{/* Header */}
|
||||
<div className="row mb-3 align-items-center">
|
||||
<div className="col-md-6 text-start">
|
||||
<p className="mb-1 fs-6 fs-md-5 fw-medium">Attendance Overview</p>
|
||||
@ -84,6 +113,7 @@ const AttendanceOverview = () => {
|
||||
</div>
|
||||
|
||||
<div className="col-md-6 d-flex flex-column align-items-end gap-2">
|
||||
{/* Day range dropdown */}
|
||||
<select
|
||||
className="form-select form-select-sm w-auto"
|
||||
value={dayRange}
|
||||
@ -94,10 +124,10 @@ const AttendanceOverview = () => {
|
||||
<option value={30}>Last 30 Days</option>
|
||||
</select>
|
||||
|
||||
{/* View toggle buttons */}
|
||||
<div className="d-flex gap-2 justify-content-end">
|
||||
<button
|
||||
className={`btn btn-sm p-1 ${
|
||||
view === "chart" ? "btn-primary" : "btn-outline-primary"
|
||||
className={`btn btn-sm p-1 ${view === "chart" ? "btn-primary" : "btn-outline-primary"
|
||||
}`}
|
||||
onClick={() => setView("chart")}
|
||||
title="Chart View"
|
||||
@ -106,8 +136,7 @@ const AttendanceOverview = () => {
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`btn btn-sm p-1 ${
|
||||
view === "table" ? "btn-primary" : "btn-outline-primary"
|
||||
className={`btn btn-sm p-1 ${view === "table" ? "btn-primary" : "btn-outline-primary"
|
||||
}`}
|
||||
onClick={() => setView("table")}
|
||||
title="Table View"
|
||||
@ -118,15 +147,13 @@ const AttendanceOverview = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content Section */}
|
||||
{/* Content */}
|
||||
<div className="flex-grow-1 d-flex align-items-center justify-content-center position-relative">
|
||||
{isLoading && (
|
||||
{isLoading ? (
|
||||
<div className="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center bg-white bg-opacity-50">
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoading && (!attendanceData || attendanceData.length === 0) ? (
|
||||
) : !attendanceData || attendanceData.length === 0 ? (
|
||||
<div
|
||||
className="text-muted fw-semibold d-flex align-items-center justify-content-center"
|
||||
style={{ minHeight: "250px" }}
|
||||
@ -139,7 +166,7 @@ const AttendanceOverview = () => {
|
||||
options={chartOptions}
|
||||
series={chartSeries}
|
||||
type="bar"
|
||||
height={300}
|
||||
height={350}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@ -156,7 +183,7 @@ const AttendanceOverview = () => {
|
||||
<th style={{ background: "#f8f9fa" }}>Role</th>
|
||||
{dates.map((date, idx) => (
|
||||
<th key={idx} style={{ background: "#f8f9fa" }}>
|
||||
{date}
|
||||
{moment(date, "DD MMM YY").format("DD MMM")}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
@ -165,15 +192,13 @@ const AttendanceOverview = () => {
|
||||
<tbody>
|
||||
{roles.map((role) => (
|
||||
<tr key={role}>
|
||||
<td className="fw-medium text-start table-cell">{role}</td>
|
||||
<td className="fw-medium text-start">{role}</td>
|
||||
{tableData.map((row, idx) => {
|
||||
const value = row[role];
|
||||
return (
|
||||
<td
|
||||
key={idx}
|
||||
style={
|
||||
value > 0 ? { backgroundColor: "#e9ecef" } : {}
|
||||
}
|
||||
style={value > 0 ? { backgroundColor: "#d5d5d5" } : {}}
|
||||
>
|
||||
{value}
|
||||
</td>
|
||||
|
||||
@ -19,6 +19,13 @@ import { useProjectName } from "../../hooks/useProjects";
|
||||
import ExpenseAnalysis from "./ExpenseAnalysis";
|
||||
import ExpenseStatus from "./ExpenseStatus";
|
||||
import ExpenseByProject from "./ExpenseByProject";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import {
|
||||
APPROVE_EXPENSE,
|
||||
EXPENSE_MANAGE,
|
||||
VIEW_ALL_EXPNESE,
|
||||
} from "../../utils/constants";
|
||||
import { useHasAnyPermission } from "../../hooks/useExpense";
|
||||
|
||||
const Dashboard = () => {
|
||||
// const { projectsCardData } = useDashboardProjectsCardData();
|
||||
@ -29,8 +36,14 @@ const Dashboard = () => {
|
||||
const projectId = useSelector((store) => store.localVariables.projectId);
|
||||
const isAllProjectsSelected = projectId === null;
|
||||
|
||||
const isViewExpense = useHasAnyPermission(
|
||||
VIEW_ALL_EXPNESE,
|
||||
APPROVE_EXPENSE,
|
||||
EXPENSE_MANAGE
|
||||
);
|
||||
return (
|
||||
<div className="container-fluid py-5">
|
||||
{isViewExpense && (
|
||||
<div className="row mb-6 g-6">
|
||||
<div className="col-12 col-xl-8">
|
||||
<div className="card h-100">
|
||||
@ -44,6 +57,7 @@ const Dashboard = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="row vh-100">
|
||||
{!isAllProjectsSelected && (
|
||||
@ -55,8 +69,7 @@ const Dashboard = () => {
|
||||
<ExpenseByProject />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -74,7 +74,7 @@ const EmployeeList = ({ employees, onChange, bucket }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="d-flex justify-content-between align-items-center mt-2">
|
||||
<div className="d-flex justify-content-between align-items-center mt-2 h-25" >
|
||||
<p className="m-0 fw-normal">Add Employee</p>
|
||||
<div className="px-1">
|
||||
<input
|
||||
@ -87,7 +87,7 @@ const EmployeeList = ({ employees, onChange, bucket }) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="table-responsive px-1 my-1 px-sm-0">
|
||||
<div className="table-responsive px-1 my-1 px-sm-0" style={{maxHeight:'200px'}}>
|
||||
<table className="table align-middle mb-0">
|
||||
<thead className="table-light">
|
||||
<tr>
|
||||
|
||||
@ -19,3 +19,14 @@ const Loader = () => {
|
||||
|
||||
export default Loader;
|
||||
|
||||
|
||||
export const SpinnerLoader = ()=>{
|
||||
return (
|
||||
<div className="text-center">
|
||||
<div className="spinner-border text-primary mb-3" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p className="text-secondary mb-0">Loading attendance data...</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -169,7 +169,7 @@ const AttendancePage = () => {
|
||||
</div>
|
||||
|
||||
{/* Search + Organization filter */}
|
||||
<div className="col-12 col-md-auto mt-2 mt-md-0 ms-md-auto d-flex gap-2 align-items-center">
|
||||
<div className="col-12 col-md-auto pb-2 mt-md-0 ms-md-auto d-flex gap-2 align-items-center nav-tabs">
|
||||
{/* Organization Dropdown */}
|
||||
{/* <select
|
||||
className="form-select form-select-sm"
|
||||
@ -206,7 +206,7 @@ const AttendancePage = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="tab-content attedanceTabs py-0 px-1 px-sm-3 pb-10">
|
||||
<div className="tab-content attedanceTabs py-0 px-1 px-sm-3 pb-10 page-min-h">
|
||||
|
||||
<>
|
||||
{activeTab === "all" && (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user