diff --git a/package-lock.json b/package-lock.json index 39e9a3b4..75c151b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "apexcharts": "^4.5.0", "axios": "^1.7.9", "axios-retry": "^4.5.0", + "date-fns": "^4.1.0", "dotenv": "^16.4.7", "dotenv-webpack": "^8.1.0", "eventemitter3": "^5.0.1", @@ -2455,6 +2456,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", diff --git a/package.json b/package.json index 3f4d52fe..33e1f019 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "apexcharts": "^4.5.0", "axios": "^1.7.9", "axios-retry": "^4.5.0", + "date-fns": "^4.1.0", "dotenv": "^16.4.7", "dotenv-webpack": "^8.1.0", "eventemitter3": "^5.0.1", diff --git a/public/assets/css/core-extend.css b/public/assets/css/core-extend.css index c017c963..327febfa 100644 --- a/public/assets/css/core-extend.css +++ b/public/assets/css/core-extend.css @@ -3,10 +3,6 @@ --bs-nav-link-font-size: 0.7375rem; } -.nav { - --bs-nav-link-font-size: 0.7375rem; -} - .card-header { padding: 0.5rem var(--bs-card-cap-padding-x); } diff --git a/public/assets/img/avatars/avatar_f_01.png b/public/assets/img/avatars/avatar_f_01.png new file mode 100644 index 00000000..978d2438 Binary files /dev/null and b/public/assets/img/avatars/avatar_f_01.png differ diff --git a/public/assets/img/avatars/avatar_f_02.png b/public/assets/img/avatars/avatar_f_02.png new file mode 100644 index 00000000..38958cb6 Binary files /dev/null and b/public/assets/img/avatars/avatar_f_02.png differ diff --git a/public/assets/img/avatars/avatar_m_01.png b/public/assets/img/avatars/avatar_m_01.png new file mode 100644 index 00000000..30dfa72d Binary files /dev/null and b/public/assets/img/avatars/avatar_m_01.png differ diff --git a/public/assets/img/avatars/avatar_m_02.png b/public/assets/img/avatars/avatar_m_02.png new file mode 100644 index 00000000..b43e1c74 Binary files /dev/null and b/public/assets/img/avatars/avatar_m_02.png differ diff --git a/public/img/favicon.ico b/public/img/favicon.ico new file mode 100644 index 00000000..be94d1b7 Binary files /dev/null and b/public/img/favicon.ico differ diff --git a/public/img/favicon/favicon.ico b/public/img/favicon/favicon.ico index 1ccd2aea..be94d1b7 100644 Binary files a/public/img/favicon/favicon.ico and b/public/img/favicon/favicon.ico differ diff --git a/src/components/Dashboard/AttendanceChart.jsx b/src/components/Dashboard/AttendanceChart.jsx index 7fcc862e..df99b07d 100644 --- a/src/components/Dashboard/AttendanceChart.jsx +++ b/src/components/Dashboard/AttendanceChart.jsx @@ -6,180 +6,198 @@ import flatColors from "../Charts/flatColor"; import ChartSkeleton from "../Charts/Skelton"; const formatDate = (dateStr) => { - const date = new Date(dateStr); - return date.toLocaleDateString("en-GB", { - day: "2-digit", - month: "long", - }); + const date = new Date(dateStr); + return date.toLocaleDateString("en-GB", { + day: "2-digit", + month: "long", + }); }; const AttendanceOverview = () => { - const [dayRange, setDayRange] = useState(7); - const [view, setView] = useState("chart"); + const [dayRange, setDayRange] = useState(7); + const [view, setView] = useState("chart"); - const projectId = useSelector((store) => store.localVariables.projectId); - const { attendanceOverviewData, loading, error } = useAttendanceOverviewData(projectId, dayRange); + const projectId = useSelector((store) => store.localVariables.projectId); + const { attendanceOverviewData, loading, error } = useAttendanceOverviewData( + projectId, + dayRange + ); - const { tableData, roles, dates } = useMemo(() => { - const map = new Map(); + const { tableData, roles, dates } = useMemo(() => { + const map = new Map(); - attendanceOverviewData.forEach((entry) => { - const date = formatDate(entry.date); - if (!map.has(date)) map.set(date, {}); - map.get(date)[entry.role.trim()] = entry.present; - }); + attendanceOverviewData.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 sortedDates = [...map.keys()]; - const data = sortedDates.map((date) => { - const row = { date }; - uniqueRoles.forEach((role) => { - row[role] = map.get(date)?.[role] ?? 0; - }); - return row; - }); + const uniqueRoles = [ + ...new Set(attendanceOverviewData.map((e) => e.role.trim())), + ]; + const sortedDates = [...map.keys()]; + const data = sortedDates.map((date) => { + const row = { date }; + uniqueRoles.forEach((role) => { + row[role] = map.get(date)?.[role] ?? 0; + }); + return row; + }); - return { - tableData: data, - roles: uniqueRoles, - dates: sortedDates, - }; - }, [attendanceOverviewData]); - - const chartSeries = roles.map((role) => ({ - name: role, - data: tableData.map((row) => row[role]), - })); - - const chartOptions = { - 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', - offsetX: 0, - offsetY: 0 - }, - axisTicks: { - show: true, - borderType: 'solid', - color: '#78909C', - width: 6, - offsetX: 0, - offsetY: 0 - }, - }, - legend: { - position: "bottom", - }, - fill: { - opacity: 1, - }, - colors: roles.map((_, i) => flatColors[i % flatColors.length]), + return { + tableData: data, + roles: uniqueRoles, + dates: sortedDates, }; + }, [attendanceOverviewData]); - return ( -
- {/* Header */} -
-
-
Attendance Overview
-

