diff --git a/public/assets/css/core-extend.css b/public/assets/css/core-extend.css index e8854007..2f9fc387 100644 --- a/public/assets/css/core-extend.css +++ b/public/assets/css/core-extend.css @@ -126,4 +126,12 @@ .fs-md-large { font-size: 150% !important; } .fs-md-xlarge { font-size: 170% !important; } .fs-md-xxlarge { font-size: calc(1.725rem + 5.7vw) !important; } -} \ No newline at end of file +} + +/* Tables */ +.table th.actions-col, +.table td.actions-col { + width: 1%; + white-space: nowrap; + text-align: center; +} diff --git a/src/components/Activities/Attendance.jsx b/src/components/Activities/Attendance.jsx index 615627aa..22d437d7 100644 --- a/src/components/Activities/Attendance.jsx +++ b/src/components/Activities/Attendance.jsx @@ -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,12 +33,12 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat attendance, loading: attLoading, recall: attrecall, - isFetching + isFetching, } = useAttendance(selectedProject, organizationId, includeInactive, date); const filteredAttendance = ShowPending ? attendance?.filter( - (att) => att?.checkInTime !== null && att?.checkOutTime === null - ) + (att) => att?.checkInTime !== null && att?.checkOutTime === null + ) : attendance; const attendanceList = Array.isArray(filteredAttendance) @@ -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 ( <>
-
-
+
+
Date : {formatUTCToLocalTime(todayDate)}
{attLoading ? ( -
Loading...
+
+ +
) : currentItems?.length > 0 ? ( <> - +
- + {/* */} - + @@ -190,7 +200,7 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat - + {/* */} -
NameRoleRoleOrganization Check-In - Check-Out + + Check-Out ActionsActions
{item.jobRoleName}{item.jobRoleName}{item.organizationName || "--"} @@ -204,7 +214,7 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat : "--"} + )} @@ -253,4 +263,4 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat ); }; -export default Attendance; \ No newline at end of file +export default Attendance; diff --git a/src/components/Activities/AttendcesLogs.jsx b/src/components/Activities/AttendcesLogs.jsx index 20f9e647..5f785eaf 100644 --- a/src/components/Activities/AttendcesLogs.jsx +++ b/src/components/Activities/AttendcesLogs.jsx @@ -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,57 +89,65 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => { dateRange.endDate, organizationId ); - const filtering = useCallback((dataToFilter) => { - const filteredData = showPending - ? dataToFilter.filter((item) => item.checkOutTime === null) - : dataToFilter; + const filtering = useCallback( + (dataToFilter) => { + const filteredData = showPending + ? dataToFilter.filter((item) => item.checkOutTime === null) + : dataToFilter; - const group1 = filteredData - .filter((d) => d.activity === 1 && isSameDay(d.checkInTime)) - .sort(sortByName); - const group2 = filteredData - .filter((d) => d.activity === 4 && isSameDay(d.checkOutTime)) - .sort(sortByName); - const group3 = filteredData - .filter((d) => d.activity === 1 && isBeforeToday(d.checkInTime)) - .sort(sortByName); - const group4 = filteredData.filter( - (d) => d.activity === 4 && isBeforeToday(d.checkOutTime) - ); - const group5 = filteredData - .filter((d) => d.activity === 2 && isBeforeToday(d.checkOutTime)) - .sort(sortByName); - const group6 = filteredData - .filter((d) => d.activity === 5) - .sort(sortByName); + const group1 = filteredData + .filter((d) => d.activity === 1 && isSameDay(d.checkInTime)) + .sort(sortByName); + const group2 = filteredData + .filter((d) => d.activity === 4 && isSameDay(d.checkOutTime)) + .sort(sortByName); + const group3 = filteredData + .filter((d) => d.activity === 1 && isBeforeToday(d.checkInTime)) + .sort(sortByName); + const group4 = filteredData.filter( + (d) => d.activity === 4 && isBeforeToday(d.checkOutTime) + ); + const group5 = filteredData + .filter((d) => d.activity === 2 && isBeforeToday(d.checkOutTime)) + .sort(sortByName); + const group6 = filteredData + .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) => { - const date = (item.checkInTime || item.checkOutTime)?.split("T")[0]; - if (date) { - acc[date] = acc[date] || []; - acc[date].push(item); - } - return acc; - }, {}); + // Group by date + const groupedByDate = sortedList.reduce((acc, item) => { + const date = (item.checkInTime || item.checkOutTime)?.split("T")[0]; + if (date) { + acc[date] = acc[date] || []; + acc[date].push(item); + } + return acc; + }, {}); - const sortedDates = Object.keys(groupedByDate).sort( - (a, b) => new Date(b) - new Date(a) - ); + const sortedDates = Object.keys(groupedByDate).sort( + (a, b) => new Date(b) - new Date(a) + ); - const finalData = sortedDates.flatMap((date) => groupedByDate[date]); - setProcessedData(finalData); - }, [showPending]); - - - useEffect(() => { - if (data?.length) { - filtering(data); - } -}, [data, showPending]); + const finalData = sortedDates.flatMap((date) => groupedByDate[date]); + setProcessedData(finalData); + }, + [showPending] + ); + useEffect(() => { + if (data?.length) { + filtering(data); + } + }, [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,29 +229,29 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => { return () => eventBus.off("employee", employeeHandler); }, [employeeHandler]); - return ( <>
-
+
-
-
-
+
{isLoading ? ( -
-

Loading...

+
+
) : filteredSearchData?.length > 0 ? ( - +
*/} - + @@ -269,9 +278,9 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => { const previousAttendance = arr[index - 1]; const previousDate = previousAttendance ? moment( - previousAttendance.checkInTime || - previousAttendance.checkOutTime - ).format("YYYY-MM-DD") + previousAttendance.checkInTime || + previousAttendance.checkOutTime + ).format("YYYY-MM-DD") : null; if (!previousDate || currentDate !== previousDate) { @@ -281,8 +290,8 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => { className="table-row-header" > @@ -299,7 +308,9 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
@@ -253,12 +261,13 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => { {/* Organization - Check-In + {" "} + Check-In Check-Out ActionAction
- - {formatUTCToLocalTime(currentDate)} + + {formatUTCToLocalTime(currentDate)}
+ {
) : ( -
No data for this date range. Please choose another.
+
+

+ No data for this date range. Please choose another. +

+
)}
{paginatedAttendances?.length == 0 && filteredSearchData?.length > 0 && ( diff --git a/src/components/Activities/Regularization.jsx b/src/components/Activities/Regularization.jsx index fbf1cddd..169c4a45 100644 --- a/src/components/Activities/Regularization.jsx +++ b/src/components/Activities/Regularization.jsx @@ -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,
{loading ? ( -
-

Loading...

+
+
) : currentItems?.length > 0 ? ( - +
@@ -154,7 +158,7 @@ const Regularization = ({ handleRequest, searchTerm, projectId, organizationId, - + diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx index 36cc6252..c720c960 100644 --- a/src/components/Dashboard/Dashboard.jsx +++ b/src/components/Dashboard/Dashboard.jsx @@ -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,34 +36,40 @@ const Dashboard = () => { const projectId = useSelector((store) => store.localVariables.projectId); const isAllProjectsSelected = projectId === null; + const isViewExpense = useHasAnyPermission( + VIEW_ALL_EXPNESE, + APPROVE_EXPENSE, + EXPENSE_MANAGE + ); return ( -
-
-
-
- +
+ {isViewExpense && ( +
+
+
+ +
+
+ +
+
+ +
+
+
+ )} + +
+ {!isAllProjectsSelected && ( +
+ +
+ )} +
+ +
- -
-
- -
-
-
- -
- {!isAllProjectsSelected && ( -
- -
- )} -
- -
-
-
- ); }; diff --git a/src/components/common/Loader.jsx b/src/components/common/Loader.jsx index 07078edb..53c90486 100644 --- a/src/components/common/Loader.jsx +++ b/src/components/common/Loader.jsx @@ -19,3 +19,14 @@ const Loader = () => { export default Loader; + +export const SpinnerLoader = ()=>{ + return ( +
+
+ Loading... +
+

Loading attendance data...

+
+ ) +} \ No newline at end of file diff --git a/src/pages/Activities/AttendancePage.jsx b/src/pages/Activities/AttendancePage.jsx index 63721bbc..d4f5de2b 100644 --- a/src/pages/Activities/AttendancePage.jsx +++ b/src/pages/Activities/AttendancePage.jsx @@ -169,7 +169,7 @@ const AttendancePage = () => {
{/* Search + Organization filter */} -
+
{/* Organization Dropdown */} {/*
NameRequest By Requested AtActionAction