diff --git a/public/assets/css/core-extend.css b/public/assets/css/core-extend.css index 68fd44df..2e9e51ef 100644 --- a/public/assets/css/core-extend.css +++ b/public/assets/css/core-extend.css @@ -9,4 +9,274 @@ } .table_header_border { border-bottom:2px solid var(--bs-table-border-color) ; -} \ No newline at end of file +} + + +.text-xxs { font-size: 0.55rem; } /* 8px */ +.text-xs { font-size: 0.75rem; } /* 12px */ +.text-sm { font-size: 0.875rem; } /* 14px */ +.text-base { font-size: 1rem; } /* 16px */ +.text-lg { font-size: 1.125rem; } /* 18px */ +.text-xl { font-size: 1.25rem; } /* 20px */ +.text-2xl { font-size: 1.5rem; } /* 24px */ +.text-3xl { font-size: 1.875rem; } /* 30px */ +.text-4xl { font-size: 2.25rem; } /* 36px */ +.text-5xl { font-size: 3rem; } /* 48px */ +.text-6xl { font-size: 3.75rem; } /* 60px */ +.text-7xl { font-size: 4.5rem; } /* 72px */ +.text-8xl { font-size: 6rem; } /* 96px */ +.text-9xl { font-size: 8rem; } /* 128px */ + + +/* */ + +.w-0 { width: 0px; } +.w-px { width: 1px; } +.w-1 { width: 0.25rem; } /* 4px */ +.w-2 { width: 0.5rem; } /* 8px */ +.w-3 { width: 0.75rem; } /* 12px */ +.w-4 { width: 1rem; } /* 16px */ +.w-5 { width: 1.25rem; } /* 20px */ +.w-6 { width: 1.5rem; } /* 24px */ +.w-8 { width: 2rem; } /* 32px */ +.w-10 { width: 2.5rem; } /* 40px */ +.w-12 { width: 3rem; } /* 48px */ +.w-16 { width: 4rem; } /* 64px */ +.w-20 { width: 5rem; } /* 80px */ +.w-24 { width: 6rem; } /* 96px */ +.w-32 { width: 8rem; } /* 128px */ +.w-40 { width: 10rem; } /* 160px */ +.w-48 { width: 12rem; } /* 192px */ +.w-56 { width: 14rem; } /* 224px */ +.w-64 { width: 16rem; } /* 256px */ +.w-auto { width: auto; } +.w-full { width: 100%; } +.w-screen{ width: 100vw; } +.w-min { width: min-content; } +.w-max { width: max-content; } + + + +.h-0 { height: 0px; } +.h-px { height: 1px; } +.h-1 { height: 0.25rem; } /* 4px */ +.h-2 { height: 0.5rem; } /* 8px */ +.h-3 { height: 0.75rem; } /* 12px */ +.h-4 { height: 1rem; } /* 16px */ +.h-5 { height: 1.25rem; } /* 20px */ +.h-6 { height: 1.5rem; } /* 24px */ +.h-8 { height: 2rem; } /* 32px */ +.h-10 { height: 2.5rem; } /* 40px */ +.h-12 { height: 3rem; } /* 48px */ +.h-16 { height: 4rem; } /* 64px */ +.h-20 { height: 5rem; } /* 80px */ +.h-24 { height: 6rem; } /* 96px */ +.h-32 { height: 8rem; } /* 128px */ +.h-40 { height: 10rem; } /* 160px */ +.h-48 { height: 12rem; } /* 192px */ +.h-56 { height: 14rem; } /* 224px */ +.h-64 { height: 16rem; } /* 256px */ +.h-auto { height: auto; } +.h-full { height: 100%; } +.h-screen{ height: 100vh; } +.h-min { height: min-content; } +.h-max { height: max-content; } + + + + + +/* ========================== + Base Font Sizes (mobile first) + ========================== */ +.text-xxs { font-size: 0.55rem; } /* 8px */ +.text-xs { font-size: 0.75rem; } /* 12px */ +.text-sm { font-size: 0.875rem; } /* 14px */ +.text-base{ font-size: 1rem; } /* 16px */ +.text-lg { font-size: 1.125rem; } /* 18px */ +.text-xl { font-size: 1.25rem; } /* 20px */ +.text-2xl { font-size: 1.5rem; } /* 24px */ +.text-3xl { font-size: 1.875rem; } /* 30px */ +.text-4xl { font-size: 2.25rem; } /* 36px */ +.text-5xl { font-size: 3rem; } /* 48px */ +.text-6xl { font-size: 3.75rem; } /* 60px */ +.text-7xl { font-size: 4.5rem; } /* 72px */ +.text-8xl { font-size: 6rem; } /* 96px */ +.text-9xl { font-size: 8rem; } /* 128px */ + +/* ========================== + Base Heights + ========================== */ +.h-0 { height: 0; } +.h-px { height: 1px; } +.h-1 { height: 0.25rem; } /* 4px */ +.h-2 { height: 0.5rem; } /* 8px */ +.h-3 { height: 0.75rem; } /* 12px */ +.h-4 { height: 1rem; } /* 16px */ +.h-5 { height: 1.25rem; } /* 20px */ +.h-6 { height: 1.5rem; } /* 24px */ +.h-8 { height: 2rem; } /* 32px */ +.h-10 { height: 2.5rem; } /* 40px */ +.h-12 { height: 3rem; } /* 48px */ +.h-16 { height: 4rem; } /* 64px */ +.h-20 { height: 5rem; } /* 80px */ +.h-24 { height: 6rem; } /* 96px */ +.h-32 { height: 8rem; } /* 128px */ +.h-40 { height: 10rem; } /* 160px */ +.h-48 { height: 12rem; } /* 192px */ +.h-56 { height: 14rem; } /* 224px */ +.h-64 { height: 16rem; } /* 256px */ +.h-full { height: 100%; } +.h-screen{ height: 100vh; } + +/* ========================== + Base Widths + ========================== */ +.w-0 { width: 0; } +.w-px { width: 1px; } +.w-1 { width: 0.25rem; } +.w-2 { width: 0.5rem; } +.w-3 { width: 0.75rem; } +.w-4 { width: 1rem; } +.w-5 { width: 1.25rem; } +.w-6 { width: 1.5rem; } +.w-8 { width: 2rem; } +.w-10 { width: 2.5rem; } +.w-12 { width: 3rem; } +.w-16 { width: 4rem; } +.w-20 { width: 5rem; } +.w-24 { width: 6rem; } +.w-32 { width: 8rem; } +.w-40 { width: 10rem; } +.w-48 { width: 12rem; } +.w-56 { width: 14rem; } +.w-64 { width: 16rem; } +.w-full { width: 100%; } +.w-screen{ width: 100vw; } + +/* ========================== + Responsive Variants + ========================== */ +@media (min-width: 576px) { /* sm */ + /* Font */ + .text-xxs-sm { font-size: 0.55rem; } + .text-xs-sm { font-size: 0.75rem; } + .text-sm-sm { font-size: 0.875rem; } + .text-base-sm{ font-size: 1rem; } + .text-lg-sm { font-size: 1.125rem; } + .text-xl-sm { font-size: 1.25rem; } + .text-2xl-sm{ font-size: 1.5rem; } + + /* Height */ + .h-1-sm{ height: 0.25rem; } + .h-2-sm{ height: 0.5rem; } + .h-3-sm{ height: 0.75rem; } + .h-4-sm{ height: 1rem; } + .h-5-sm{ height: 1.25rem; } + .h-6-sm{ height: 1.5rem; } + .h-8-sm{ height: 2rem; } + .h-10-sm{ height: 2.5rem; } + + /* Width */ + .w-1-sm{ width: 0.25rem; } + .w-2-sm{ width: 0.5rem; } + .w-3-sm{ width: 0.75rem; } + .w-4-sm{ width: 1rem; } + .w-5-sm{ width: 1.25rem; } + .w-6-sm{ width: 1.5rem; } + .w-8-sm{ width: 2rem; } + .w-10-sm{ width: 2.5rem; } +} + +@media (min-width: 768px) { /* md */ + /* Font */ + .text-xxs-md { font-size: 0.55rem; } + .text-xs-md { font-size: 0.75rem; } + .text-sm-md { font-size: 0.875rem; } + .text-base-md{ font-size: 1rem; } + .text-lg-md { font-size: 1.125rem; } + .text-xl-md { font-size: 1.25rem; } + .text-2xl-md{ font-size: 1.5rem; } + + /* Height */ + .h-1-md{ height: 0.25rem; } + .h-2-md{ height: 0.5rem; } + .h-3-md{ height: 0.75rem; } + .h-4-md{ height: 1rem; } + .h-5-md{ height: 1.25rem; } + .h-6-md{ height: 1.5rem; } + .h-8-md{ height: 2rem; } + .h-10-md{ height: 2.5rem; } + + /* Width */ + .w-1-md{ width: 0.25rem; } + .w-2-md{ width: 0.5rem; } + .w-3-md{ width: 0.75rem; } + .w-4-md{ width: 1rem; } + .w-5-md{ width: 1.25rem; } + .w-6-md{ width: 1.5rem; } + .w-8-md{ width: 2rem; } + .w-10-md{ width: 2.5rem; } +} + +@media (min-width: 992px) { /* lg */ + /* Font */ + .text-xxs-lg { font-size: 0.55rem; } + .text-xs-lg { font-size: 0.75rem; } + .text-sm-lg { font-size: 0.875rem; } + .text-base-lg{ font-size: 1rem; } + .text-lg-lg { font-size: 1.125rem; } + .text-xl-lg { font-size: 1.25rem; } + .text-2xl-lg{ font-size: 1.5rem; } + + /* Height */ + .h-1-lg{ height: 0.25rem; } + .h-2-lg{ height: 0.5rem; } + .h-3-lg{ height: 0.75rem; } + .h-4-lg{ height: 1rem; } + .h-5-lg{ height: 1.25rem; } + .h-6-lg{ height: 1.5rem; } + .h-8-lg{ height: 2rem; } + .h-10-lg{ height: 2.5rem; } + + /* Width */ + .w-1-lg{ width: 0.25rem; } + .w-2-lg{ width: 0.5rem; } + .w-3-lg{ width: 0.75rem; } + .w-4-lg{ width: 1rem; } + .w-5-lg{ width: 1.25rem; } + .w-6-lg{ width: 1.5rem; } + .w-8-lg{ width: 2rem; } + .w-10-lg{ width: 2.5rem; } +} + +@media (min-width: 1200px) { /* xl */ + /* Font */ + .text-xxs-xl { font-size: 0.55rem; } + .text-xs-xl { font-size: 0.75rem; } + .text-sm-xl { font-size: 0.875rem; } + .text-base-xl{ font-size: 1rem; } + .text-lg-xl { font-size: 1.125rem; } + .text-xl-xl { font-size: 1.25rem; } + .text-2xl-xl{ font-size: 1.5rem; } + + /* Height */ + .h-1-xl{ height: 0.25rem; } + .h-2-xl{ height: 0.5rem; } + .h-3-xl{ height: 0.75rem; } + .h-4-xl{ height: 1rem; } + .h-5-xl{ height: 1.25rem; } + .h-6-xl{ height: 1.5rem; } + .h-8-xl{ height: 2rem; } + .h-10-xl{ height: 2.5rem; } + + /* Width */ + .w-1-xl{ width: 0.25rem; } + .w-2-xl{ width: 0.5rem; } + .w-3-xl{ width: 0.75rem; } + .w-4-xl{ width: 1rem; } + .w-5-xl{ width: 1.25rem; } + .w-6-xl{ width: 1.5rem; } + .w-8-xl{ width: 2rem; } + .w-10-xl{ width: 2.5rem; } +} diff --git a/src/components/Activities/AttendcesLogs.jsx b/src/components/Activities/AttendcesLogs.jsx index e58565e8..8f8a55ac 100644 --- a/src/components/Activities/AttendcesLogs.jsx +++ b/src/components/Activities/AttendcesLogs.jsx @@ -38,190 +38,136 @@ 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 selectedProject = useSelectedProject(); +const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); +const dispatch = useDispatch(); +const [loading, setLoading] = useState(false); +const [showPending, setShowPending] = useState(false); +const [isRefreshing, setIsRefreshing] = useState(false); - const [isRefreshing, setIsRefreshing] = useState(false); - const [processedData, setProcessedData] = useState([]); +const today = new Date(); +today.setHours(0, 0, 0, 0); - const today = new Date(); - today.setHours(0, 0, 0, 0); +const yesterday = new Date(); +yesterday.setDate(yesterday.getDate() - 1); - const yesterday = new Date(); - yesterday.setDate(yesterday.getDate() - 1); +const isSameDay = (dateStr) => { + if (!dateStr) return false; + const d = new Date(dateStr); + d.setHours(0, 0, 0, 0); + return d.getTime() === today.getTime(); +}; - const isSameDay = (dateStr) => { - if (!dateStr) return false; - const d = new Date(dateStr); - d.setHours(0, 0, 0, 0); - return d.getTime() === today.getTime(); - }; +const isBeforeToday = (dateStr) => { + if (!dateStr) return false; + const d = new Date(dateStr); + d.setHours(0, 0, 0, 0); + return d.getTime() < today.getTime(); +}; - const isBeforeToday = (dateStr) => { - if (!dateStr) return false; - const d = new Date(dateStr); - d.setHours(0, 0, 0, 0); - return d.getTime() < today.getTime(); - }; +const sortByName = (a, b) => { + const nameA = (a.firstName + a.lastName).toLowerCase(); + const nameB = (b.firstName + b.lastName).toLowerCase(); + return nameA.localeCompare(nameB); +}; - const sortByName = (a, b) => { - const nameA = a.firstName.toLowerCase() + a.lastName.toLowerCase(); - const nameB = b.firstName.toLowerCase() + b.lastName.toLowerCase(); - return nameA?.localeCompare(nameB); - }; +const { data = [], isLoading, error, refetch, isFetching } = useAttendancesLogs( + selectedProject, + dateRange.startDate, + dateRange.endDate, + organizationId +); - const { - data = [], - isLoading, - error, - refetch, - isFetching, - } = useAttendancesLogs( - selectedProject, - dateRange.startDate, - dateRange.endDate, - organizationId - ); - const filtering = (data) => { - const filteredData = showPending - ? data.filter((item) => item.checkOutTime === null) - : data; +const processedData = useMemo(() => { + const filteredData = showPending + ? data.filter((item) => item.checkOutTime === null) + : data; - 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; - }, {}); - - const sortedDates = Object.keys(groupedByDate).sort( - (a, b) => new Date(b) - new Date(a) - ); - - const finalData = sortedDates.flatMap((date) => groupedByDate[date]); - setProcessedData(finalData); - }; - - useEffect(() => { - filtering(data); - }, [data, showPending]); - - // New useEffect to handle search filtering - const filteredSearchData = useMemo(() => { - if (!searchTerm) { - return processedData; + 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); } - const lowercasedSearchTerm = searchTerm.toLowerCase(); - return processedData.filter((item) => { - const fullName = `${item.firstName} ${item.lastName}`.toLowerCase(); - return fullName.includes(lowercasedSearchTerm); - }); - }, [processedData, searchTerm]); + return acc; + }, {}); - const { - currentPage, - totalPages, - currentItems: paginatedAttendances, - paginate, - resetPage, - } = usePagination(filteredSearchData, 20); + const sortedDates = Object.keys(groupedByDate).sort((a, b) => new Date(b) - new Date(a)); + return sortedDates.flatMap((date) => groupedByDate[date]); +}, [data, showPending]); - useEffect(() => { - resetPage(); - }, [filteredSearchData, resetPage]); +const filteredSearchData = useMemo(() => { + if (!searchTerm) return processedData; - const handler = useCallback( - (msg) => { - const { startDate, endDate } = dateRange; - const checkIn = msg.response.checkInTime.substring(0, 10); - if ( - selectedProject === msg.projectId && - startDate <= checkIn && - checkIn <= endDate - ) { - queryClient.setQueriesData(["attendanceLogs"], (oldData) => { - if (!oldData) { - queryClient.invalidateQueries({ queryKey: ["attendanceLogs"] }); - return; - } - const updatedAttendance = oldData.map((record) => - record.id === msg.response.id - ? { ...record, ...msg.response } - : record - ); - filtering(updatedAttendance); - return updatedAttendance; - }); - resetPage(); - } - }, - [selectedProject, dateRange, filtering, resetPage] + const lowercased = searchTerm.toLowerCase(); + return processedData.filter((item) => + `${item.firstName} ${item.lastName}`.toLowerCase().includes(lowercased) ); +}, [processedData, searchTerm]); - useEffect(() => { - eventBus.on("attendance_log", handler); - return () => eventBus.off("attendance_log", handler); - }, [handler]); +const { + currentPage, + totalPages, + currentItems: paginatedAttendances, + paginate, + resetPage, +} = usePagination(filteredSearchData, 20); - const employeeHandler = useCallback( - (msg) => { - const { startDate, endDate } = dateRange; - if (data.some((item) => item.employeeId == msg.employeeId)) { - // dispatch( - // fetchAttendanceData({ - // , - // fromDate: startDate, - // toDate: endDate, - // }) - // ); +useEffect(() => { + resetPage(); +}, [filteredSearchData]); + +const handler = useCallback( + (msg) => { + const { startDate, endDate } = dateRange; + const checkIn = msg.response.checkInTime.substring(0, 10); + + if (selectedProject === msg.projectId && startDate <= checkIn && checkIn <= endDate) { + queryClient.setQueriesData(["attendanceLogs"], (oldData) => { + if (!oldData) { + queryClient.invalidateQueries({ queryKey: ["attendanceLogs"] }); + return; + } + return oldData.map((record) => + record.id === msg.response.id ? { ...record, ...msg.response } : record + ); + }); + resetPage(); + } + }, + [selectedProject, dateRange, resetPage] +); + +useEffect(() => { + eventBus.on("attendance_log", handler); + return () => eventBus.off("attendance_log", handler); +}, [handler]); + +const employeeHandler = useCallback( + (msg) => { + const { startDate, endDate } = dateRange; + if (data.some((item) => item.employeeId == msg.employeeId)) { + refetch(); + } + }, + [data, refetch] +); + +useEffect(() => { + eventBus.on("employee", employeeHandler); + return () => eventBus.off("employee", employeeHandler); +}, [employeeHandler]); - refetch(); - } - }, - [selectedProject, dateRange, data, refetch] - ); - useEffect(() => { - eventBus.on("employee", employeeHandler); - return () => eventBus.off("employee", employeeHandler); - }, [employeeHandler]); return ( <> diff --git a/src/components/Activities/Regularization.jsx b/src/components/Activities/Regularization.jsx index 64a337a3..0aab7474 100644 --- a/src/components/Activities/Regularization.jsx +++ b/src/components/Activities/Regularization.jsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useState, useMemo } from "react"; import Avatar from "../common/Avatar"; -import { convertShortTime } from "../../utils/dateUtils"; +import { convertShortTime, formatUTCToLocalTime } from "../../utils/dateUtils"; import RegularizationActions from "./RegularizationActions"; import { useSelector } from "react-redux"; import { useRegularizationRequests } from "../../hooks/useAttendance"; @@ -33,7 +33,11 @@ const Regularization = ({ ); useEffect(() => { + if(!regularizes) return + if(regularizes?.length) { setregularizedList(regularizes); + + } }, [regularizes]); const sortByName = (a, b) => { @@ -122,13 +126,19 @@ const Regularization = ({