Role-wise present count

-
-
- - - -
-
+ const chartSeries = roles.map((role) => ({ + name: role, + data: tableData.map((row) => row[role]), + })); - {/* Content */} -
- {loading ? ( - - ) : error ? ( -

{error}

- ) : view === "chart" ? ( -
- -
- ) : ( -
- - - - - {dates.map((date, idx) => ( - - ))} - - + const chartOptions = { + 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", + offsetX: 0, + offsetY: 0, + }, + axisTicks: { + show: true, + borderType: "solid", + color: "#78909C", + width: 6, + offsetX: 0, + offsetY: 0, + }, + }, + legend: { + position: "bottom", + }, + fill: { + opacity: 1, + }, + colors: roles.map((_, i) => flatColors[i % flatColors.length]), + }; - - {roles.map((role) => ( - - - {tableData.map((row, idx) => { - const value = row[role]; - const cellStyle = value > 0 ? { backgroundColor: '#d5d5d5' } : {}; - return ( - - ); - })} - - ))} - -
Role{date}
{role} - {value} -
-
- )} -
+ return ( +
+ {/* Header */} +
+
+
Attendance Overview
+

Role-wise present count

- ); +
+ + + +
+
+ + {/* Content */} +
+ {loading ? ( + + ) : error ? ( +

{error}

+ ) : view === "chart" ? ( +
+ +
+ ) : ( +
+ + + + + {dates.map((date, idx) => ( + + ))} + + + + + {roles.map((role) => ( + + + {tableData.map((row, idx) => { + const value = row[role]; + const cellStyle = + value > 0 ? { backgroundColor: "#d5d5d5" } : {}; + return ( + + ); + })} + + ))} + +
+ Role + + {date} +
{role} + {value} +
+
+ )} +
+
+ ); }; -export default AttendanceOverview; \ No newline at end of file +export default AttendanceOverview; diff --git a/src/components/Employee/EmpActivities.jsx b/src/components/Employee/EmpActivities.jsx new file mode 100644 index 00000000..f5d9e8c2 --- /dev/null +++ b/src/components/Employee/EmpActivities.jsx @@ -0,0 +1,161 @@ +import React, { useState, useEffect } from "react"; +import moment from "moment"; +import DateRangePicker from "../common/DateRangePicker"; +import useFormattedDate from "../../hooks/useFormattedDate"; +import { useProjectTasksByEmployee } from "../../hooks/useProjects"; + +const EmpActivities = ({ employee }) => { + const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); + const myDate = new Date("2025-08-06T10:30:00Z"); + const formattedToday = useFormattedDate(myDate, "dd-MMM-yyyy"); + + + const { + data, +isError, +isLoading, +error, + refetch, + } = useProjectTasksByEmployee(employee?.id,dateRange.startDate,dateRange.endDate); + + if(isLoading) return
Loading...
+ return ( + <> +
+
+
+ +
+
    + {data?.map((activity)=>( +
  • + +
    +
    +
    {activity.projectName}
    + + {useFormattedDate(activity.assignmentDate, "dd-MMM-yyyy")} + +
    +

    Activity:{activity.activityName}

    +

    + Location: {activity.location} +

    +

    + Planned: {activity.plannedTask} + Completed : {activity.completedTask} +

    +
    +
  • + ))} + + + {/*
  • + +
    +
    +
    Client Meeting
    + 45 min ago +
    +

    Project meeting with john @10:15am

    +
    +
    +
    + Avatar +
    +
    +

    + Lester McCarthy (Client) +

    + CEO of ThemeSelection +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    Create a new project for client
    + 2 Day Ago +
    +

    6 team members in a project

    +
      +
    • +
      +
        +
      • + Avatar +
      • +
      • + Avatar +
      • +
      • + Avatar +
      • +
      • + + +3 + +
      • +
      +
      +
    • +
    +
    +
  • */} +
+
+
+ + ); +}; + +export default EmpActivities; diff --git a/src/pages/employee/AttendancesEmployeeRecords.jsx b/src/components/Employee/EmpAttendance.jsx similarity index 83% rename from src/pages/employee/AttendancesEmployeeRecords.jsx rename to src/components/Employee/EmpAttendance.jsx index 9b3b7b06..d3cbe817 100644 --- a/src/pages/employee/AttendancesEmployeeRecords.jsx +++ b/src/components/Employee/EmpAttendance.jsx @@ -1,24 +1,30 @@ import React, { useState, useEffect } from "react"; import moment from "moment"; -import DateRangePicker from "../../components/common/DateRangePicker"; +import DateRangePicker from "../common/DateRangePicker"; import { useDispatch, useSelector } from "react-redux"; import { fetchEmployeeAttendanceData } from "../../slices/apiSlice/employeeAttendanceSlice"; import usePagination from "../../hooks/usePagination"; -import Avatar from "../../components/common/Avatar"; +import Avatar from "../common/Avatar"; import { convertShortTime } from "../../utils/dateUtils"; -import RenderAttendanceStatus from "../../components/Activities/RenderAttendanceStatus"; -import AttendLogs from "../../components/Activities/AttendLogs"; +import RenderAttendanceStatus from "../Activities/RenderAttendanceStatus"; +import AttendLogs from "../Activities/AttendLogs"; import { useAttendanceByEmployee } from "../../hooks/useAttendance"; -import GlobalModel from "../../components/common/GlobalModel"; +import GlobalModel from "../common/GlobalModel"; import { ITEMS_PER_PAGE } from "../../utils/constants"; -const AttendancesEmployeeRecords = ({ employee }) => { +const EmpAttendance = ({ employee }) => { const [attendances, setAttendnaces] = useState([]); const [selectedDate, setSelectedDate] = useState(""); const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); const [isModalOpen, setIsModalOpen] = useState(false); const [attendanceId, setAttendanecId] = useState(); - const {data =[],isLoading:loading,isFetching,error,refetch} = useAttendanceByEmployee(employee,dateRange.startDate, dateRange.endDate) + const { + data = [], + isLoading: loading, + isFetching, + error, + refetch, + } = useAttendanceByEmployee(employee, dateRange.startDate, dateRange.endDate); const dispatch = useDispatch(); // const { data, loading, error } = useSelector( @@ -75,16 +81,24 @@ const AttendancesEmployeeRecords = ({ employee }) => { const uniqueMap = new Map(); [...group1, ...group2, ...group3, ...group4, ...group5].forEach((rec) => { - const date = moment(rec.checkInTime || rec.checkOutTime).format("YYYY-MM-DD"); + const date = moment(rec.checkInTime || rec.checkOutTime).format( + "YYYY-MM-DD" + ); const key = `${rec.employeeId}-${date}`; const existing = uniqueMap.get(key); - if (!existing || new Date(rec.checkInTime || rec.checkOutTime) > new Date(existing.checkInTime || existing.checkOutTime)) { + if ( + !existing || + new Date(rec.checkInTime || rec.checkOutTime) > + new Date(existing.checkInTime || existing.checkOutTime) + ) { uniqueMap.set(key, rec); } }); - const sortedFinalList = [...uniqueMap.values()].sort((a, b) => - new Date(b.checkInTime || b.checkOutTime) - new Date(a.checkInTime || a.checkOutTime) + const sortedFinalList = [...uniqueMap.values()].sort( + (a, b) => + new Date(b.checkInTime || b.checkOutTime) - + new Date(a.checkInTime || a.checkOutTime) ); const currentDate = new Date().toLocaleDateString("en-CA"); @@ -93,8 +107,6 @@ const AttendancesEmployeeRecords = ({ employee }) => { ITEMS_PER_PAGE ); - - const openModal = (id) => { setAttendanecId(id); setIsModalOpen(true); @@ -103,25 +115,28 @@ const AttendancesEmployeeRecords = ({ employee }) => { return ( <> - - {isModalOpen && ( - - + + )} -
+
- +
refetch()} @@ -214,8 +229,9 @@ const AttendancesEmployeeRecords = ({ employee }) => { {[...Array(totalPages)].map((_, index) => (
  • ))}
  • +
  • +
  • + {profile?.id == loggedInUser?.employeeInfo?.id && ( + + )} +
  • + +
    +
    +
    +
    + + ); +}; + +export default EmpBanner; diff --git a/src/components/Employee/EmpDashboard.jsx b/src/components/Employee/EmpDashboard.jsx new file mode 100644 index 00000000..8f029443 --- /dev/null +++ b/src/components/Employee/EmpDashboard.jsx @@ -0,0 +1,84 @@ +import React, { useEffect, useState } from "react"; +import EmpOverview from "./EmpOverview"; +import { useProjectsAllocationByEmployee } from "../../hooks/useProjects"; + +const EmpDashboard = ({ profile }) => { + const { + projectList, + loading: selectedProjectLoding, + refetch, + } = useProjectsAllocationByEmployee(profile?.id); + + console.log(projectList); + return ( + <> +
    +
    + {" "} + +
    +
    +
    +
    + + My Projects + {" "} +
      + {selectedProjectLoding && Loading} + {projectList.map((project) => ( +
    • + {/* Project Info */} +
      +
      +
      +
      + + + +
      +
      +
      {project.projectShortName}
      + {project.projectName} +
      + Assigned:{" "} + {project.assignedDate ? ( + new Date(project.assignedDate).toLocaleDateString() + ) : ( + NA + )} +
      +
      +
      +
      + + {project.designation} + +
      +
      + + {/* Dates */} + {project.removedDate && ( +
      +
      + Unassigned:{" "} + {new Date(project.removedDate).toLocaleDateString()} +
      +
      + )} +
      +
    • + ))} +
    + +
    +
    +
    +
    + + ); +}; + +export default EmpDashboard; diff --git a/src/components/Employee/EmpDocuments.jsx b/src/components/Employee/EmpDocuments.jsx new file mode 100644 index 00000000..46aa33d0 --- /dev/null +++ b/src/components/Employee/EmpDocuments.jsx @@ -0,0 +1,12 @@ +import React, { useState, useEffect } from "react"; +import { ComingSoonPage } from "../../pages/Misc/ComingSoonPage"; + +const EmpDocuments = ({ profile, loggedInUser }) => { + return ( + <> + + + ); +}; + +export default EmpDocuments; diff --git a/src/components/Employee/EmpOverview.jsx b/src/components/Employee/EmpOverview.jsx new file mode 100644 index 00000000..467a2a58 --- /dev/null +++ b/src/components/Employee/EmpOverview.jsx @@ -0,0 +1,109 @@ +import React from "react"; +import Avatar from "../common/Avatar"; +import { useProfile } from "../../hooks/useProfile"; + +const EmpOverview = ({ profile }) => { + const { loggedInUserProfile } = useProfile(); + return ( + <> + {" "} +
    +
    +
    +
    + + About + +
      +
    • + + Full Name:{" "} + {`${profile?.firstName} ${profile?.lastName}`} +
    • +
    • + + Status:{" "} + Active +
    • +
    • + + Role:{" "} + {profile?.jobRole || NA} +
    • +
    • + + Gender:{" "} + {profile?.gender || NA} +
    • {" "} +
    • + + Birth Date:{" "} + + {profile?.birthDate ? ( + new Date(profile.birthDate).toLocaleDateString() + ) : ( + NA + )} + +
    • +
    • + + Joining Date:{" "} + + {profile?.joiningDate ? ( + new Date(profile.joiningDate).toLocaleDateString() + ) : ( + NA + )} + +
    • +
    + + Contacts + +
      +
    • + + Contact:{" "} + {profile?.phoneNumber || NA} +
    • +
    • + + Email:{" "} + + {" "} + {profile?.email || NA} + +
    • +
    • + + + {" "} + Emergency Contact: + {" "} + {profile?.emergencyContactPerson || NA} +
    • +
    • + + Emergency Phone:{" "} + {profile?.emergencyPhoneNumber || NA} +
    • +
    • +
      +
      + Address: +
      +
      + {profile?.currentAddress || NA} +
      +
      +
    • +
    +
    +
    +
    +
    + + ); +}; +export default EmpOverview; diff --git a/src/components/Employee/EmployeeNav.jsx b/src/components/Employee/EmployeeNav.jsx index 32c94b20..d9a6b3e0 100644 --- a/src/components/Employee/EmployeeNav.jsx +++ b/src/components/Employee/EmployeeNav.jsx @@ -1,53 +1,36 @@ import React from "react"; - const EmployeeNav = ({ onPillClick, activePill }) => { + 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" }, + { key: "activities", icon: "bx bx-grid-alt", label: "Activities" }, + ]; + return (
    - diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index 47ca8bf4..6f4988bc 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -27,12 +27,25 @@ const Header = () => { const { data, loading } = useMaster(); const navigate = useNavigate(); const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT); + // { + // console.log(location.pathname); + // } - const isDirectoryPath = /^\/directory$/.test(location.pathname); - const isProjectPath = /^\/projects$/.test(location.pathname); - const isDashboard = + const isDashboardPath = /^\/dashboard$/.test(location.pathname) || /^\/$/.test(location.pathname); + const isProjectPath = /^\/projects$/.test(location.pathname); + const showProjectDropdown = (pathname) => { + const isDirectoryPath = /^\/directory$/.test(pathname); + + // const isProfilePage = /^\/employee$/.test(location.pathname); + const isProfilePage = + /^\/employee\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test( + pathname + ); + + return !(isDirectoryPath || isProfilePage); + }; const allowedProjectStatusIds = [ "603e994b-a27f-4e5d-a251-f3d69b0498ba", "cdad86aa-8a56-4ff4-b633-9c629057dfef", @@ -82,16 +95,16 @@ const Header = () => { }; const handleProfilePage = () => { - navigate(`/employee/${profile?.employeeInfo?.id}?for=attendance`); + navigate(`/employee/${profile?.employeeInfo?.id}`); }; const { projectNames, loading: projectLoading, fetchData } = useProjectName(); const selectedProject = useSelectedproject(); - const projectsForDropdown = isDashboard + const projectsForDropdown = isDashboardPath ? projectNames - : projectNames?.filter(project => + : projectNames?.filter((project) => allowedProjectStatusIds.includes(project.projectStatusId) ); @@ -109,7 +122,9 @@ const Header = () => { const selectedProjectObj = projectNames.find( (p) => p?.id === selectedProject ); - currentProjectDisplayName = selectedProjectObj ? selectedProjectObj.name : "All Projects"; + currentProjectDisplayName = selectedProjectObj + ? selectedProjectObj.name + : "All Projects"; } } @@ -125,16 +140,17 @@ const Header = () => { if (projectNames.length === 1) { dispatch(setProjectId(projectNames[0]?.id || null)); } else { - if (isDashboard) { + if (isDashboardPath) { dispatch(setProjectId(null)); } else { - const firstAllowedProject = projectNames.find(project => allowedProjectStatusIds.includes(project.projectStatusId)); + const firstAllowedProject = projectNames.find((project) => + allowedProjectStatusIds.includes(project.projectStatusId) + ); dispatch(setProjectId(firstAllowedProject?.id || null)); } } } - }, [projectNames, selectedProject, dispatch, isDashboard]); - + }, [projectNames, selectedProject, dispatch, isDashboardPath]); const handler = useCallback( async (data) => { @@ -204,7 +220,7 @@ const Header = () => { className="navbar-nav-right d-flex align-items-center justify-content-between" id="navbar-collapse" > - {projectNames && !isDirectoryPath && ( + {showProjectDropdown(location.pathname) && (
    @@ -223,40 +239,42 @@ const Header = () => { )} - {shouldShowDropdown && projectsForDropdown && projectsForDropdown.length > 0 && ( -
      - {isDashboard && ( -
    • - -
    • - )} - {[...projectsForDropdown] - .sort((a, b) => a?.name?.localeCompare(b.name)) - .map((project) => ( -
    • + {shouldShowDropdown && + projectsForDropdown && + projectsForDropdown.length > 0 && ( +
        + {isDashboardPath && ( +
      • - ))} -
      - )} + )} + {[...projectsForDropdown] + .sort((a, b) => a?.name?.localeCompare(b.name)) + .map((project) => ( +
    • + +
    • + ))} +
    + )}
    )} @@ -457,4 +475,4 @@ const Header = () => { ); }; -export default Header; \ No newline at end of file +export default Header; diff --git a/src/components/common/DateRangePicker.jsx b/src/components/common/DateRangePicker.jsx index 1e95ef09..ce89b5e1 100644 --- a/src/components/common/DateRangePicker.jsx +++ b/src/components/common/DateRangePicker.jsx @@ -27,8 +27,7 @@ const DateRangePicker = ({ altInput: true, altFormat: "d-m-Y", defaultDate: [startDate, endDate], - static: false, - // appendTo: document.body, + static: true, clickOpens: true, maxDate: endDate, onChange: (selectedDates, dateStr) => { diff --git a/src/hooks/useFormattedDate.js b/src/hooks/useFormattedDate.js new file mode 100644 index 00000000..4c908865 --- /dev/null +++ b/src/hooks/useFormattedDate.js @@ -0,0 +1,26 @@ +import { format } from "date-fns"; + +/** + * A custom hook to format a date string, object, or number. + * @param {string | Date | number} date - The date to be formatted. + * @param {string} formatString - The format string (e.g., 'MMMM do, yyyy'). + * @returns {string} The formatted date string. + */ +const useFormattedDate = (date, formatString) => { + // Return early if no date is provided + if (!date) { + return ""; + } + + try { + const dateObj = new Date(date); + // You could also add error handling for invalid dates + // if (!isValid(dateObj)) return 'Invalid Date'; + return format(dateObj, formatString); + } catch (error) { + console.error("Error formatting date:", error); + return ""; // Return an empty string or a default value on error + } +}; + +export default useFormattedDate; diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js index d41fe34a..554af77e 100644 --- a/src/hooks/useProjects.js +++ b/src/hooks/useProjects.js @@ -96,7 +96,7 @@ export const useProjectDetails = (projectId, isAuto = true) => { export const useProjectsByEmployee = (employeeId) => { const { - data: projectNameList = [], + data: projectList = [], isLoading, error, refetch, @@ -117,6 +117,36 @@ export const useProjectsByEmployee = (employeeId) => { return { projectList, loading: isLoading, error, refetch }; }; +export const useProjectsAllocationByEmployee = (employeeId) => { + const { + data: projectList = [], + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["ProjectsAllocationByEmployee", employeeId], + queryFn: async () => { + const res = await ProjectRepository.getProjectsAllocationByEmployee( + employeeId + ); + const sortedProjects = [...res.data].sort((a, b) => + a.projectName.localeCompare(b.projectName) + ); + return sortedProjects || res; + + //return res.data || res; + }, + enabled: !!employeeId, + onError: (error) => { + showToast( + error.message || "Error while Fetching project Employee", + "error" + ); + }, + }); + return { projectList, loading: isLoading, error, refetch }; +}; + export const useProjectName = () => { const { data = [], @@ -176,6 +206,29 @@ export const useProjectTasks = (workAreaId, IsExpandedArea = false) => { return { ProjectTaskList, isLoading, error }; }; +export const useProjectTasksByEmployee = ( + employeeId, + fromDate, + toDate, +) => { + + return useQuery({ + queryKey: ["TasksByEmployee", employeeId], + queryFn: async () => { + const res = await ProjectRepository.getProjectTasksByEmployee( + employeeId, + fromDate, + toDate + ); + return res.data; + }, + enabled: !!employeeId && !!fromDate && !!toDate, + onError: (error) => { + showToast(error.message || "Error while Fetching project Tasks", "error"); + }, + }); +}; + // -- -------------Mutation------------------------------- export const useCreateProject = ({ onSuccessCallback }) => { diff --git a/src/pages/employee/EmployeeList.jsx b/src/pages/employee/EmployeeList.jsx index a7695c4d..86af49a9 100644 --- a/src/pages/employee/EmployeeList.jsx +++ b/src/pages/employee/EmployeeList.jsx @@ -51,7 +51,8 @@ const EmployeeList = () => { const { employees, loading, setLoading, error, recallEmployeeData } = useEmployeesAllOrByProjectId( - showAllEmployees ,selectedProjectId, + showAllEmployees, + selectedProjectId, showInactive ); @@ -153,8 +154,6 @@ const EmployeeList = () => { } }; - - const handleAllEmployeesToggle = (e) => { const isChecked = e.target.checked; setShowInactive(false); @@ -295,450 +294,449 @@ const EmployeeList = () => { {ViewTeamMember ? ( //
    -
    -
    -
    -
    - {/* Switches: All Employees + Inactive */} -
    - {/* All Employees Switch */} - {ViewAllEmployee && ( -
    - - -
    - )} - - {/* Show Inactive Employees Switch */} - {showAllEmployees && ( -
    - setShowInactive(e.target.checked)} - /> - -
    - )} -
    - - {/* Right side: Search + Export + Add Employee */} -
    - {/* Search Input - ALWAYS ENABLED */} -
    -
    +
    ) : ( + //
    diff --git a/src/pages/employee/EmployeeProfile.jsx b/src/pages/employee/EmployeeProfile.jsx index fd94c668..a17f4c21 100644 --- a/src/pages/employee/EmployeeProfile.jsx +++ b/src/pages/employee/EmployeeProfile.jsx @@ -1,26 +1,27 @@ import React, { useState, useEffect } from "react"; -import EmpProfile from "../../components/Employee/EmpProfile"; -import axios from "axios"; -import Breadcrumb from "../../components/common/Breadcrumb"; -import EmployeeNav from "../../components/Employee/EmployeeNav"; -import { useSearchParams, useParams } from "react-router-dom"; -import { getCachedData } from "../../slices/apiDataManager"; +import { useSearchParams, useParams, useNavigate } from "react-router-dom"; +import { useSelector } from "react-redux"; + import { useEmployeeProfile, useEmployees, useEmployeesByProject, } from "../../hooks/useEmployees"; import { useProfile } from "../../hooks/useProfile"; +import { getCachedData } from "../../slices/apiDataManager"; -import { useSelector } from "react-redux"; import EmployeeRepository from "../../repositories/EmployeeRepository"; import { ComingSoonPage } from "../Misc/ComingSoonPage"; -import { useNavigate } from "react-router-dom"; -import Avatar from "../../components/common/Avatar"; -import AttendancesEmployeeRecords from "./AttendancesEmployeeRecords"; + +import Breadcrumb from "../../components/common/Breadcrumb"; +import EmployeeNav from "../../components/Employee/EmployeeNav"; +import EmpProfile from "../../components/Employee/EmpProfile"; +import EmpAttendance from "../../components/Employee/EmpAttendance"; import ManageEmployee from "../../components/Employee/ManageEmployee"; -import { useChangePassword } from "../../components/Context/ChangePasswordContext"; -import GlobalModel from "../../components/common/GlobalModel"; +import EmpBanner from "../../components/Employee/EmpBanner"; +import EmpDashboard from "../../components/Employee/EmpDashboard"; +import EmpDocuments from "../../components/Employee/EmpDocuments"; +import EmpActivities from "../../components/Employee/EmpActivities"; const EmployeeProfile = () => { const { profile } = useProfile(); @@ -32,7 +33,7 @@ const EmployeeProfile = () => { const [SearchParams] = useSearchParams(); const tab = SearchParams.get("for"); - const [activePill, setActivePill] = useState(tab); + const [activePill, setActivePill] = useState(tab || "profile"); const [currentEmployee, setCurrentEmployee] = useState(); const [showModal, setShowModal] = useState(false); @@ -40,8 +41,6 @@ const EmployeeProfile = () => { setActivePill(pillKey); }; - - const fetchEmployeeProfile = async (employeeID) => { try { const resp = await EmployeeRepository.getEmployeeProfile(employeeID); @@ -62,17 +61,24 @@ const EmployeeProfile = () => { const renderContent = () => { if (loading) return
    Loading
    ; switch (activePill) { - case "attendance": { + case "profile": { return ( <> - + ); } - case "dcoument": { + case "attendance": { return ( <> - + + + ); + } + case "documents": { + return ( + <> + ); break; @@ -80,7 +86,7 @@ const EmployeeProfile = () => { case "activities": { return ( <> - + ); break; @@ -98,14 +104,8 @@ const EmployeeProfile = () => { if (loading) { return
    Loading...
    ; } - const { openChangePassword } = useChangePassword(); return ( <> - {showModal && ( - setShowModal(false)}> - setShowModal(false)} /> - - )}
    { { label: "Profile", link: null }, ]} > -
    -
    -
    -
    -
    -
    -
    -
    - -
    -

    {`${currentEmployee?.firstName} ${currentEmployee?.lastName}`}

    -
    -
    -
    -
    -
    - Employee Info -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Email: - {currentEmployee?.email || NA} -
    - Phone Number: - - {currentEmployee?.phoneNumber || NA} -
    - Emergency Contact Person: - - {currentEmployee?.emergencyContactPerson || NA} -
    - Emergency Contact Number: - - {currentEmployee?.emergencyPhoneNumber || NA} -
    - Gender: - - {currentEmployee?.gender || NA} -
    - Birth Date: - - {currentEmployee?.birthDate ? ( - new Date( - currentEmployee.birthDate - ).toLocaleDateString() - ) : ( - NA - )} -
    - Joining Date: - - {currentEmployee?.joiningDate ? ( - new Date( - currentEmployee.joiningDate - ).toLocaleDateString() - ) : ( - NA - )} -
    - Job Role: - - {currentEmployee?.jobRole || NA} -
    - Address: - - {currentEmployee?.currentAddress || NA} -
    -
    - - {currentEmployee?.id == profile?.employeeInfo?.id && ( - - )} -
    -
    -
    -
    -
    -
    +
    +
    - -
    -
    - -
    -
    -
    {renderContent()}
    -
    +
    {" "} +
    +
    +
    +
    +
    {renderContent()}
    +
    ); diff --git a/src/repositories/ProjectRepository.jsx b/src/repositories/ProjectRepository.jsx index 55f5d195..b2f0f7ff 100644 --- a/src/repositories/ProjectRepository.jsx +++ b/src/repositories/ProjectRepository.jsx @@ -6,32 +6,41 @@ const ProjectRepository = { api.get(`/api/project/details/${projetid}`), getProjectAllocation: (projetid) => - api.get( `api/project/allocation/${ projetid }` ), - - getEmployeesByProject:(projectId)=>api.get(`/api/Project/employees/get/${projectId}`), + api.get(`api/project/allocation/${projetid}`), + + getEmployeesByProject: (projectId) => + api.get(`/api/Project/employees/get/${projectId}`), manageProject: (data) => api.post("/api/project", data), // updateProject: (data) => api.post("/api/project/update", data), - manageProjectAllocation: ( data ) => api.post( "/api/project/allocation", data ), - + manageProjectAllocation: (data) => api.post("/api/project/allocation", data), + manageProjectInfra: (data) => api.post("/api/project/manage-infra", data), - manageProjectTasks: ( data ) => api.post( "/api/project/task", data ), - deleteProjectTask:(id)=> api.delete(`/api/project/task/${id}`), + manageProjectTasks: (data) => api.post("/api/project/task", data), + deleteProjectTask: (id) => api.delete(`/api/project/task/${id}`), updateProject: (id, data) => api.put(`/api/project/update/${id}`, data), - deleteProject: ( id ) => api.delete( `/projects/${ id }` ), - getProjectsByEmployee: ( id ) => api.get( `/api/project/assigned-projects/${ id }` ), - updateProjectsByEmployee:(id,data)=>api.post(`/api/project/assign-projects/${id}`,data), - projectNameList: () => api.get( "/api/project/list/basic" ), - - getProjectDetails:(id)=>api.get(`/api/project/details/${id}`), - getProjectInfraByproject: ( id ) => api.get( `/api/project/infra-details/${ id }` ), - getProjectTasksByWorkArea:(id)=>api.get(`/api/project/tasks/${id}`) + deleteProject: (id) => api.delete(`/projects/${id}`), + getProjectsByEmployee: (id) => + api.get(`/api/project/assigned-projects/${id}`), + getProjectsAllocationByEmployee: (id) => + api.get(`/api/project/allocation-histery/${id}`), + updateProjectsByEmployee: (id, data) => + api.post(`/api/project/assign-projects/${id}`, data), + projectNameList: () => api.get("/api/project/list/basic"), + + getProjectDetails: (id) => api.get(`/api/project/details/${id}`), + getProjectInfraByproject: (id) => api.get(`/api/project/infra-details/${id}`), + getProjectTasksByWorkArea: (id) => api.get(`/api/project/tasks/${id}`), + getProjectTasksByEmployee: (id, fromDate, toDate) => + api.get( + `/api/project/tasks-employee/${id}?fromDate=${fromDate}&toDate=${toDate}` + ), }; export const TasksRepository = { - assignTask: ( data ) => api.post( "/api/task/assign", data ), + assignTask: (data) => api.post("/api/task/assign", data), // reportTask:(data)=>api.post("/api/task/report",data) -} +}; export default ProjectRepository;