diff --git a/health/micro-ui/web/micro-ui-internals/example/package.json b/health/micro-ui/web/micro-ui-internals/example/package.json index 71f104204fa..5f524e73f46 100644 --- a/health/micro-ui/web/micro-ui-internals/example/package.json +++ b/health/micro-ui/web/micro-ui-internals/example/package.json @@ -16,7 +16,7 @@ "@egovernments/digit-ui-module-utilities": "1.0.3", "@egovernments/digit-ui-react-components": "1.8.12", "@egovernments/digit-ui-module-hcmworkbench": "0.1.0", - "@egovernments/digit-ui-module-campaign-manager": "0.3.0", + "@egovernments/digit-ui-module-campaign-manager": "0.4.0", "@egovernments/digit-ui-module-microplan": "0.0.1", "@egovernments/digit-ui-module-health-payments": "0.0.1", "http-proxy-middleware": "^1.0.5", diff --git a/health/micro-ui/web/micro-ui-internals/example/src/setupProxy.js b/health/micro-ui/web/micro-ui-internals/example/src/setupProxy.js index 6f35778c42a..ef0a76bdc24 100644 --- a/health/micro-ui/web/micro-ui-internals/example/src/setupProxy.js +++ b/health/micro-ui/web/micro-ui-internals/example/src/setupProxy.js @@ -97,7 +97,9 @@ module.exports = function (app) { "/plan-service", "/health-project", "/service-request", - "/census-service" + "/census-service", + "/health-attendance", + "/health-individual" ].forEach((location) => app.use(location, createProxy)); ["/pb-egov-assets"].forEach((location) => app.use(location, assetsProxy)); ["/mdms-v2/v2/_create"].forEach((location) => app.use(location, mdmsProxy)); diff --git a/health/micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/campaign.scss b/health/micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/campaign.scss index 8a5dc65c2a0..d01ad1d128b 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/campaign.scss +++ b/health/micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/campaign.scss @@ -236,4 +236,55 @@ > div:nth-of-type(1) { width: 69%; } -} \ No newline at end of file +} + +.attendanceCell { + display: flex; +} + +.chip-container { + display: flex; + align-items: center; + gap: 0.5rem; /* Adjust the spacing between chips and the button */ +} + +.chip-wrapper { + display: inline-flex; +} + +.more-button { + margin-left: 0.5rem; /* Adjust the spacing between chips and the + button */ +} + +/* Container for the progress bar */ +.progress-bar-container { + position: relative; + width: 20rem; + max-width: 20rem; + height: 20px; + background-color: #f3f3f3; + border-radius: 10px; + overflow: hidden; + border: 1px solid #d6d5d4; +} + +/* The fill of the progress bar */ +.progress-bar-fill { + height: 100%; + background-color: #2BD27F; /* Use your PRIMARY_COLOR */ + transition: width 0.4s ease-in-out; + display: flex; + align-items: center; + justify-content: center; +} + +/* Text inside the progress bar */ +.progress-bar-text { + position: absolute; + width: 100%; + text-align: center; + font-size: 14px; + color: #fff; + font-weight: bold; + pointer-events: none; +} diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/package.json b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/package.json index 9c9faa70b01..36ec79ba0ce 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/package.json +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/package.json @@ -1,6 +1,6 @@ { "name": "@egovernments/digit-ui-module-campaign-manager", - "version": "0.3.0", + "version": "0.4.0", "description": "Campaign", "main": "dist/index.js", "module": "dist/index.modern.js", diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/Module.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/Module.js index aa986b565db..af8099e913c 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/Module.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/Module.js @@ -57,6 +57,9 @@ import MapView from "./components/MapView"; import NoResultsFound from "./components/NoResultsFound"; import UploadDataMappingWrapper from "./components/UploadDataMappingWrapper"; import DataUploadWrapper from "./components/DataUploadWrapper"; +import Attendance from "./pages/employee/Attendance"; +import IndividualUserTable from "./components/IndividualUserTable"; +import ProgressBar from "./components/ProgressBar"; /** * MDMS Module name @@ -175,6 +178,9 @@ const componentsToRegister = { NoResultsFound, UploadDataMappingWrapper, DataUploadWrapper, + Attendance, + IndividualUserTable, + ProgressBar }; const overrideHooks = () => { diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/IndividualUserTable.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/IndividualUserTable.js new file mode 100644 index 00000000000..b43d684c1d7 --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/IndividualUserTable.js @@ -0,0 +1,257 @@ +import React, { useState, useCallback, useEffect } from 'react'; +import { Card, Button, PopUp, Loader, Chip, TextInput } from "@egovernments/digit-ui-components"; +import DataTable from "react-data-table-component"; +import { CustomSVG } from "@egovernments/digit-ui-components"; +import { tableCustomStyle } from "./tableCustomStyle"; +import { useTranslation } from "react-i18next"; +import ProgressBar from './ProgressBar'; + +const SupervisorPopup = ({ setShowPopUp, supervisors }) => { + const { t } = useTranslation(); + return ( + setShowPopUp(false)} + onClose={() => setShowPopUp(false)} + > +
+ {supervisors.map((supervisor, index) => ( + + ))} +
+
+ ); +}; + +const IndividualUserTable = ({ tenantId, staffAttendeeIds = [], supervisorName = "" }) => { + const { t } = useTranslation(); + const [totalRows, setTotalRows] = useState(0); + const [currentPage, setCurrentPage] = useState(1); + const [rowsPerPage, setRowsPerPage] = useState(5); + const [searchQuery, setSearchQuery] = useState(''); + const [paginatedData, setPaginatedData] = useState([]); + const [chipPopUpRowId, setChipPopUpRowId] = useState(null); + + const individualUsers = { + url: `/health-individual/v1/_search`, + params: { + tenantId: tenantId, + limit: 1000, + offset: 0 + }, + body: { + Individual: {}, + }, + }; + + const { isLoading, data, isFetching } = Digit.Hooks.useCustomAPIHook(individualUsers); + + const getDisplayData = useCallback(() => { + if (!data?.Individual) return []; + + const filteredData = data.Individual.filter(user => { + const userName = user.name?.givenName?.toLowerCase() || ''; + return userName.includes(searchQuery.toLowerCase()); + }).map(user => ({ + ...user, + isAssigned: staffAttendeeIds.includes(user.id) + })); + + setTotalRows(filteredData.length); + + const startIndex = (currentPage - 1) * rowsPerPage; + const endIndex = startIndex + rowsPerPage; + return filteredData.slice(startIndex, endIndex); + }, [data, searchQuery, staffAttendeeIds, currentPage, rowsPerPage]); + + useEffect(() => { + if (data?.Individual) { + setPaginatedData(getDisplayData()); + } + }, [data, searchQuery, currentPage, rowsPerPage, getDisplayData]); + + const handlePaginationChange = (page) => { + setCurrentPage(page); + }; + + const handleRowsPerPageChange = (newPerPage) => { + setRowsPerPage(newPerPage); + setCurrentPage(1); + }; + + const handleSearchChange = (e) => { + setSearchQuery(e.target.value); + setCurrentPage(1); + }; + + const handleAssign = (userId) => { + //code to be written + }; + + const handleUnassign = (userId) => { + //code to be written + }; + + const columns = [ + { + name: t('Name'), + selector: row => row.name?.givenName || '', + sortable: true, + }, + { + name: t('Mobile Number'), + selector: row => row.mobileNumber || '', + sortable: true, + }, + { + name: t("USER_TYPE"), + selector: row => row?.userDetails?.type || '', + sortable: true + }, + { + name: t('Assigned Supervisor'), + selector: row => row.isAssigned ? supervisorName : t('Not Assigned'), + sortable: true, + grow: 2, + cell: row => { + const supervisors = row.isAssigned ? [supervisorName] : []; + + return ( +
+ {supervisors.length > 0 ? ( +
+ {supervisors.slice(0, 2).map((supervisor, index) => ( +
+ +
+ ))} + + {supervisors.length > 2 && ( +
+ ) : ( + {t('Not Assigned')} + )} +
+ ); + }, + }, + { + name: t('Actions'), + cell: row => ( +
+ {row.isAssigned ? ( +
+ ) + }, + ]; + + return ( +
+ {isLoading && } + {!isLoading && ( +
+ +
+
{t("USER_NAME")}
+ +
+
+ + } + pagination + paginationServer + customStyles={tableCustomStyle} + paginationTotalRows={totalRows} + onChangePage={handlePaginationChange} + onChangeRowsPerPage={handleRowsPerPageChange} + paginationPerPage={rowsPerPage} + sortIcon={} + paginationRowsPerPageOptions={[5, 10, 15, 20]} + noDataComponent={
{t('No records found')}
} + /> + + {/* Progress Bar Section */} +
+
+ {t("REGISTER_ASSIGNED_USERS")} + + {`${40}%`} +
+
+
+ )} +
+ ); + +}; + +export default IndividualUserTable; \ No newline at end of file diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/ProgressBar.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/ProgressBar.js new file mode 100644 index 00000000000..a86735f9016 --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/ProgressBar.js @@ -0,0 +1,15 @@ +import React from 'react'; + +const ProgressBar = ({ amount, total }) => { + const percentage = Math.min((amount / total) * 100, 100); + + return ( +
+
+ {`${percentage.toFixed(1)}%`} +
+
+ ); +}; + +export default ProgressBar; diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/tableCustomStyle.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/tableCustomStyle.js index 380ef998802..d615f538d1c 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/tableCustomStyle.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/tableCustomStyle.js @@ -1,28 +1,15 @@ export const tableCustomStyle = { tableWrapper: { - style: { - overflow: "visible", - - // overflow: "scroll", - }, + style: {}, }, table: { - style: { - overflow: "visible", - // overflow: "scroll", - }, + style: {}, }, responsiveWrapper: { - style: { - overflow: "visible", - // overflow: "scroll", - }, + style: {}, }, contextMenu: { - style: { - overflow: "visible", - // overflow: "scroll", - }, + style: {}, }, header: { style: { @@ -35,33 +22,17 @@ export const tableCustomStyle = { "&:hover": { backgroundColor: "#FBEEE8", }, + borderBottom: "1px solid #D6D5D4", // Horizontal lines between rows }, }, headRow: { style: { - borderTopStyle: "solid", - borderTopWidth: "1px", - borderTopColor: "#D6D5D4", + borderBottom: "1px solid #D6D5D4", backgroundColor: "#EEEEEE", }, }, headCells: { style: { - "&:first-of-type": { - borderLeftStyle: "solid", - borderLeftWidth: "1px", - borderLeftColor: "#D6D5D4", - borderTopLeftRadius: "0.25rem", - }, - "&:last-of-type": { - borderLeftStyle: "solid", - borderLeftWidth: "1px", - borderLeftColor: "#D6D5D4", - borderTopRightRadius: "0.25rem", - }, - borderRightStyle: "solid", - borderRightWidth: "1px", - borderRightColor: "#D6D5D4", fontFamily: "Roboto", fontWeight: "700", fontStyle: "normal", @@ -69,19 +40,12 @@ export const tableCustomStyle = { color: "#0B4B66", padding: "16px", lineHeight: "1.14rem", - zIndex: 10, + borderBottom: "1px solid #D6D5D4", // Only horizontal lines in header + borderRight: "none", // Remove vertical lines }, }, cells: { style: { - "&:first-of-type": { - borderLeftStyle: "solid", - borderLeftWidth: "1px", - borderLeftColor: "#D6D5D4", - }, - borderRightStyle: "solid", - borderRightWidth: "1px", - borderRightColor: "#D6D5D4", color: "#363636", fontFamily: "Roboto", fontStyle: "normal", @@ -90,6 +54,8 @@ export const tableCustomStyle = { textAlign: "left", fontSize: "16px", padding: "16px", + borderBottom: "1px solid #D6D5D4", // Horizontal lines between rows + borderRight: "none", // Remove vertical lines }, pagination: { style: { @@ -103,6 +69,7 @@ export const tableCustomStyle = { }, }; + export const getTableCustomStyle = (freezeFirstColumn = false) => ({ tableWrapper: { style: { diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/configs/UICustomizations.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/configs/UICustomizations.js index f4a1e430249..d40e7e01797 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/configs/UICustomizations.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/configs/UICustomizations.js @@ -464,6 +464,19 @@ export const UICustomizations = { const navEvent1 = new PopStateEvent("popstate"); window.dispatchEvent(navEvent1); break; + case "ACTION_LABEL_CONFIGURE_REGISTER": + window.history.pushState( + { + name: row?.campaignName, + data: row, + projectId: row?.projectId, + }, + "", + `/${window.contextPath}/employee/campaign/attendance` + ); + const navEvent2 = new PopStateEvent("popstate"); + window.dispatchEvent(navEvent2); + break; case "ACTION_LABEL_UPDATE_BOUNDARY_DETAILS": window.history.pushState( @@ -509,6 +522,7 @@ export const UICustomizations = { ...(row?.status === "created" ? [{ key: 1, code: "ACTION_LABEL_UPDATE_DATES", i18nKey: t("ACTION_LABEL_UPDATE_DATES") }] : []), { key: 2, code: "ACTION_LABEL_CONFIGURE_APP", i18nKey: t("ACTION_LABEL_CONFIGURE_APP") }, { key: 3, code: "ACTION_LABEL_VIEW_TIMELINE", i18nKey: t("ACTION_LABEL_VIEW_TIMELINE") }, + { key: 4, code: "ACTION_LABEL_CONFIGURE_REGISTER", i18nKey: t("ACTION_LABEL_CONFIGURE_REGISTER") }, ...(row?.status === "created" ? [{ key: 1, code: "ACTION_LABEL_UPDATE_BOUNDARY_DETAILS", i18nKey: t("ACTION_LABEL_UPDATE_BOUNDARY_DETAILS") }] : []), @@ -799,6 +813,20 @@ export const UICustomizations = { window.dispatchEvent(navEvent1); break; + case "ACTION_LABEL_CONFIGURE_REGISTER": + window.history.pushState( + { + name: row?.campaignName, + data: row, + projectId: row?.projectId, + }, + "", + `/${window.contextPath}/employee/campaign/attendance` + ); + const navEvent2 = new PopStateEvent("popstate"); + window.dispatchEvent(navEvent2); + break; + default: console.log(value); break; @@ -831,6 +859,7 @@ export const UICustomizations = { ...(row?.status === "created" ? [{ key: 1, code: "ACTION_LABEL_UPDATE_DATES", i18nKey: t("ACTION_LABEL_UPDATE_DATES") }] : []), { key: 2, code: "ACTION_LABEL_CONFIGURE_APP", i18nKey: t("ACTION_LABEL_CONFIGURE_APP") }, { key: 3, code: "ACTION_LABEL_VIEW_TIMELINE", i18nKey: t("ACTION_LABEL_VIEW_TIMELINE") }, + { key: 4, code: "ACTION_LABEL_CONFIGURE_REGISTER", i18nKey: t("ACTION_LABEL_CONFIGURE_REGISTER") }, ...(row?.status === "created" ? [{ key: 1, code: "ACTION_LABEL_UPDATE_BOUNDARY_DETAILS", i18nKey: t("ACTION_LABEL_UPDATE_BOUNDARY_DETAILS") }] : []), diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/hooks/index.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/hooks/index.js index da35cdc455a..bae573fa6e7 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/hooks/index.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/hooks/index.js @@ -19,6 +19,7 @@ import useMDMSServiceSearch from "./useMDMSServiceSearch"; import useBoundaryHome from "./useBoundaryHome"; import useFetchFromMicroplan from "./useFetchFromMicroplan"; import { useReadExcelData, useUpdateAndUploadExcel } from "./useReadExcelData"; +import useAttendanceWithUserDetails from "./useAttendanceWithUserDetails"; const UserService = {}; @@ -48,6 +49,7 @@ const campaign = { useFetchFromMicroplan, useReadExcelData, useUpdateAndUploadExcel, + useAttendanceWithUserDetails }; const Hooks = { diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/hooks/services/searchAttendanceWithUserDetails.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/hooks/services/searchAttendanceWithUserDetails.js new file mode 100644 index 00000000000..bdaf801a629 --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/hooks/services/searchAttendanceWithUserDetails.js @@ -0,0 +1,161 @@ +const searchAttendanceWithUserDetails = async ({ tenantId, limit, offset }) => { + try { + + // First API call to get attendance register + const registerResponse = await Digit.CustomService.getResponse({ + url: "/health-attendance/v1/_search", + method: "POST", + params: { tenantId, limit, offset }, + body: { + attendanceRegister: { + tenantId, + }, + }, + }); + + if (!registerResponse?.attendanceRegister?.length) { + return { + registers: [], + staffAttendeeMap: {}, + attendeeDetailsMap: {}, + userDetails: [], + isError: false + }; + } + + // Create maps and collect IDs + const staffAttendeeMap = {}; + const individualIds = new Set(); + const staffUserIds = new Set(); + + // Populate maps and collect IDs + registerResponse.attendanceRegister.forEach((register) => { + const staffMember = register.staff?.[0]; + if (staffMember?.userId) { + // Collect staff user IDs + staffUserIds.add(staffMember.userId); + + // Map staff ID to their attendees + staffAttendeeMap[staffMember.userId] = register.attendees || []; + + // Collect all individual IDs from attendees + register.attendees?.forEach(attendee => { + if (attendee.individualId) { + individualIds.add(attendee.individualId); + } + }); + } + }); + + // If no IDs found, return early + if (individualIds.size === 0 && staffUserIds.size === 0) { + return { + registers: registerResponse.attendanceRegister, + staffAttendeeMap, + attendeeDetailsMap: {}, + userDetails: [], + isError: false + }; + } + + // Get staff user details + const staffResponse = await Digit.CustomService.getResponse({ + url: "/health-individual/v1/_search", + method: "POST", + params: { + limit: 1000, + offset: 0, + tenantId, + }, + body: { + Individual: { + id: Array.from(staffUserIds) + }, + }, + }); + + + // Get attendee details + const individualResponse = await Digit.CustomService.getResponse({ + url: "/health-individual/v1/_search", + method: "POST", + params: { + limit: 1000, + offset: 0, + tenantId, + }, + body: { + Individual: { + id: Array.from(individualIds) + }, + }, + }); + + // Create map of staff details + const userDetails = staffResponse?.Individual.map(user => { + // Find the register that contains this staff member + const registerWithStaff = registerResponse.attendanceRegister.find( + register => register.staff?.[0]?.userId === user.id + ); + + return { + id: user.id, + name: user?.name?.givenName, + dob: user.dateOfBirth, + email: user?.email, + mobileNumber: user?.mobileNumber, + registerId: registerWithStaff?.staff?.[0]?.registerId || null // Add registerId here + }; + }); + + // Create map of individual details + const attendeeDetailsMap = {}; + individualResponse?.Individual.forEach(individual => { + attendeeDetailsMap[individual.id] = { + name: individual?.name?.givenName, + dob: individual.dateOfBirth, + email: individual?.email, + mobileNumber: individual?.mobileNumber, + }; + }); + + // Create a map for quick lookup of staff details + const staffDetailsMap = {}; + userDetails.forEach(user => { + staffDetailsMap[user.id] = user; + }); + + // Enhance staffAttendeeMap with both staff and individual details + Object.keys(staffAttendeeMap).forEach(staffId => { + const enhancedStaffData = { + staffDetails: staffDetailsMap[staffId] || null, + attendees: staffAttendeeMap[staffId].map(attendee => ({ + ...attendee, + individualDetails: attendeeDetailsMap[attendee.individualId] || null + })) + }; + staffAttendeeMap[staffId] = enhancedStaffData; + }); + + return { + registers: registerResponse.attendanceRegister, + staffAttendeeMap, + attendeeDetailsMap, + userDetails, + isError: false, + }; + + } catch (error) { + console.error("Error in searchAttendanceWithUserDetails:", error); + return { + registers: [], + staffAttendeeMap: {}, + attendeeDetailsMap: {}, + userDetails: [], + isError: true, + error: error.message || "An unknown error occurred", + }; + } + }; + + export default searchAttendanceWithUserDetails; diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/hooks/useAttendanceWithUserDetails.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/hooks/useAttendanceWithUserDetails.js new file mode 100644 index 00000000000..3ffb310682e --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/hooks/useAttendanceWithUserDetails.js @@ -0,0 +1,17 @@ +// hooks/useAttendanceWithUserDetails.js +import { useQuery } from "react-query"; +import searchAttendanceWithUserDetails from "../hooks/services/searchAttendanceWithUserDetails"; + +const useAttendanceWithUserDetails = ({ tenantId, limit, offset, config = {} }) => { + console.log("hook eorking"); + return useQuery( + ["ATTENDANCE_WITH_USER_DETAILS", tenantId, limit, offset, config?.queryKey], + () => searchAttendanceWithUserDetails({ tenantId, limit, offset }), + { + enabled: true, + ...config, + } + ); +}; + +export default useAttendanceWithUserDetails; \ No newline at end of file diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/Attendance.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/Attendance.js new file mode 100644 index 00000000000..800f6314351 --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/Attendance.js @@ -0,0 +1,339 @@ +import { Card, Button, PopUp, Stepper, Loader, Chip, TextInput } from "@egovernments/digit-ui-components"; +import React, { Fragment, useState, useEffect, useCallback } from "react"; +import { useTranslation } from "react-i18next"; +import { useHistory } from "react-router-dom"; +import DataTable from "react-data-table-component"; +import { CustomSVG } from "@egovernments/digit-ui-components"; +import useAttendanceWithUserDetails from "../../hooks/useAttendanceWithUserDetails"; +import IndividualUserTable from "../../components/IndividualUserTable"; +import { tableCustomStyle } from "../../components/tableCustomStyle"; +import ProgressBar from "../../components/ProgressBar"; + +const Wrapper = ({ setShowPopUp, alreadyQueuedSelectedState }) => { + const { t } = useTranslation(); + return ( + setShowPopUp(false)} + onClose={() => setShowPopUp(false)} + > +
+ {alreadyQueuedSelectedState?.map((item, index) => ( + + ))} +
+
+ ); +}; + +const Attendance = () => { + const { t } = useTranslation(); + const history = useHistory(); + const tenantId = Digit.ULBService.getCurrentTenantId(); + const [showToast, setShowToast] = useState(null); + const [searchQuery, setSearchQuery] = useState(''); + const [totalRows, setTotalRows] = useState(0); + const [currentPage, setCurrentPage] = useState(1); + const [rowsPerPage, setRowsPerPage] = useState(5); + const [paginatedData, setPaginatedData] = useState([]); + const [filteredData, setFilteredData] = useState([]); + const [assignUsersPopup, setAssignUsersPopup] = useState(false); + const [curAttendeesList, setCurAttendeesList] = useState([]); + const [curSupervisor, setCurSupervisor] = useState(""); + const [curRegisterId, setCurRegisterId] = useState(""); + const [chipPopUp, setChipPopUp] = useState(null); + const [chipPopUpRowId, setChipPopUpRowId] = useState(null); + const [currentStep, setCurrentStep] = useState(3); + const [active, setActive] = useState(3); + + const { isLoading, data: userDetails, refetch: refetchAttendance } = useAttendanceWithUserDetails({ + tenantId, + limit: 100, + offset: 0, + config: { enabled: true } + }); + + // Filter and paginate data + useEffect(() => { + if (userDetails?.userDetails) { + const filtered = searchQuery.trim() === '' + ? userDetails.userDetails + : userDetails.userDetails.filter(user => + user.name?.toLowerCase()?.includes(searchQuery.toLowerCase()) + ); + + setFilteredData(filtered); + setTotalRows(filtered.length); + setCurrentPage(1); + } + }, [userDetails, searchQuery]); + + // Handle pagination + useEffect(() => { + if (filteredData.length > 0) { + const startIndex = (currentPage - 1) * rowsPerPage; + const endIndex = startIndex + rowsPerPage; + setPaginatedData(filteredData.slice(startIndex, endIndex)); + } + }, [filteredData, currentPage, rowsPerPage]); + + const handlePaginationChange = (page) => { + setCurrentPage(page); + }; + + const handleRowsPerPageChange = (newPerPage) => { + setRowsPerPage(newPerPage); + setCurrentPage(1); + }; + + const handleSearchChange = (e) => { + setSearchQuery(e.target.value); + }; + + const columns = [ + { + name: t("NAME"), + selector: (row) => { + return ( +
+ {row.name || t("NA")} +
+ ); + }, + sortable: true, + sortFunction: (rowA, rowB) => { + const nameA = t(rowA.name).toLowerCase(); + const nameB = t(rowB.name).toLowerCase(); + if (nameA < nameB) return -1; + if (nameA > nameB) return 1; + return 0; + }, + }, + { + name: t("EMAIL"), + selector: (row) => { + return ( +
+ {row.email || t("NA")} +
+ ); + }, + sortable: false, + }, + { + name: t("CONTACT_NUMBER"), + selector: (row) => { + return ( +
+ {row?.mobileNumber || t("NA")} +
+ ); + }, + sortable: true, + sortFunction: (rowA, rowB) => { + const numberA = parseInt(rowA.number, 10); + const numberB = parseInt(rowB.number, 10); + if (isNaN(numberA)) return 1; // Treat invalid numbers as larger + if (isNaN(numberB)) return -1; + + if (numberA < numberB) return -1; + if (numberA > numberB) return 1; + return 0; + }, + }, + { + name: t("USERS_ASSIGNED"), + sortable: false, + grow: 2, + cell: (row) => { + return ( +
+ {userDetails?.staffAttendeeMap[row?.id]?.attendees.length > 0 && ( +
+
+ {userDetails?.staffAttendeeMap[row?.id]?.attendees.slice(0, 2).map((item, index) => ( +
+ +
+ ))} + {userDetails?.staffAttendeeMap[row?.id]?.attendees.length > 2 && ( +
+ {chipPopUpRowId === row.id && ( + + )} +
+ )} + {userDetails?.staffAttendeeMap[row?.id]?.attendees.length === 0 && ( +
+ {t("USERS_NOT_ASSIGNED")} +
+ )} +
+ + ); + }, + }, + { + name: t("ACTION"), + sortable: false, + cell: (row) => { + return ( +