diff --git a/src/components/Activities/Attendance.jsx b/src/components/Activities/Attendance.jsx index eb7dc500..6d751b12 100644 --- a/src/components/Activities/Attendance.jsx +++ b/src/components/Activities/Attendance.jsx @@ -114,7 +114,10 @@ const Attendance = ({ getRole, handleModalData, searchTerm }) => { return ( <> -
+
Date : {formatUTCToLocalTime(todayDate)}
@@ -209,7 +212,11 @@ const Attendance = ({ getRole, handleModalData, searchTerm }) => { ))} {!attendance && ( - No employees assigned to the project! + + + No employees assigned to the project! + + )} @@ -258,7 +265,10 @@ const Attendance = ({ getRole, handleModalData, searchTerm }) => { )} ) : ( -
+
{searchTerm ? "No results found for your search." : attendanceList.length === 0 diff --git a/src/components/Activities/AttendcesLogs.jsx b/src/components/Activities/AttendcesLogs.jsx index 681783be..faf4bdc5 100644 --- a/src/components/Activities/AttendcesLogs.jsx +++ b/src/components/Activities/AttendcesLogs.jsx @@ -42,7 +42,7 @@ const AttendanceLog = ({ handleModalData, searchTerm }) => { 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([]); @@ -245,17 +245,16 @@ const AttendanceLog = ({ handleModalData, searchTerm }) => {
refetch()} />
-
+
{isLoading ? ( -
+

Loading...

) : filteredSearchData?.length > 0 ? ( @@ -284,9 +283,9 @@ const AttendanceLog = ({ handleModalData, searchTerm }) => { 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) { @@ -346,12 +345,15 @@ const AttendanceLog = ({ handleModalData, searchTerm }) => { ) : ( -
No Record Available !
+
No Record Available !
)}
{paginatedAttendances?.length == 0 && filteredSearchData?.length > 0 && ( -
- No Pending Record Available ! +
+ No Record Available !
)} {filteredSearchData.length > ITEMS_PER_PAGE && ( @@ -369,9 +371,8 @@ const AttendanceLog = ({ handleModalData, searchTerm }) => { (pageNumber) => (
  • +
    +
    ); diff --git a/src/components/Activities/Regularization.jsx b/src/components/Activities/Regularization.jsx index cdcd3972..82c906b5 100644 --- a/src/components/Activities/Regularization.jsx +++ b/src/components/Activities/Regularization.jsx @@ -87,9 +87,9 @@ const Regularization = ({ handleRequest, searchTerm }) => { }, [employeeHandler]); return ( -
    +
    {loading ? ( -
    +

    Loading...

    ) : currentItems?.length > 0 ? ( @@ -143,9 +143,14 @@ const Regularization = ({ handleRequest, searchTerm }) => { ) : ( -
    +
    - {searchTerm ? "No results found for your search." : "No Requests Found !"} + {searchTerm + ? "No results found for your search." + : "No Requests Found !"}
    )} @@ -163,9 +168,8 @@ const Regularization = ({ handleRequest, searchTerm }) => { {[...Array(totalPages)].map((_, index) => (
  • ))}
  • -
    - +
    + +
    ); }; - export default ReportTask; \ No newline at end of file +export default ReportTask; \ No newline at end of file diff --git a/src/components/Activities/ReportTaskComments.jsx b/src/components/Activities/ReportTaskComments.jsx index c48f9796..60a81ff3 100644 --- a/src/components/Activities/ReportTaskComments.jsx +++ b/src/components/Activities/ReportTaskComments.jsx @@ -10,6 +10,7 @@ import { getBgClassFromHash } from "../../utils/projectStatus"; import { cacheData, getCachedData } from "../../slices/apiDataManager"; import ImagePreview from "../common/ImagePreview"; import { useAuditStatus, useSubmitTaskComment } from "../../hooks/useTasks"; +import Label from "../common/Label"; const ReportTaskComments = ({ commentsData, @@ -291,10 +292,10 @@ const ReportTaskComments = ({

    )}
    -
    -
    + return ( +
    + {/* Header */} +
    +
    +
    Attendance Overview
    +

    Role-wise present count

    +
    +
    + + + +
    +
    {/* Content */}
    diff --git a/src/components/Dashboard/ProjectCompletionChart.jsx b/src/components/Dashboard/ProjectCompletionChart.jsx index f0c85179..8ce4b13a 100644 --- a/src/components/Dashboard/ProjectCompletionChart.jsx +++ b/src/components/Dashboard/ProjectCompletionChart.jsx @@ -19,7 +19,7 @@ const ProjectCompletionChart = () => {
    -
    Projects
    +
    Projects

    Projects Completion Status

    diff --git a/src/components/Dashboard/ProjectProgressChart.jsx b/src/components/Dashboard/ProjectProgressChart.jsx index f80bb0d5..61747f6c 100644 --- a/src/components/Dashboard/ProjectProgressChart.jsx +++ b/src/components/Dashboard/ProjectProgressChart.jsx @@ -90,7 +90,7 @@ const ProjectProgressChart = ({
    {/* Left: Title */}
    -
    Project Progress
    +
    Project Progress

    Progress Overview by Project

    diff --git a/src/components/Directory/ManageBucket.jsx b/src/components/Directory/ManageBucket.jsx index 58344a15..5cbd89c4 100644 --- a/src/components/Directory/ManageBucket.jsx +++ b/src/components/Directory/ManageBucket.jsx @@ -198,23 +198,14 @@ const ManageBucket = () => { return ( <> {deleteBucket && ( -
    - setDeleteBucket(null)} - /> -
    + setDeleteBucket(null)} + /> )}
    @@ -237,8 +228,9 @@ const ManageBucket = () => { onChange={(e) => setSearchTerm(e.target.value)} /> refetch()} /> @@ -247,8 +239,9 @@ const ManageBucket = () => {
    )} - {!loading && buckets.length > 0 && sortedBucktesList.length === 0 && ( -
    -
    - No matching buckets found. + {!loading && + buckets.length > 0 && + sortedBucktesList.length === 0 && ( +
    +
    + No matching buckets found. +
    -
    - )} + )} {!loading && sortedBucktesList.map((bucket) => (
    @@ -305,29 +300,29 @@ const ManageBucket = () => { {(DirManager || DirAdmin || bucket?.createdBy?.id === - profile?.employeeInfo?.id) && ( -
    - { - select_bucket(bucket); - setAction_bucket(true); - const initialSelectedEmployees = employeesList - .filter((emp) => - bucket.employeeIds?.includes( - emp.employeeId - ) + profile?.employeeInfo?.id) && ( +
    + { + select_bucket(bucket); + setAction_bucket(true); + const initialSelectedEmployees = employeesList + .filter((emp) => + bucket.employeeIds?.includes( + emp.employeeId ) - .map((emp) => ({ ...emp, isActive: true })); - setSelectEmployee(initialSelectedEmployees); - }} - > - setDeleteBucket(bucket?.id)} - > -
    - )} + ) + .map((emp) => ({ ...emp, isActive: true })); + setSelectEmployee(initialSelectedEmployees); + }} + >
    + setDeleteBucket(bucket?.id)} + > +
    + )}
    Contacts:{" "} diff --git a/src/components/Directory/ManageDirectory.jsx b/src/components/Directory/ManageDirectory.jsx index 5c858815..d03139a9 100644 --- a/src/components/Directory/ManageDirectory.jsx +++ b/src/components/Directory/ManageDirectory.jsx @@ -23,6 +23,7 @@ import { useProjects } from "../../hooks/useProjects"; import SelectMultiple from "../common/SelectMultiple"; import { ContactSchema } from "./DirectorySchema"; import InputSuggestions from "../common/InputSuggestion"; +import Label from "../common/Label"; const ManageDirectory = ({ submitContact, onCLosed }) => { const selectedMaster = useSelector( @@ -40,7 +41,7 @@ const ManageDirectory = ({ submitContact, onCLosed }) => { const { designationList, loading: designloading } = useDesignation(); const { contactTags, loading: Tagloading } = useContactTags(); const [IsSubmitting, setSubmitting] = useState(false); - const [showSuggestions,setShowSuggestions] = useState(false); + const [showSuggestions, setShowSuggestions] = useState(false); const [filteredDesignationList, setFilteredDesignationList] = useState([]); const dispatch = useDispatch(); @@ -132,6 +133,12 @@ const ManageDirectory = ({ submitContact, onCLosed }) => { setValue("designation", val); }; + // Handle phone number input to only allow numbers and max length of 10 + const handlePhoneInput = (e) => { + const value = e.target.value.replace(/[^0-9]/g, ""); + e.target.value = value.slice(0, 10); + }; + const toggleBucketId = (id) => { const updated = watchBucketIds?.includes(id) @@ -171,11 +178,11 @@ const ManageDirectory = ({ submitContact, onCLosed }) => {
    -
    Create New Contact
    +
    Create New Contact
    - + {
    - + {
    - + { placeholder="email@example.com" /> {index === emailFields.length - 1 ? ( - //
    - +
      {bucketsLoaging &&

      Loading...

      } @@ -475,8 +460,8 @@ const ManageDirectory = ({ submitContact, onCLosed }) => { />
    -
    - +
    + + {errors.description && ( +
    {errors.description.message}
    + )} +
    + + {/* Buttons */} +
    + + +
    + + +
    + ); +}; + +export default ManageDocument; diff --git a/src/components/Documents/VersionListSkeleton.jsx b/src/components/Documents/VersionListSkeleton.jsx new file mode 100644 index 00000000..d239315c --- /dev/null +++ b/src/components/Documents/VersionListSkeleton.jsx @@ -0,0 +1,47 @@ +import React from "react"; + +const SkeletonLine = ({ height = 16, width = "100%", className = "" }) => ( +
    +); + +const VersionListSkeleton = ({ items = 5 }) => { + return ( +
    + {[...Array(items)].map((_, idx) => ( +
    + {/* Top row: document name + version/status */} +
    + +
    + + +
    +
    + + {/* Upload by row */} +
    + + +
    + + {/* Updated at row */} +
    + +
    +
    + ))} +
    + ); +}; + +export default VersionListSkeleton; diff --git a/src/components/Documents/ViewDocument.jsx b/src/components/Documents/ViewDocument.jsx new file mode 100644 index 00000000..6405a3ed --- /dev/null +++ b/src/components/Documents/ViewDocument.jsx @@ -0,0 +1,175 @@ +import React, { useState } from "react"; +import { + useDocumentDetails, + useDocumentVersionList, + useVerifyDocument, +} from "../../hooks/useDocument"; +import { getDocuementsStatus, useDocumentContext } from "./Documents"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import Avatar from "../common/Avatar"; +import { + DOWNLOAD_DOCUMENT, + ITEMS_PER_PAGE, + VERIFY_DOCUMENT, +} from "../../utils/constants"; +import DocumentDetailsSkeleton from "./DocumentDetailsSkeleton "; +import { useHasUserPermission } from "../../hooks/useHasUserPermission"; +import DocumentVersionList from "./DocumentVersionList"; + +const ViewDocument = () => { + const { viewDoc, setOpenDocument } = useDocumentContext(); + const [currentPage, setCurrentPage] = useState(1); + const [showVersions, setShowVersions] = useState(false); + const canVerifyDocument = useHasUserPermission(VERIFY_DOCUMENT); + + // Document Details + const { data, isLoading, isError, error } = useDocumentDetails( + viewDoc?.document + ); + + // Document Versions (fetch only if toggle is ON) + const { + data: versionList, + isLoading: versionLoading, + } = useDocumentVersionList( + showVersions ? data?.parentAttachmentId : null, + ITEMS_PER_PAGE - 10, + currentPage + ); + + const paginate = (page) => { + if (page >= 1 && page <= (versionList?.totalPages ?? 1)) { + setCurrentPage(page); + } + }; + + // Verify / Reject + const { mutate: VerifyDoc, isPending } = useVerifyDocument(); + const VerifyDocument = () => { + VerifyDoc({ documentId: viewDoc?.document, isVerify: true }); + }; + const RejectDocument = () => { + VerifyDoc({ documentId: viewDoc?.document, isVerify: false }); + }; + + if (isLoading) return ; + if (isError) + return ( +
    +

    {error?.response?.data?.message || error?.message}

    +

    {error?.response?.status}

    +
    + ); + + return ( +
    +

    Document Details

    + + {/* Document Info Rows */} +
    +
    + + Category: + + + {data.documentType?.documentCategory?.name || "-"} + +
    +
    + + Type: + + {data.documentType?.name || "-"} +
    +
    + +
    +
    + + Document Name: + + {data.name || "-"} +
    +
    + + Document ID: + + {data.documentId || "-"} +
    +
    + +
    +
    + + Uploaded At: + + + {formatUTCToLocalTime(data.uploadedAt)} + +
    + +
    + +
    +
    + + Tags: + +
    + {data.tags?.length > 0 ? ( + data.tags.map((t, i) => ( + + {t.name} + + )) + ) : ( + - + )} +
    +
    +
    + +
    +
    + + Description: + + {data.description || "-"} +
    +
    + + {/* Toggle for Versions */} +
    +

    Documents:

    +
    + + setShowVersions(e.target.checked)} + /> +
    +
    + + +
    + ); +}; + +export default ViewDocument; \ No newline at end of file diff --git a/src/components/Employee/EmpActivities.jsx b/src/components/Employee/EmpActivities.jsx index f5d9e8c2..e79b181d 100644 --- a/src/components/Employee/EmpActivities.jsx +++ b/src/components/Employee/EmpActivities.jsx @@ -25,7 +25,7 @@ error,
    diff --git a/src/components/Employee/EmpAttendance.jsx b/src/components/Employee/EmpAttendance.jsx index 26ae9531..dfa12e98 100644 --- a/src/components/Employee/EmpAttendance.jsx +++ b/src/components/Employee/EmpAttendance.jsx @@ -128,7 +128,7 @@ const EmpAttendance = ({ employee }) => { >
    diff --git a/src/components/Employee/EmpBanner.jsx b/src/components/Employee/EmpBanner.jsx index 6cb12073..bb019bc6 100644 --- a/src/components/Employee/EmpBanner.jsx +++ b/src/components/Employee/EmpBanner.jsx @@ -3,6 +3,7 @@ import React, { useState, useEffect } from "react"; import { useChangePassword } from "../../components/Context/ChangePasswordContext"; import GlobalModel from "../common/GlobalModel"; import ManageEmployee from "./ManageEmployee"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; const EmpBanner = ({ profile, loggedInUser }) => { const { openChangePassword } = useChangePassword(); @@ -77,7 +78,7 @@ const EmpBanner = ({ profile, loggedInUser }) => { {" "} Joined on{" "} {profile?.joiningDate ? ( - new Date(profile.joiningDate).toLocaleDateString() + formatUTCToLocalTime(profile.joiningDate) ) : ( NA )} diff --git a/src/components/Employee/EmpDashboard.jsx b/src/components/Employee/EmpDashboard.jsx index b25f6803..85c97836 100644 --- a/src/components/Employee/EmpDashboard.jsx +++ b/src/components/Employee/EmpDashboard.jsx @@ -9,7 +9,6 @@ const EmpDashboard = ({ profile }) => { refetch, } = useProjectsAllocationByEmployee(profile?.id); - console.log(projectList); return ( <>
    diff --git a/src/components/Employee/EmpDocuments.jsx b/src/components/Employee/EmpDocuments.jsx index 46aa33d0..b4c8046b 100644 --- a/src/components/Employee/EmpDocuments.jsx +++ b/src/components/Employee/EmpDocuments.jsx @@ -1,10 +1,15 @@ import React, { useState, useEffect } from "react"; import { ComingSoonPage } from "../../pages/Misc/ComingSoonPage"; +import DocumentPage from "../../pages/Documents/DocumentPage"; +import Documents from "../Documents/Documents"; +import { useParams } from "react-router-dom"; +import { DOCUMENTS_ENTITIES } from "../../utils/constants"; const EmpDocuments = ({ profile, loggedInUser }) => { + const {employeeId} = useParams() return ( <> - + ); }; diff --git a/src/components/Employee/EmpOverview.jsx b/src/components/Employee/EmpOverview.jsx index 6b396745..cf4ce4af 100644 --- a/src/components/Employee/EmpOverview.jsx +++ b/src/components/Employee/EmpOverview.jsx @@ -142,13 +142,14 @@ const EmpOverview = ({ profile }) => {
    {/* Address */} -
    - +
    + - Address + Address + : - + {profile?.currentAddress || NA}
    diff --git a/src/components/Employee/EmployeeList.jsx b/src/components/Employee/EmployeeList.jsx index 2ae93d5e..5a79c6ce 100644 --- a/src/components/Employee/EmployeeList.jsx +++ b/src/components/Employee/EmployeeList.jsx @@ -4,4 +4,4 @@ const EmployeeList = () => { return
    EmployeeList
    ; }; -export default EmployeeList; +export default EmployeeList; \ No newline at end of file diff --git a/src/components/Employee/EmployeeNav.jsx b/src/components/Employee/EmployeeNav.jsx index d9a6b3e0..8c25aa22 100644 --- a/src/components/Employee/EmployeeNav.jsx +++ b/src/components/Employee/EmployeeNav.jsx @@ -1,12 +1,31 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; +import { useHasUserPermission } from "../../hooks/useHasUserPermission"; +import { VIEW_DOCUMENT } from "../../utils/constants"; +import { useProfile } from "../../hooks/useProfile"; +import { useParams } from "react-router-dom"; const EmployeeNav = ({ onPillClick, activePill }) => { + const { employeeId } = useParams(); + const [isAbleToViewDocuments, setIsAbleToViewDocuments] = useState(false); + + const canViewDocuments = useHasUserPermission(VIEW_DOCUMENT); + const { profile } = useProfile(); + + useEffect(() => { + if (profile?.employeeInfo?.id) { + setIsAbleToViewDocuments(profile.employeeInfo.id === employeeId); + } + }, [profile?.employeeInfo?.id, employeeId]); + const tabs = [ { key: "profile", icon: "bx bx-user", label: "Profile" }, { key: "attendance", icon: "bx bx-group", label: "Attendances" }, - { key: "documents", icon: "bx bx-user", label: "Documents" }, + (isAbleToViewDocuments || canViewDocuments) && { + key: "documents", + icon: "bx bx-file", + label: "Documents", + }, { key: "activities", icon: "bx bx-grid-alt", label: "Activities" }, - ]; - + ].filter(Boolean); return (
    diff --git a/src/components/Employee/ManageEmployee.jsx b/src/components/Employee/ManageEmployee.jsx index 1805220b..81a4c4d2 100644 --- a/src/components/Employee/ManageEmployee.jsx +++ b/src/components/Employee/ManageEmployee.jsx @@ -17,6 +17,8 @@ import { } from "../../slices/apiDataManager"; import { clearApiCacheKey } from "../../slices/apiCacheSlice"; import { useMutation } from "@tanstack/react-query"; +import Label from "../common/Label"; +import DatePicker from "../common/DatePicker"; const mobileNumberRegex = /^[0-9]\d{9}$/; @@ -220,10 +222,10 @@ const ManageEmployee = ({ employeeId, onClosed, IsAllEmployee }) => { return ( <>
    -

    {employee ? "Update Employee" : "Create Employee"}

    +

    {employee ? "Update Employee" : "Create Employee"}

    -
    First Name
    + {
    -
    Last Name
    + { )}
    -
    Phone Number
    + {
    -
    Gender
    +
    + {errors.birthDate && ( -
    +
    {errors.birthDate.message}
    )}
    -
    Joining Date
    +
    -
    + {errors.joiningDate && ( -
    +
    {errors.joiningDate.message}
    )} @@ -412,7 +416,7 @@ const ManageEmployee = ({ employeeId, onClosed, IsAllEmployee }) => {
    -
    Current Address
    +