Merge pull request 'HotChanges_10_10_25' (#472) from HotChanges_10_10_25 into main
Reviewed-on: #472 Merged
This commit is contained in:
commit
9b8c8c34ab
@ -234,7 +234,7 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
onRangeChange={setDateRange}
|
||||
defaultStartDate={yesterday}
|
||||
/>
|
||||
<div className="form-check form-switch text-start m-0 ms-5">
|
||||
<div className="form-check form-switch text-start ms-1 ms-md-2 align-items-center mb-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
|
@ -7,16 +7,30 @@ import { useRegularizationRequests } from "../../hooks/useAttendance";
|
||||
import moment from "moment";
|
||||
import usePagination from "../../hooks/usePagination";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import { cacheData, clearCacheKey, useSelectedProject } from "../../slices/apiDataManager";
|
||||
import {
|
||||
cacheData,
|
||||
clearCacheKey,
|
||||
useSelectedProject,
|
||||
} from "../../slices/apiDataManager";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import Pagination from "../../components/common/Pagination";
|
||||
|
||||
const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, IncludeInActive }) => {
|
||||
const Regularization = ({
|
||||
handleRequest,
|
||||
searchTerm,
|
||||
projectId,
|
||||
organizationId,
|
||||
IncludeInActive,
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
// var selectedProject = useSelector((store) => store.localVariables.projectId);
|
||||
const selectedProject = useSelectedProject();
|
||||
const [regularizesList, setregularizedList] = useState([]);
|
||||
const { regularizes, loading, error, refetch } =
|
||||
useRegularizationRequests(selectedProject, organizationId, IncludeInActive);
|
||||
const { regularizes, loading, error, refetch } = useRegularizationRequests(
|
||||
selectedProject,
|
||||
organizationId,
|
||||
IncludeInActive
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setregularizedList(regularizes);
|
||||
@ -54,48 +68,15 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
|
||||
}
|
||||
const lowercasedSearchTerm = searchTerm.toLowerCase();
|
||||
return sortedList.filter((item) => {
|
||||
const fullName = `${item.firstName} ${item.lastName}`.toLowerCase();
|
||||
const fullName = `${item?.firstName} ${item?.lastName}`.toLowerCase();
|
||||
return fullName.includes(lowercasedSearchTerm);
|
||||
});
|
||||
}, [regularizesList, searchTerm]);
|
||||
|
||||
// const filteredSearchData = useMemo(() => {
|
||||
// let sortedList = [...regularizesList].sort(sortByName);
|
||||
|
||||
// // Search filter
|
||||
// if (searchTerm) {
|
||||
// const lowercasedSearchTerm = searchTerm.toLowerCase();
|
||||
// sortedList = sortedList.filter((item) => {
|
||||
// const fullName = `${item.firstName} ${item.lastName}`.toLowerCase();
|
||||
// return fullName.includes(lowercasedSearchTerm);
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Organization filter
|
||||
// if (filters?.selectedOrganization) {
|
||||
// sortedList = sortedList.filter(
|
||||
// (item) => item.organization?.name === filters.selectedOrganization
|
||||
// );
|
||||
// }
|
||||
|
||||
// // Services filter
|
||||
// if (filters?.selectedServices?.length > 0) {
|
||||
// sortedList = sortedList.filter((item) =>
|
||||
// filters.selectedServices.includes(item.service?.name)
|
||||
// );
|
||||
// }
|
||||
|
||||
// return sortedList;
|
||||
// }, [regularizesList, searchTerm, filters]);
|
||||
|
||||
|
||||
const { currentPage, totalPages, currentItems, paginate } =
|
||||
usePagination(filteredSearchData, 20);
|
||||
|
||||
// Reset pagination when the search term or data changes
|
||||
useEffect(() => {
|
||||
|
||||
}, [filteredSearchData]);
|
||||
const { currentPage, totalPages, currentItems, paginate } = usePagination(
|
||||
filteredSearchData,
|
||||
20
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
eventBus.on("regularization", handler);
|
||||
@ -117,9 +98,15 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
|
||||
}, [employeeHandler]);
|
||||
|
||||
return (
|
||||
<div className="table-responsive text-nowrap pb-4" style={{ minHeight: "200px" }}>
|
||||
<div
|
||||
className="table-responsive text-nowrap pb-4"
|
||||
style={{ minHeight: "200px" }}
|
||||
>
|
||||
{loading ? (
|
||||
<div className="d-flex justify-content-center align-items-center" style={{ height: "200px" }}>
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ height: "200px" }}
|
||||
>
|
||||
<p className="text-secondary">Loading...</p>
|
||||
</div>
|
||||
) : currentItems?.length > 0 ? (
|
||||
@ -143,10 +130,7 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
|
||||
<tr key={index}>
|
||||
<td colSpan={2}>
|
||||
<div className="d-flex justify-content-start align-items-center">
|
||||
<Avatar
|
||||
firstName={att.firstName}
|
||||
lastName={att.lastName}
|
||||
/>
|
||||
<Avatar firstName={att.firstName} lastName={att.lastName} />
|
||||
<div className="d-flex flex-column">
|
||||
<a href="#" className="text-heading text-truncate">
|
||||
<span className="fw-normal">
|
||||
@ -175,7 +159,6 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
) : (
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
@ -188,7 +171,7 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{!loading && totalPages > 1 && (
|
||||
{/* {!loading && totalPages > 1 && (
|
||||
<nav aria-label="Page ">
|
||||
<ul className="pagination pagination-sm justify-content-end py-1 mt-3">
|
||||
<li className={`page-item ${currentPage === 1 ? "disabled" : ""}`}>
|
||||
@ -226,6 +209,14 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
)} */}
|
||||
|
||||
{totalPages > 0 && (
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
onPageChange={paginate}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -55,6 +55,7 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
||||
endField="dateTo"
|
||||
resetSignal={resetKey}
|
||||
defaultRange={false}
|
||||
maxDate={new Date()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -192,7 +192,7 @@ const TaskReportList = () => {
|
||||
if (isLoading) return <TaskReportListSkeleton />;
|
||||
if (isError) return <div>Loading....</div>;
|
||||
return (
|
||||
<div className="mt-2">
|
||||
<div className="mt-2 table-responsive text-nowrap">
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -179,7 +179,7 @@ const ListViewContact = ({ data, Pagination }) => {
|
||||
<tr style={{ height: "200px" }}>
|
||||
<td
|
||||
colSpan={contactList.length + 1}
|
||||
className="text-center align-middle"
|
||||
className="text-center border-0 align-middle"
|
||||
>
|
||||
No contacts found
|
||||
</td>
|
||||
|
@ -18,7 +18,7 @@ import { defatEmployeeObj, employeeSchema } from "./EmployeeSchema";
|
||||
import { useOrganizationsList } from "../../hooks/useOrganization";
|
||||
import { ITEMS_PER_PAGE } from "../../utils/constants";
|
||||
|
||||
const ManageEmployee = ({ employeeId, onClosed, IsAllEmployee }) => {
|
||||
const ManageEmployee = ({ employeeId, onClosed }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { mutate: updateEmployee, isPending } = useUpdateEmployee();
|
||||
const {
|
||||
@ -72,7 +72,7 @@ const ManageEmployee = ({ employeeId, onClosed, IsAllEmployee }) => {
|
||||
data.email = null;
|
||||
}
|
||||
|
||||
const payload = { ...data, IsAllEmployee };
|
||||
const payload = { ...data };
|
||||
|
||||
if (employeeId) {
|
||||
payload.id = employeeId;
|
||||
|
@ -117,6 +117,7 @@ const ExpenseFilterPanel = ({ onApply, handleGroupBy }) => {
|
||||
endField="endDate"
|
||||
resetSignal={resetKey}
|
||||
defaultRange={false}
|
||||
maxDate={new Date()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -189,6 +189,7 @@ const Header = () => {
|
||||
className="navbar-nav-right d-flex align-items-center justify-content-between"
|
||||
id="navbar-collapse"
|
||||
>
|
||||
<div className="d-flex align-items-center">
|
||||
{showProjectDropdown(location.pathname) && (
|
||||
<div className="align-items-center">
|
||||
<i className="rounded-circle bx bx-building-house bx-sm-lg bx-md me-2"></i>
|
||||
@ -247,6 +248,7 @@ const Header = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ul className="navbar-nav flex-row align-items-center ms-md-auto">
|
||||
<li className="nav-item dropdown-shortcuts navbar-dropdown dropdown me-2 me-xl-0">
|
||||
|
@ -142,7 +142,7 @@ const OrganizationsList = ({searchText}) => {
|
||||
colSpan={organizationsColumns.length + 1}
|
||||
className="text-center"
|
||||
>
|
||||
<p className="fw-semibold">Not Found</p>
|
||||
<p className="fw-semibold">{isLoading ? "Loading....":"Not Found Organization"}</p>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
@ -12,6 +12,7 @@ import showToast from "../../services/toastService";
|
||||
import {
|
||||
useEmployeeForTaskAssign,
|
||||
useProjectAssignedOrganizations,
|
||||
useProjectAssignedServices,
|
||||
useProjectDetails,
|
||||
} from "../../hooks/useProjects";
|
||||
import eventBus from "../../services/eventBus";
|
||||
@ -95,7 +96,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
}, []);
|
||||
|
||||
const selectedProject = useSelectedProject();
|
||||
const { data: serviceList, isLoading: isServiceLoading } = useServices();
|
||||
const { data: serviceList, isLoading: isServiceLoading } = useProjectAssignedServices(selectedProject);
|
||||
const { data: organizationList, isLoading: isOrgLoading } =
|
||||
useProjectAssignedOrganizations(selectedProject);
|
||||
const { data: employees, isLoading: isEmployeeLoading } =
|
||||
@ -274,7 +275,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
) : (
|
||||
<>
|
||||
<option value="">--Select Service--</option>
|
||||
{serviceList?.data?.map((service,index) => (
|
||||
{serviceList?.map((service,index) => (
|
||||
<option key={`${service.id}-${index}`} value={service.id}>
|
||||
{service.name}
|
||||
</option>
|
||||
|
@ -108,7 +108,7 @@ const WorkArea = ({ workArea, floor, forBuilding }) => {
|
||||
<ProgressBar
|
||||
completedWork={formatNumber(workArea?.completedWork)}
|
||||
plannedWork={formatNumber(workArea?.plannedWork)}
|
||||
className="m-0 my-2 me-6 text-info"
|
||||
className="m-0 my-2 " height="6px"rounded showLabel={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -367,14 +367,14 @@ const ManageProjectInfo = ({ project, onClose }) => {
|
||||
className="btn btn-label-secondary btn-sm me-2"
|
||||
onClick={handleCancel}
|
||||
aria-label="Close"
|
||||
disabled={isPending || isCreating}
|
||||
disabled={isPending || isCreating || loading}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary btn-sm"
|
||||
disabled={isPending || isCreating}
|
||||
disabled={isPending || isCreating || loading}
|
||||
>
|
||||
{isPending||isCreating ? "Please Wait..." : project ? "Update" : "Submit"}
|
||||
</button>
|
||||
|
@ -132,11 +132,15 @@ const ProjectListView = ({
|
||||
|
||||
return (
|
||||
<div className="card page-min-h py-4 px-6 shadow-sm">
|
||||
|
||||
<div
|
||||
className="table-responsive text-nowrap page-min-h"
|
||||
>
|
||||
<table className="table table-hover align-middle m-0">
|
||||
<thead className="border-bottom ">
|
||||
<tr>
|
||||
{projectColumns.map((col) => (
|
||||
<th key={col.key} colSpan={col.colSpan} className={col.className}>
|
||||
<th key={col.key} colSpan={col.colSpan} className={`${col.className} table_header_border`}>
|
||||
{col.label}
|
||||
</th>
|
||||
))}
|
||||
@ -217,6 +221,7 @@ const ProjectListView = ({
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{isLoading && (
|
||||
<div className="py-4">
|
||||
|
@ -6,6 +6,7 @@ import { useOrganizationEmployees } from "../../../hooks/useOrganization";
|
||||
import {
|
||||
useEmployeesByProjectAllocated,
|
||||
useManageProjectAllocation,
|
||||
useProjectAssignedServices,
|
||||
} from "../../../hooks/useProjects";
|
||||
import useMaster, { useServices } from "../../../hooks/masterHook/useMaster";
|
||||
import showToast from "../../../services/toastService";
|
||||
@ -29,7 +30,7 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
|
||||
useEmployeesByProjectAllocated(selectedProject, null);
|
||||
|
||||
const { data: jobRoles } = useMaster();
|
||||
const { data: services } = useServices();
|
||||
const { data: services } = useProjectAssignedServices(selectedProject);
|
||||
|
||||
const [employees, setEmployees] = useState([]);
|
||||
|
||||
@ -187,7 +188,7 @@ if (employees.length === 0) {
|
||||
}`}
|
||||
>
|
||||
<option value="">Select Service</option>
|
||||
{services?.data?.map((s) => (
|
||||
{services?.map((s) => (
|
||||
<option key={s.id} value={s.id}>
|
||||
{s.name}
|
||||
</option>
|
||||
|
@ -3,8 +3,8 @@ import { useController, useFormContext, useWatch } from "react-hook-form";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const DateRangePicker = ({
|
||||
md,
|
||||
sm,
|
||||
md=12,
|
||||
sm=6,
|
||||
onRangeChange,
|
||||
DateDifference = 7,
|
||||
endDateMode = "yesterday",
|
||||
@ -63,21 +63,22 @@ const DateRangePicker = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`col-${sm} col-sm-${md} px-1`}>
|
||||
<div className={`position-relative w-auto justify-content-center`}>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control form-control-sm ps-2 pe-5 me-4"
|
||||
className="form-control form-control-sm w-100 pe-8 "
|
||||
placeholder="From to End"
|
||||
id="flatpickr-range"
|
||||
ref={inputRef}
|
||||
/>
|
||||
|
||||
<i
|
||||
className="bx bx-calendar calendar-icon cursor-pointer position-relative top-50 translate-middle-y"
|
||||
className="bx bx-calendar calendar-icon cursor-pointer position-absolute top-50 end-0 translate-middle-y me-2 "
|
||||
onClick={handleIconClick}
|
||||
style={{ right: "22px", bottom: "-8px" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -3,35 +3,65 @@ import React from "react";
|
||||
const ProgressBar = ({
|
||||
plannedWork = 100,
|
||||
completedWork = 0,
|
||||
height = "8px",
|
||||
height = "6px",
|
||||
className = "mb-4",
|
||||
rounded = true,
|
||||
showLabel = true,
|
||||
}) => {
|
||||
const getProgress = (planned, completed) => {
|
||||
if (!planned || planned === 0) return "0%";
|
||||
return `${Math.min((completed / planned) * 100, 100).toFixed(2)}%`;
|
||||
if (!planned || planned === 0) return 0;
|
||||
return Math.min((completed / planned) * 100, 100);
|
||||
};
|
||||
|
||||
const progressStyle = {
|
||||
width: getProgress(plannedWork, completedWork),
|
||||
const percentage = getProgress(plannedWork, completedWork);
|
||||
|
||||
const progressBarStyle = {
|
||||
width: ` ${percentage.toFixed(2)}%`,
|
||||
transition: "width 0.4s ease",
|
||||
};
|
||||
|
||||
const containerStyle = {
|
||||
height,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`progress ${className} ${rounded ? "rounded" : ""}`}
|
||||
style={{ height }}
|
||||
className={`d-flex align-items-center ${className}`}
|
||||
style={containerStyle}
|
||||
>
|
||||
<div className="flex-grow-1">
|
||||
<div
|
||||
className={`progress ${rounded ? "rounded" : ""}`}
|
||||
style={{ height, backgroundColor: "#f0f0f0" }}
|
||||
>
|
||||
<div
|
||||
className={`progress-bar ${rounded ? "rounded" : ""}`}
|
||||
role="progressbar"
|
||||
style={progressStyle}
|
||||
style={progressBarStyle}
|
||||
aria-valuenow={completedWork}
|
||||
aria-valuemin="0"
|
||||
aria-valuemax={plannedWork}
|
||||
></div>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showLabel && (
|
||||
<span
|
||||
className="fw-semibold text-secondary"
|
||||
style={{
|
||||
minWidth: "45px",
|
||||
textAlign: "right",
|
||||
fontSize: "0.8rem",
|
||||
}}
|
||||
>
|
||||
{percentage.toFixed(2)}%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProgressBar;
|
||||
|
||||
|
@ -60,6 +60,7 @@ const GalleryFilterPanel = ({ onApply }) => {
|
||||
endField="endDate"
|
||||
resetSignal={resetKey}
|
||||
defaultRange={false}
|
||||
maxDate={new Date()}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
|
@ -84,6 +84,7 @@ export const useAuthModal = () => {
|
||||
|
||||
export const useLogout = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const naviget = useNavigate()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
@ -96,9 +97,11 @@ export const useLogout = () => {
|
||||
},
|
||||
|
||||
onSuccess: (data) => {
|
||||
queryClient.clear()
|
||||
removeSession();
|
||||
|
||||
window.location.href = "/auth/login";
|
||||
// window.location.href = "/auth/login";
|
||||
naviget("/auth/login",{replace:true})
|
||||
if (onSuccessCallBack) onSuccessCallBack();
|
||||
},
|
||||
|
||||
|
@ -15,10 +15,10 @@ export const useEmployee = (employeeId) => {
|
||||
return useQuery({
|
||||
queryKey: ["employeeProfile", employeeId],
|
||||
queryFn: async () => {
|
||||
const res = await EmployeeRepository.getEmployeeProfile(employeeId)
|
||||
const res = await EmployeeRepository.getEmployeeProfile(employeeId);
|
||||
return res.data;
|
||||
},
|
||||
enabled:!!employeeId
|
||||
enabled: !!employeeId,
|
||||
});
|
||||
};
|
||||
|
||||
@ -137,7 +137,6 @@ export const useEmployeesAllOrByProjectId = (
|
||||
return res.data;
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
data: employees = [],
|
||||
isLoading,
|
||||
@ -158,6 +157,18 @@ export const useEmployeesAllOrByProjectId = (
|
||||
};
|
||||
};
|
||||
|
||||
// New Employee get list by CureentOrganization
|
||||
|
||||
export const useEmployeesByOrganization = (showInactive) => {
|
||||
return useQuery({
|
||||
queryKey: ["employeesByOrganization", showInactive],
|
||||
queryFn: async () => {
|
||||
const res = await EmployeeRepository.getAllEmployeeList(showInactive);
|
||||
return res.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// ManageEmployee.jsx
|
||||
export const useEmployeeProfile = (employeeId) => {
|
||||
const isEnabled = !!employeeId;
|
||||
@ -208,9 +219,6 @@ export const useEmployeesNameByProject = (projectId) => {
|
||||
|
||||
// Mutation------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
export const useUpdateEmployee = () => {
|
||||
const selectedProject = useSelector(
|
||||
(store) => store.localVariables.projectId
|
||||
@ -247,7 +255,6 @@ export const useUpdateEmployee = () => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const useSuspendEmployee = ({
|
||||
setIsDeleteModalOpen,
|
||||
setemployeeLodaing,
|
||||
@ -300,7 +307,6 @@ export const useSuspendEmployee = ({
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const useUpdateEmployeeRoles = ({
|
||||
onClose,
|
||||
resetForm,
|
||||
|
@ -139,7 +139,7 @@ export const useAssignOrgToProject = (onSuccessCallback) => {
|
||||
queryKey: ["projectAssignedOrganiztions"],
|
||||
});
|
||||
useClient.invalidateQueries({
|
||||
queryKey: ["projectAssignedOrganization", projectId],
|
||||
queryKey: ["projectAssignedServices", projectId],
|
||||
});
|
||||
showToast("Organization successfully", "success");
|
||||
if (onSuccessCallback) onSuccessCallback();
|
||||
|
@ -296,7 +296,7 @@ export const useProjectAssignedOrganizations = (projectId) => {
|
||||
};
|
||||
export const useProjectAssignedServices = (projectId) => {
|
||||
return useQuery({
|
||||
queryKey: ["projectAssignedOrganization", projectId],
|
||||
queryKey: ["projectAssignedServices", projectId],
|
||||
queryFn: async () => {
|
||||
const resp = await ProjectRepository.getProjectAssignedServices(
|
||||
projectId
|
||||
|
@ -131,7 +131,7 @@ const AttendancePage = () => {
|
||||
<div className="nav-align-top nav-tabs-shadow ">
|
||||
{/* Tabs */}
|
||||
<div className="nav-align-top nav-tabs-shadow bg-white border-bottom pt-5">
|
||||
<div className="row align-items-center g-0 mb-3 mb-md-0 mx-5">
|
||||
<div className="row align-items-center g-0 mb-3 mb-md-0 mx-1 mx-sm-5">
|
||||
{/* Tabs */}
|
||||
<div className="col-12 col-md">
|
||||
<ul className="nav nav-tabs" role="tablist">
|
||||
@ -181,9 +181,10 @@ const AttendancePage = () => {
|
||||
{/* Search + Organization filter */}
|
||||
<div className="col-12 col-md-auto mt-2 mt-md-0 ms-md-auto d-flex gap-2 align-items-center">
|
||||
{/* Organization Dropdown */}
|
||||
<div className="row">
|
||||
<div className="col-12 col-sm-6 mb-2 mb-sm-0">
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
style={{ minWidth: "180px" }}
|
||||
value={appliedFilters.selectedOrganization}
|
||||
onChange={(e) =>
|
||||
setAppliedFilters((prev) => ({
|
||||
@ -200,7 +201,8 @@ const AttendancePage = () => {
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
</div>
|
||||
<div className="col-12 col-sm-6">
|
||||
{/* Search Input */}
|
||||
<input
|
||||
type="text"
|
||||
@ -208,10 +210,14 @@ const AttendancePage = () => {
|
||||
placeholder="Search Employee..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
style={{ minWidth: "200px" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="tab-content attedanceTabs py-0 px-1 px-sm-3 pb-10">
|
||||
|
@ -8,6 +8,8 @@ import ReportTask from "../../components/Activities/ReportTask";
|
||||
import TaskReportFilterPanel from "../../components/DailyProgressRport/TaskReportFilterPanel";
|
||||
import { useFab } from "../../Context/FabContext";
|
||||
import SubTask from "../../components/Activities/SubTask";
|
||||
import { useProjectAssignedServices } from "../../hooks/useProjects";
|
||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
|
||||
const DailyProgrssContext = createContext();
|
||||
export const useDailyProgrssContext = () => {
|
||||
@ -21,10 +23,11 @@ export const useDailyProgrssContext = () => {
|
||||
};
|
||||
|
||||
const DailyProgrssReport = () => {
|
||||
const selectedProject = useSelectedProject()
|
||||
const [service, setService] = useState("");
|
||||
const [filter,setFilter] = useState('')
|
||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||
const { data, isLoading, isError, error } = useServices();
|
||||
const { data, isLoading, isError, error } = useProjectAssignedServices(selectedProject);
|
||||
|
||||
const [modal, setModal] = useState({ type: null, data: null });
|
||||
|
||||
@ -86,7 +89,7 @@ const DailyProgrssReport = () => {
|
||||
/>
|
||||
|
||||
<div className="card card-fullscreen p-5">
|
||||
{data?.data?.length > 0 && (<div className="col-sm-4 col-md-3 col-12">
|
||||
{data?.length > 0 && (<div className="col-sm-4 col-md-3 col-12">
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
value={service ?? ""}
|
||||
@ -99,7 +102,7 @@ const DailyProgrssReport = () => {
|
||||
<option value="">
|
||||
All Services
|
||||
</option>
|
||||
{data?.data?.map((service) => (
|
||||
{data?.map((service) => (
|
||||
<option key={service.id} value={service.id}>
|
||||
{service.name}
|
||||
</option>
|
||||
|
@ -7,6 +7,7 @@ import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
import ManageEmp from "../../components/Employee/ManageRole";
|
||||
import {
|
||||
useEmployeesAllOrByProjectId,
|
||||
useEmployeesByOrganization,
|
||||
useSuspendEmployee,
|
||||
} from "../../hooks/useEmployees";
|
||||
import { useProjectName, useProjects } from "../../hooks/useProjects";
|
||||
@ -36,6 +37,7 @@ import GlobalModel from "../../components/common/GlobalModel";
|
||||
import usePagination from "../../hooks/usePagination";
|
||||
import { setProjectId } from "../../slices/localVariablesSlice";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import Pagination from "../../components/common/Pagination";
|
||||
|
||||
const EmployeeList = () => {
|
||||
const selectedProjectId = useSelector(
|
||||
@ -46,15 +48,14 @@ const EmployeeList = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [showInactive, setShowInactive] = useState(false);
|
||||
const [showAllEmployees, setShowAllEmployees] = useState(false);
|
||||
const Manage_Employee = useHasUserPermission(MANAGE_EMPLOYEES);
|
||||
|
||||
const { employees, loading, setLoading, error, recallEmployeeData } =
|
||||
useEmployeesAllOrByProjectId(
|
||||
showAllEmployees,
|
||||
selectedProjectId,
|
||||
showInactive
|
||||
);
|
||||
const {
|
||||
data: employees,
|
||||
isLoading: loading,
|
||||
error,
|
||||
refetch: recallEmployeeData,
|
||||
} = useEmployeesByOrganization(showInactive);
|
||||
|
||||
const [employeeList, setEmployeeList] = useState([]);
|
||||
const [modelConfig, setModelConfig] = useState();
|
||||
@ -70,7 +71,6 @@ const EmployeeList = () => {
|
||||
const [selectedEmpFordelete, setSelectedEmpFordelete] = useState(null);
|
||||
const [employeeLodaing, setemployeeLodaing] = useState(false);
|
||||
const ViewTeamMember = useHasUserPermission(VIEW_TEAM_MEMBERS);
|
||||
const ViewAllEmployee = useHasUserPermission(VIEW_ALL_EMPLOYEES);
|
||||
const { mutate: suspendEmployee, isPending: empLodaing } = useSuspendEmployee(
|
||||
{
|
||||
setIsDeleteModalOpen,
|
||||
@ -157,7 +157,7 @@ const EmployeeList = () => {
|
||||
const handleAllEmployeesToggle = (e) => {
|
||||
const isChecked = e.target.checked;
|
||||
setShowInactive(false);
|
||||
setShowAllEmployees(isChecked);
|
||||
// setShowAllEmployees(isChecked);
|
||||
};
|
||||
|
||||
const handleEmployeeModel = (id) => {
|
||||
@ -205,25 +205,16 @@ const EmployeeList = () => {
|
||||
|
||||
setCurrentPage((prevPage) => (prevPage !== 1 ? 1 : prevPage));
|
||||
}
|
||||
}, [loading, employees, selectedProjectId, showAllEmployees]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showAllEmployees) {
|
||||
recallEmployeeData(showInactive, selectedProjectId);
|
||||
}
|
||||
}, [selectedProjectId, showInactive, showAllEmployees, recallEmployeeData]);
|
||||
}, [loading, employees, selectedProjectId, showInactive]);
|
||||
|
||||
const handler = useCallback(
|
||||
(msg) => {
|
||||
if (employees.some((item) => item.id == msg.employeeId)) {
|
||||
setEmployeeList([]);
|
||||
recallEmployeeData(
|
||||
showInactive,
|
||||
showAllEmployees ? null : selectedProjectId
|
||||
); // Use selectedProjectId here
|
||||
recallEmployeeData(showInactive);
|
||||
}
|
||||
},
|
||||
[employees, showInactive, showAllEmployees, selectedProjectId] // Add all relevant dependencies
|
||||
[employees, showInactive, showInactive, selectedProjectId] // Add all relevant dependencies
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -254,7 +245,6 @@ const EmployeeList = () => {
|
||||
<ManageEmployee
|
||||
employeeId={selectedEmployeeId}
|
||||
onClosed={() => setShowModal(false)}
|
||||
IsAllEmployee={showAllEmployees}
|
||||
/>
|
||||
</GlobalModel>
|
||||
)}
|
||||
@ -293,38 +283,19 @@ const EmployeeList = () => {
|
||||
|
||||
{ViewTeamMember ? (
|
||||
// <div className="row">
|
||||
<div className="card p-1">
|
||||
<div className="card p-1 page-min-h">
|
||||
<div className="card-datatable table-responsive pt-5 mx-5 py-10">
|
||||
<div
|
||||
id="DataTables_Table_0_wrapper"
|
||||
className="dataTables_wrapper dt-bootstrap5 no-footer"
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
<div className="d-flex flex-wrap align-items-center justify-content-between gap-3 mb-3">
|
||||
{/* Switches: All Employees + Inactive */}
|
||||
<div className="d-flex flex-wrap align-items-center gap-3">
|
||||
{/* All Employees Switch */}
|
||||
{ViewAllEmployee && (
|
||||
<div className="form-check form-switch text-start">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
role="switch"
|
||||
id="allEmployeesCheckbox"
|
||||
checked={showAllEmployees}
|
||||
onChange={handleAllEmployeesToggle}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label ms-0"
|
||||
htmlFor="allEmployeesCheckbox"
|
||||
>
|
||||
Show All Employees
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Show Inactive Employees Switch */}
|
||||
{showAllEmployees && (
|
||||
|
||||
<div className="form-check form-switch text-start">
|
||||
<input
|
||||
type="checkbox"
|
||||
@ -341,11 +312,10 @@ const EmployeeList = () => {
|
||||
Show Inactive Employees
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Right side: Search + Export + Add Employee */}
|
||||
<div className="d-flex flex-wrap align-items-center justify-content-end gap-3 flex-grow-1">
|
||||
<div className="d-flex flex-wrap align-items-center justify-content-start justify-content-md-end gap-3 flex-grow-1">
|
||||
{/* Search Input - ALWAYS ENABLED */}
|
||||
<div className="dataTables_filter">
|
||||
<label className="mb-0">
|
||||
@ -431,7 +401,6 @@ const EmployeeList = () => {
|
||||
className="datatables-users table border-top dataTable no-footer dtr-column text-nowrap"
|
||||
id="DataTables_Table_0"
|
||||
aria-describedby="DataTables_Table_0_info"
|
||||
style={{ width: "100%" }}
|
||||
ref={tableRef}
|
||||
>
|
||||
<thead>
|
||||
@ -522,28 +491,27 @@ const EmployeeList = () => {
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{/* Conditional messages for no data or no search results */}
|
||||
|
||||
{!loading &&
|
||||
displayData?.length === 0 &&
|
||||
searchText &&
|
||||
!showAllEmployees ? (
|
||||
(!searchText ) ? (
|
||||
<tr>
|
||||
<td colSpan={8}>
|
||||
<small className="muted">
|
||||
'{searchText}' employee not found
|
||||
</small>{" "}
|
||||
<td colSpan={8} className="border-0 py-3">
|
||||
<div className="py-4">
|
||||
No Data Found
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
|
||||
{!loading &&
|
||||
displayData?.length === 0 &&
|
||||
(!searchText || showAllEmployees) ? (
|
||||
(searchText ) ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={8}
|
||||
style={{ paddingTop: "20px", textAlign: "center" }}
|
||||
>
|
||||
No Data Found
|
||||
<td colSpan={8} className="border-0 py-3">
|
||||
<div className="py-4">
|
||||
{`No match record found ${searchText} `}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
@ -697,55 +665,12 @@ const EmployeeList = () => {
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style={{ width: "1%" }}></div>
|
||||
|
||||
{/* Pagination */}
|
||||
{!loading && displayData.length > ITEMS_PER_PAGE && (
|
||||
<nav aria-label="Page">
|
||||
<ul className="pagination pagination-sm justify-content-end py-1">
|
||||
<li
|
||||
className={`page-item ${
|
||||
currentPage === 1 ? "disabled" : ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="page-link btn-xs"
|
||||
onClick={() => paginate(currentPage - 1)}
|
||||
>
|
||||
«
|
||||
</button>
|
||||
</li>
|
||||
|
||||
{[...Array(totalPages)]?.map((_, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={`page-item ${
|
||||
currentPage === index + 1 ? "active" : ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="page-link"
|
||||
onClick={() => paginate(index + 1)}
|
||||
>
|
||||
{index + 1}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
|
||||
<li
|
||||
className={`page-item ${
|
||||
currentPage === totalPages ? "disabled" : ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="page-link"
|
||||
onClick={() => paginate(currentPage + 1)}
|
||||
>
|
||||
»
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{displayData?.length > 0 && (
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
onPageChange={paginate}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -92,3 +92,46 @@ Date.UTC(Number(year), Number(month) - 1, Number(day), 0, 0, 0)
|
||||
);
|
||||
return isNaN(date.getTime()) ? null : date.toISOString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flexible number formatter for currency, numbers, or percentages.
|
||||
*
|
||||
* @param {number} amount - The value to format.
|
||||
* @param {Object} options - Formatting options.
|
||||
* @param {"currency"|"number"|"percent"} [options.type="number"] - Type of format.
|
||||
* @param {string} [options.currency="INR"] - Currency code (only used when type="currency").
|
||||
* @param {string} [options.locale="en-US"] - Locale for formatting.
|
||||
* @param {"short"|"long"|"standard"} [options.notation="compact"] - Display style for large numbers.
|
||||
* @param {number} [options.minimumFractionDigits=0] - Minimum decimal places.
|
||||
* @param {number} [options.maximumFractionDigits=2] - Maximum decimal places.
|
||||
* @returns {string} Formatted number string.
|
||||
*/
|
||||
export const formatFigure = (
|
||||
amount,
|
||||
{
|
||||
type = "number",
|
||||
currency = "INR",
|
||||
locale = "en-US",
|
||||
notation = "compact",
|
||||
compactDisplay = "short",
|
||||
minimumFractionDigits = 0,
|
||||
maximumFractionDigits = 2,
|
||||
} = {}
|
||||
) => {
|
||||
if (amount == null || isNaN(amount)) return "-";
|
||||
|
||||
const formatterOptions = {
|
||||
style: type === "currency" ? "currency" : type === "percent" ? "percent" : "decimal",
|
||||
notation: notation,
|
||||
compactDisplay,
|
||||
minimumFractionDigits,
|
||||
maximumFractionDigits,
|
||||
};
|
||||
|
||||
if (type === "currency") {
|
||||
formatterOptions.currency = currency;
|
||||
}
|
||||
|
||||
return new Intl.NumberFormat(locale, formatterOptions).format(amount);
|
||||
};
|
||||
|
@ -3,6 +3,11 @@ export const DURATION_TIME = 10; // minutes
|
||||
export const ITEMS_PER_PAGE = 20;
|
||||
export const OTP_EXPIRY_SECONDS = 300; // OTP time
|
||||
|
||||
|
||||
export const BASE_URL = process.env.VITE_BASE_URL;
|
||||
|
||||
// export const BASE_URL = "https://api.marcoaiot.com";
|
||||
|
||||
export const MANAGE_MASTER = "588a8824-f924-4955-82d8-fc51956cf323";
|
||||
|
||||
export const VIEW_MASTER = "5ffbafe0-7ab0-48b1-bb50-c1bf76b65f9d";
|
||||
@ -142,6 +147,3 @@ export const PROJECT_STATUS = [
|
||||
];
|
||||
export const DEFAULT_EMPTY_STATUS_ID = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
export const BASE_URL = process.env.VITE_BASE_URL;
|
||||
|
||||
// export const BASE_URL = "https://api.marcoaiot.com";
|
||||
|
Loading…
x
Reference in New Issue
Block a user