Compare commits

...

29 Commits

Author SHA1 Message Date
12b632f087 added new api for list of org 2025-10-11 18:06:51 +05:30
6ee4fb6d04 Updated TeamEmployeeList to fetch employees using useOrganizationEmployees during search. 2025-10-11 17:48:25 +05:30
67bb685d4b added new api for orgaization dropdown 2025-10-11 16:55:22 +05:30
8fd4e7f3f1 added space between first and last name 2025-10-11 16:30:27 +05:30
aca2decb00 clear fully cache after remove session 2025-10-11 16:26:04 +05:30
f7f4b68997 changed reimburse data during transaction to current date 2025-10-11 15:09:04 +05:30
f839613066 Merge pull request 'Fix: Ensure orgData can be cleared when opening Organization Modal' (#474) from HotChanges_11_10_25 into main
Reviewed-on: #474
Merged
2025-10-11 08:42:26 +00:00
b58bd33774 added requested b and requested at column inside rgularization 2025-10-11 14:08:27 +05:30
136bc94c5b removed dbugger and add new classes 2025-10-11 13:12:23 +05:30
281a956ac8 fixed directory header layout 2025-10-11 12:43:37 +05:30
31882c3d12 REMOVED UNNEEDED CARD CLASS 2025-10-11 11:13:38 +05:30
a64635cd37 Fix: Ensure orgData can be cleared when opening Organization Modal 2025-10-11 10:29:40 +05:30
9b8c8c34ab Merge pull request 'HotChanges_10_10_25' (#472) from HotChanges_10_10_25 into main
Reviewed-on: #472
Merged
2025-10-10 14:50:04 +00:00
704ba79289 prevent to select future date 2025-10-10 20:17:06 +05:30
3440467107 wapped header content into separated div 2025-10-10 18:25:19 +05:30
f4edcfd2f3 changed prod url 2025-10-10 18:18:20 +05:30
62e5c6899a profile icon should display always at end 2025-10-10 18:17:24 +05:30
00a23b3de9 changed class for table responsive 2025-10-10 18:11:43 +05:30
79161a8ede prevent to pick future date 2025-10-10 18:09:35 +05:30
860779d096 fixed service list show in Dialy Task 2025-10-10 18:08:21 +05:30
a2067e150d fixed service list show 2025-10-10 17:51:12 +05:30
edce5ef614 added newhook that return only organization employee for employee list (Active or Inactive) 2025-10-10 16:48:22 +05:30
dd944b3414 datepicker should not take future date 2025-10-10 16:46:34 +05:30
13d3572cf6 fixed datepicke ui 2025-10-10 16:45:43 +05:30
0dd7c19457 added navigation hook to replace path 2025-10-10 15:37:59 +05:30
3cc4f0b416 mismatch service api fixed 2025-10-10 14:15:30 +05:30
f8095ac9bf upgraded progress 2025-10-10 10:55:51 +05:30
6280abf95e added marging between fltes input box 2025-10-09 17:32:39 +05:30
1200937097 Merge pull request 'HotChanges_09_10_25 : Added Daily Task Filter' (#467) from HotChanges_09_10_25 into main
Reviewed-on: #467
Merged
2025-10-09 12:00:21 +00:00
42 changed files with 800 additions and 490 deletions

View File

@ -9,4 +9,274 @@
} }
.table_header_border { .table_header_border {
border-bottom:2px solid var(--bs-table-border-color) ; border-bottom:2px solid var(--bs-table-border-color) ;
} }
.text-xxs { font-size: 0.55rem; } /* 8px */
.text-xs { font-size: 0.75rem; } /* 12px */
.text-sm { font-size: 0.875rem; } /* 14px */
.text-base { font-size: 1rem; } /* 16px */
.text-lg { font-size: 1.125rem; } /* 18px */
.text-xl { font-size: 1.25rem; } /* 20px */
.text-2xl { font-size: 1.5rem; } /* 24px */
.text-3xl { font-size: 1.875rem; } /* 30px */
.text-4xl { font-size: 2.25rem; } /* 36px */
.text-5xl { font-size: 3rem; } /* 48px */
.text-6xl { font-size: 3.75rem; } /* 60px */
.text-7xl { font-size: 4.5rem; } /* 72px */
.text-8xl { font-size: 6rem; } /* 96px */
.text-9xl { font-size: 8rem; } /* 128px */
/* */
.w-0 { width: 0px; }
.w-px { width: 1px; }
.w-1 { width: 0.25rem; } /* 4px */
.w-2 { width: 0.5rem; } /* 8px */
.w-3 { width: 0.75rem; } /* 12px */
.w-4 { width: 1rem; } /* 16px */
.w-5 { width: 1.25rem; } /* 20px */
.w-6 { width: 1.5rem; } /* 24px */
.w-8 { width: 2rem; } /* 32px */
.w-10 { width: 2.5rem; } /* 40px */
.w-12 { width: 3rem; } /* 48px */
.w-16 { width: 4rem; } /* 64px */
.w-20 { width: 5rem; } /* 80px */
.w-24 { width: 6rem; } /* 96px */
.w-32 { width: 8rem; } /* 128px */
.w-40 { width: 10rem; } /* 160px */
.w-48 { width: 12rem; } /* 192px */
.w-56 { width: 14rem; } /* 224px */
.w-64 { width: 16rem; } /* 256px */
.w-auto { width: auto; }
.w-full { width: 100%; }
.w-screen{ width: 100vw; }
.w-min { width: min-content; }
.w-max { width: max-content; }
.h-0 { height: 0px; }
.h-px { height: 1px; }
.h-1 { height: 0.25rem; } /* 4px */
.h-2 { height: 0.5rem; } /* 8px */
.h-3 { height: 0.75rem; } /* 12px */
.h-4 { height: 1rem; } /* 16px */
.h-5 { height: 1.25rem; } /* 20px */
.h-6 { height: 1.5rem; } /* 24px */
.h-8 { height: 2rem; } /* 32px */
.h-10 { height: 2.5rem; } /* 40px */
.h-12 { height: 3rem; } /* 48px */
.h-16 { height: 4rem; } /* 64px */
.h-20 { height: 5rem; } /* 80px */
.h-24 { height: 6rem; } /* 96px */
.h-32 { height: 8rem; } /* 128px */
.h-40 { height: 10rem; } /* 160px */
.h-48 { height: 12rem; } /* 192px */
.h-56 { height: 14rem; } /* 224px */
.h-64 { height: 16rem; } /* 256px */
.h-auto { height: auto; }
.h-full { height: 100%; }
.h-screen{ height: 100vh; }
.h-min { height: min-content; }
.h-max { height: max-content; }
/* ==========================
Base Font Sizes (mobile first)
========================== */
.text-xxs { font-size: 0.55rem; } /* 8px */
.text-xs { font-size: 0.75rem; } /* 12px */
.text-sm { font-size: 0.875rem; } /* 14px */
.text-base{ font-size: 1rem; } /* 16px */
.text-lg { font-size: 1.125rem; } /* 18px */
.text-xl { font-size: 1.25rem; } /* 20px */
.text-2xl { font-size: 1.5rem; } /* 24px */
.text-3xl { font-size: 1.875rem; } /* 30px */
.text-4xl { font-size: 2.25rem; } /* 36px */
.text-5xl { font-size: 3rem; } /* 48px */
.text-6xl { font-size: 3.75rem; } /* 60px */
.text-7xl { font-size: 4.5rem; } /* 72px */
.text-8xl { font-size: 6rem; } /* 96px */
.text-9xl { font-size: 8rem; } /* 128px */
/* ==========================
Base Heights
========================== */
.h-0 { height: 0; }
.h-px { height: 1px; }
.h-1 { height: 0.25rem; } /* 4px */
.h-2 { height: 0.5rem; } /* 8px */
.h-3 { height: 0.75rem; } /* 12px */
.h-4 { height: 1rem; } /* 16px */
.h-5 { height: 1.25rem; } /* 20px */
.h-6 { height: 1.5rem; } /* 24px */
.h-8 { height: 2rem; } /* 32px */
.h-10 { height: 2.5rem; } /* 40px */
.h-12 { height: 3rem; } /* 48px */
.h-16 { height: 4rem; } /* 64px */
.h-20 { height: 5rem; } /* 80px */
.h-24 { height: 6rem; } /* 96px */
.h-32 { height: 8rem; } /* 128px */
.h-40 { height: 10rem; } /* 160px */
.h-48 { height: 12rem; } /* 192px */
.h-56 { height: 14rem; } /* 224px */
.h-64 { height: 16rem; } /* 256px */
.h-full { height: 100%; }
.h-screen{ height: 100vh; }
/* ==========================
Base Widths
========================== */
.w-0 { width: 0; }
.w-px { width: 1px; }
.w-1 { width: 0.25rem; }
.w-2 { width: 0.5rem; }
.w-3 { width: 0.75rem; }
.w-4 { width: 1rem; }
.w-5 { width: 1.25rem; }
.w-6 { width: 1.5rem; }
.w-8 { width: 2rem; }
.w-10 { width: 2.5rem; }
.w-12 { width: 3rem; }
.w-16 { width: 4rem; }
.w-20 { width: 5rem; }
.w-24 { width: 6rem; }
.w-32 { width: 8rem; }
.w-40 { width: 10rem; }
.w-48 { width: 12rem; }
.w-56 { width: 14rem; }
.w-64 { width: 16rem; }
.w-full { width: 100%; }
.w-screen{ width: 100vw; }
/* ==========================
Responsive Variants
========================== */
@media (min-width: 576px) { /* sm */
/* Font */
.text-xxs-sm { font-size: 0.55rem; }
.text-xs-sm { font-size: 0.75rem; }
.text-sm-sm { font-size: 0.875rem; }
.text-base-sm{ font-size: 1rem; }
.text-lg-sm { font-size: 1.125rem; }
.text-xl-sm { font-size: 1.25rem; }
.text-2xl-sm{ font-size: 1.5rem; }
/* Height */
.h-1-sm{ height: 0.25rem; }
.h-2-sm{ height: 0.5rem; }
.h-3-sm{ height: 0.75rem; }
.h-4-sm{ height: 1rem; }
.h-5-sm{ height: 1.25rem; }
.h-6-sm{ height: 1.5rem; }
.h-8-sm{ height: 2rem; }
.h-10-sm{ height: 2.5rem; }
/* Width */
.w-1-sm{ width: 0.25rem; }
.w-2-sm{ width: 0.5rem; }
.w-3-sm{ width: 0.75rem; }
.w-4-sm{ width: 1rem; }
.w-5-sm{ width: 1.25rem; }
.w-6-sm{ width: 1.5rem; }
.w-8-sm{ width: 2rem; }
.w-10-sm{ width: 2.5rem; }
}
@media (min-width: 768px) { /* md */
/* Font */
.text-xxs-md { font-size: 0.55rem; }
.text-xs-md { font-size: 0.75rem; }
.text-sm-md { font-size: 0.875rem; }
.text-base-md{ font-size: 1rem; }
.text-lg-md { font-size: 1.125rem; }
.text-xl-md { font-size: 1.25rem; }
.text-2xl-md{ font-size: 1.5rem; }
/* Height */
.h-1-md{ height: 0.25rem; }
.h-2-md{ height: 0.5rem; }
.h-3-md{ height: 0.75rem; }
.h-4-md{ height: 1rem; }
.h-5-md{ height: 1.25rem; }
.h-6-md{ height: 1.5rem; }
.h-8-md{ height: 2rem; }
.h-10-md{ height: 2.5rem; }
/* Width */
.w-1-md{ width: 0.25rem; }
.w-2-md{ width: 0.5rem; }
.w-3-md{ width: 0.75rem; }
.w-4-md{ width: 1rem; }
.w-5-md{ width: 1.25rem; }
.w-6-md{ width: 1.5rem; }
.w-8-md{ width: 2rem; }
.w-10-md{ width: 2.5rem; }
}
@media (min-width: 992px) { /* lg */
/* Font */
.text-xxs-lg { font-size: 0.55rem; }
.text-xs-lg { font-size: 0.75rem; }
.text-sm-lg { font-size: 0.875rem; }
.text-base-lg{ font-size: 1rem; }
.text-lg-lg { font-size: 1.125rem; }
.text-xl-lg { font-size: 1.25rem; }
.text-2xl-lg{ font-size: 1.5rem; }
/* Height */
.h-1-lg{ height: 0.25rem; }
.h-2-lg{ height: 0.5rem; }
.h-3-lg{ height: 0.75rem; }
.h-4-lg{ height: 1rem; }
.h-5-lg{ height: 1.25rem; }
.h-6-lg{ height: 1.5rem; }
.h-8-lg{ height: 2rem; }
.h-10-lg{ height: 2.5rem; }
/* Width */
.w-1-lg{ width: 0.25rem; }
.w-2-lg{ width: 0.5rem; }
.w-3-lg{ width: 0.75rem; }
.w-4-lg{ width: 1rem; }
.w-5-lg{ width: 1.25rem; }
.w-6-lg{ width: 1.5rem; }
.w-8-lg{ width: 2rem; }
.w-10-lg{ width: 2.5rem; }
}
@media (min-width: 1200px) { /* xl */
/* Font */
.text-xxs-xl { font-size: 0.55rem; }
.text-xs-xl { font-size: 0.75rem; }
.text-sm-xl { font-size: 0.875rem; }
.text-base-xl{ font-size: 1rem; }
.text-lg-xl { font-size: 1.125rem; }
.text-xl-xl { font-size: 1.25rem; }
.text-2xl-xl{ font-size: 1.5rem; }
/* Height */
.h-1-xl{ height: 0.25rem; }
.h-2-xl{ height: 0.5rem; }
.h-3-xl{ height: 0.75rem; }
.h-4-xl{ height: 1rem; }
.h-5-xl{ height: 1.25rem; }
.h-6-xl{ height: 1.5rem; }
.h-8-xl{ height: 2rem; }
.h-10-xl{ height: 2.5rem; }
/* Width */
.w-1-xl{ width: 0.25rem; }
.w-2-xl{ width: 0.5rem; }
.w-3-xl{ width: 0.75rem; }
.w-4-xl{ width: 1rem; }
.w-5-xl{ width: 1.25rem; }
.w-6-xl{ width: 1.5rem; }
.w-8-xl{ width: 2rem; }
.w-10-xl{ width: 2.5rem; }
}

View File

@ -38,195 +38,141 @@ const usePagination = (data, itemsPerPage) => {
}; };
const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => { const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
// const selectedProject = useSelector( const selectedProject = useSelectedProject();
// (store) => store.localVariables.projectId const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" });
// ); const dispatch = useDispatch();
const selectedProject = useSelectedProject(); const [loading, setLoading] = useState(false);
const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); const [showPending, setShowPending] = useState(false);
const dispatch = useDispatch(); const [isRefreshing, setIsRefreshing] = useState(false);
const [loading, setLoading] = useState(false);
const [showPending, setShowPending] = useState(false);
const [isRefreshing, setIsRefreshing] = useState(false); const today = new Date();
const [processedData, setProcessedData] = useState([]); today.setHours(0, 0, 0, 0);
const today = new Date(); const yesterday = new Date();
today.setHours(0, 0, 0, 0); yesterday.setDate(yesterday.getDate() - 1);
const yesterday = new Date(); const isSameDay = (dateStr) => {
yesterday.setDate(yesterday.getDate() - 1); if (!dateStr) return false;
const d = new Date(dateStr);
d.setHours(0, 0, 0, 0);
return d.getTime() === today.getTime();
};
const isSameDay = (dateStr) => { const isBeforeToday = (dateStr) => {
if (!dateStr) return false; if (!dateStr) return false;
const d = new Date(dateStr); const d = new Date(dateStr);
d.setHours(0, 0, 0, 0); d.setHours(0, 0, 0, 0);
return d.getTime() === today.getTime(); return d.getTime() < today.getTime();
}; };
const isBeforeToday = (dateStr) => { const sortByName = (a, b) => {
if (!dateStr) return false; const nameA = (a.firstName + a.lastName).toLowerCase();
const d = new Date(dateStr); const nameB = (b.firstName + b.lastName).toLowerCase();
d.setHours(0, 0, 0, 0); return nameA.localeCompare(nameB);
return d.getTime() < today.getTime(); };
};
const sortByName = (a, b) => { const { data = [], isLoading, error, refetch, isFetching } = useAttendancesLogs(
const nameA = a.firstName.toLowerCase() + a.lastName.toLowerCase(); selectedProject,
const nameB = b.firstName.toLowerCase() + b.lastName.toLowerCase(); dateRange.startDate,
return nameA?.localeCompare(nameB); dateRange.endDate,
}; organizationId
);
const { const processedData = useMemo(() => {
data = [], const filteredData = showPending
isLoading, ? data.filter((item) => item.checkOutTime === null)
error, : data;
refetch,
isFetching,
} = useAttendancesLogs(
selectedProject,
dateRange.startDate,
dateRange.endDate,
organizationId
);
const filtering = (data) => {
const filteredData = showPending
? data.filter((item) => item.checkOutTime === null)
: data;
const group1 = filteredData const group1 = filteredData.filter((d) => d.activity === 1 && isSameDay(d.checkInTime)).sort(sortByName);
.filter((d) => d.activity === 1 && isSameDay(d.checkInTime)) const group2 = filteredData.filter((d) => d.activity === 4 && isSameDay(d.checkOutTime)).sort(sortByName);
.sort(sortByName); const group3 = filteredData.filter((d) => d.activity === 1 && isBeforeToday(d.checkInTime)).sort(sortByName);
const group2 = filteredData const group4 = filteredData.filter((d) => d.activity === 4 && isBeforeToday(d.checkOutTime));
.filter((d) => d.activity === 4 && isSameDay(d.checkOutTime)) const group5 = filteredData.filter((d) => d.activity === 2 && isBeforeToday(d.checkOutTime)).sort(sortByName);
.sort(sortByName); const group6 = filteredData.filter((d) => d.activity === 5).sort(sortByName);
const group3 = filteredData
.filter((d) => d.activity === 1 && isBeforeToday(d.checkInTime))
.sort(sortByName);
const group4 = filteredData.filter(
(d) => d.activity === 4 && isBeforeToday(d.checkOutTime)
);
const group5 = filteredData
.filter((d) => d.activity === 2 && isBeforeToday(d.checkOutTime))
.sort(sortByName);
const group6 = filteredData
.filter((d) => d.activity === 5)
.sort(sortByName);
const sortedList = [ const sortedList = [...group1, ...group2, ...group3, ...group4, ...group5, ...group6];
...group1,
...group2,
...group3,
...group4,
...group5,
...group6,
];
// Group by date const groupedByDate = sortedList.reduce((acc, item) => {
const groupedByDate = sortedList.reduce((acc, item) => { const date = (item.checkInTime || item.checkOutTime)?.split("T")[0];
const date = (item.checkInTime || item.checkOutTime)?.split("T")[0]; if (date) {
if (date) { acc[date] = acc[date] || [];
acc[date] = acc[date] || []; acc[date].push(item);
acc[date].push(item);
}
return acc;
}, {});
const sortedDates = Object.keys(groupedByDate).sort(
(a, b) => new Date(b) - new Date(a)
);
const finalData = sortedDates.flatMap((date) => groupedByDate[date]);
setProcessedData(finalData);
};
useEffect(() => {
filtering(data);
}, [data, showPending]);
// New useEffect to handle search filtering
const filteredSearchData = useMemo(() => {
if (!searchTerm) {
return processedData;
} }
const lowercasedSearchTerm = searchTerm.toLowerCase(); return acc;
return processedData.filter((item) => { }, {});
const fullName = `${item.firstName} ${item.lastName}`.toLowerCase();
return fullName.includes(lowercasedSearchTerm);
});
}, [processedData, searchTerm]);
const { const sortedDates = Object.keys(groupedByDate).sort((a, b) => new Date(b) - new Date(a));
currentPage, return sortedDates.flatMap((date) => groupedByDate[date]);
totalPages, }, [data, showPending]);
currentItems: paginatedAttendances,
paginate,
resetPage,
} = usePagination(filteredSearchData, 20);
useEffect(() => { const filteredSearchData = useMemo(() => {
resetPage(); if (!searchTerm) return processedData;
}, [filteredSearchData, resetPage]);
const handler = useCallback( const lowercased = searchTerm.toLowerCase();
(msg) => { return processedData.filter((item) =>
const { startDate, endDate } = dateRange; `${item.firstName} ${item.lastName}`.toLowerCase().includes(lowercased)
const checkIn = msg.response.checkInTime.substring(0, 10);
if (
selectedProject === msg.projectId &&
startDate <= checkIn &&
checkIn <= endDate
) {
queryClient.setQueriesData(["attendanceLogs"], (oldData) => {
if (!oldData) {
queryClient.invalidateQueries({ queryKey: ["attendanceLogs"] });
return;
}
const updatedAttendance = oldData.map((record) =>
record.id === msg.response.id
? { ...record, ...msg.response }
: record
);
filtering(updatedAttendance);
return updatedAttendance;
});
resetPage();
}
},
[selectedProject, dateRange, filtering, resetPage]
); );
}, [processedData, searchTerm]);
useEffect(() => { const {
eventBus.on("attendance_log", handler); currentPage,
return () => eventBus.off("attendance_log", handler); totalPages,
}, [handler]); currentItems: paginatedAttendances,
paginate,
resetPage,
} = usePagination(filteredSearchData, 20);
const employeeHandler = useCallback( useEffect(() => {
(msg) => { resetPage();
const { startDate, endDate } = dateRange; }, [filteredSearchData]);
if (data.some((item) => item.employeeId == msg.employeeId)) {
// dispatch( const handler = useCallback(
// fetchAttendanceData({ (msg) => {
// , const { startDate, endDate } = dateRange;
// fromDate: startDate, const checkIn = msg.response.checkInTime.substring(0, 10);
// toDate: endDate,
// }) if (selectedProject === msg.projectId && startDate <= checkIn && checkIn <= endDate) {
// ); queryClient.setQueriesData(["attendanceLogs"], (oldData) => {
if (!oldData) {
queryClient.invalidateQueries({ queryKey: ["attendanceLogs"] });
return;
}
return oldData.map((record) =>
record.id === msg.response.id ? { ...record, ...msg.response } : record
);
});
resetPage();
}
},
[selectedProject, dateRange, resetPage]
);
useEffect(() => {
eventBus.on("attendance_log", handler);
return () => eventBus.off("attendance_log", handler);
}, [handler]);
const employeeHandler = useCallback(
(msg) => {
const { startDate, endDate } = dateRange;
if (data.some((item) => item.employeeId == msg.employeeId)) {
refetch();
}
},
[data, refetch]
);
useEffect(() => {
eventBus.on("employee", employeeHandler);
return () => eventBus.off("employee", employeeHandler);
}, [employeeHandler]);
refetch();
}
},
[selectedProject, dateRange, data, refetch]
);
useEffect(() => {
eventBus.on("employee", employeeHandler);
return () => eventBus.off("employee", employeeHandler);
}, [employeeHandler]);
return ( return (
<> <>
<div <div
className="dataTables_length text-start py-2 d-flex justify-content-between" className="dataTables_length text-start py-2 d-flex justify-content-between "
id="DataTables_Table_0_length" id="DataTables_Table_0_length"
> >
<div className="d-flex align-items-center my-0 "> <div className="d-flex align-items-center my-0 ">
@ -234,7 +180,7 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
onRangeChange={setDateRange} onRangeChange={setDateRange}
defaultStartDate={yesterday} 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 <input
type="checkbox" type="checkbox"
className="form-check-input" className="form-check-input"

View File

@ -1,25 +1,43 @@
import React, { useCallback, useEffect, useState, useMemo } from "react"; import React, { useCallback, useEffect, useState, useMemo } from "react";
import Avatar from "../common/Avatar"; import Avatar from "../common/Avatar";
import { convertShortTime } from "../../utils/dateUtils"; import { convertShortTime, formatUTCToLocalTime } from "../../utils/dateUtils";
import RegularizationActions from "./RegularizationActions"; import RegularizationActions from "./RegularizationActions";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { useRegularizationRequests } from "../../hooks/useAttendance"; import { useRegularizationRequests } from "../../hooks/useAttendance";
import moment from "moment"; import moment from "moment";
import usePagination from "../../hooks/usePagination"; import usePagination from "../../hooks/usePagination";
import eventBus from "../../services/eventBus"; 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 { 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(); const queryClient = useQueryClient();
// var selectedProject = useSelector((store) => store.localVariables.projectId); // var selectedProject = useSelector((store) => store.localVariables.projectId);
const selectedProject = useSelectedProject(); const selectedProject = useSelectedProject();
const [regularizesList, setregularizedList] = useState([]); const [regularizesList, setregularizedList] = useState([]);
const { regularizes, loading, error, refetch } = const { regularizes, loading, error, refetch } = useRegularizationRequests(
useRegularizationRequests(selectedProject, organizationId, IncludeInActive); selectedProject,
organizationId,
IncludeInActive
);
useEffect(() => { useEffect(() => {
if(!regularizes) return
if(regularizes?.length) {
setregularizedList(regularizes); setregularizedList(regularizes);
}
}, [regularizes]); }, [regularizes]);
const sortByName = (a, b) => { const sortByName = (a, b) => {
@ -54,48 +72,15 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
} }
const lowercasedSearchTerm = searchTerm.toLowerCase(); const lowercasedSearchTerm = searchTerm.toLowerCase();
return sortedList.filter((item) => { return sortedList.filter((item) => {
const fullName = `${item.firstName} ${item.lastName}`.toLowerCase(); const fullName = `${item?.firstName} ${item?.lastName}`.toLowerCase();
return fullName.includes(lowercasedSearchTerm); return fullName.includes(lowercasedSearchTerm);
}); });
}, [regularizesList, searchTerm]); }, [regularizesList, searchTerm]);
// const filteredSearchData = useMemo(() => { const { currentPage, totalPages, currentItems, paginate } = usePagination(
// let sortedList = [...regularizesList].sort(sortByName); filteredSearchData,
20
// // 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]);
useEffect(() => { useEffect(() => {
eventBus.on("regularization", handler); eventBus.on("regularization", handler);
@ -117,9 +102,15 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
}, [employeeHandler]); }, [employeeHandler]);
return ( return (
<div className="table-responsive text-nowrap pb-4" style={{ minHeight: "200px" }}> <div
className="table-responsive text-nowrap pb-4"
style={{ minHeight: "200px" }}
>
{loading ? ( {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> <p className="text-secondary">Loading...</p>
</div> </div>
) : currentItems?.length > 0 ? ( ) : currentItems?.length > 0 ? (
@ -135,18 +126,21 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
<th> <th>
<i className="bx bxs-up-arrow-alt text-danger"></i>Check-Out <i className="bx bxs-up-arrow-alt text-danger"></i>Check-Out
</th> </th>
<th colSpan={2}>
Requested By
</th>
<th >
Requested At
</th>
<th>Action</th> <th>Action</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{currentItems?.map((att, index) => ( {currentItems?.map((att, index) => (
<tr key={index}> <tr key={index}>
<td colSpan={2}> <td colSpan={2}>
<div className="d-flex justify-content-start align-items-center"> <div className="d-flex justify-content-start align-items-center">
<Avatar <Avatar firstName={att.firstName} lastName={att.lastName} />
firstName={att.firstName}
lastName={att.lastName}
/>
<div className="d-flex flex-column"> <div className="d-flex flex-column">
<a href="#" className="text-heading text-truncate"> <a href="#" className="text-heading text-truncate">
<span className="fw-normal"> <span className="fw-normal">
@ -162,7 +156,23 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
<td>{convertShortTime(att.checkInTime)}</td> <td>{convertShortTime(att.checkInTime)}</td>
<td> <td>
{att.checkOutTime ? convertShortTime(att.checkOutTime) : "--"} {att.requestedAt ? convertShortTime(att.checkOutTime) : "--"}
</td>
<td colSpan={2}>
{att.requestedBy ? ( <div className="d-flex justify-content-start align-items-center">
<Avatar firstName={att?.requestedBy?.firstName} lastName={att?.requestedBy?.lastName} />
<div className="d-flex flex-column">
<a href="#" className="text-heading text-truncate">
<span className="fw-normal">
{att?.requestedBy?.firstName} {att?.requestedBy?.lastName}
</span>
</a>
</div>
</div>):(<small>--</small>)}
</td>
<td>
{att?.requestedAt ? formatUTCToLocalTime(att.requestedAt,true) : "--"}
</td> </td>
<td className="text-center "> <td className="text-center ">
<RegularizationActions <RegularizationActions
@ -175,7 +185,6 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
))} ))}
</tbody> </tbody>
</table> </table>
) : ( ) : (
<div <div
className="d-flex justify-content-center align-items-center" className="d-flex justify-content-center align-items-center"
@ -188,7 +197,7 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
</span> </span>
</div> </div>
)} )}
{!loading && totalPages > 1 && ( {/* {!loading && totalPages > 1 && (
<nav aria-label="Page "> <nav aria-label="Page ">
<ul className="pagination pagination-sm justify-content-end py-1 mt-3"> <ul className="pagination pagination-sm justify-content-end py-1 mt-3">
<li className={`page-item ${currentPage === 1 ? "disabled" : ""}`}> <li className={`page-item ${currentPage === 1 ? "disabled" : ""}`}>
@ -226,6 +235,14 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
</li> </li>
</ul> </ul>
</nav> </nav>
)} */}
{totalPages > 0 && (
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={paginate}
/>
)} )}
</div> </div>
); );

View File

@ -55,10 +55,11 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
endField="dateTo" endField="dateTo"
resetSignal={resetKey} resetSignal={resetKey}
defaultRange={false} defaultRange={false}
maxDate={new Date()}
/> />
</div> </div>
<div className="row g-2"> <div className="row mb-2">
<SelectMultiple <SelectMultiple
name="buildingIds" name="buildingIds"
label="Building" label="Building"
@ -67,7 +68,7 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
valueKey="id" valueKey="id"
/> />
</div> </div>
<div className="row g-2"> <div className="row mb-2">
<SelectMultiple <SelectMultiple
name="floorIds" name="floorIds"
label="Floor" label="Floor"
@ -76,7 +77,7 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
valueKey="id" valueKey="id"
/> />
</div> </div>
<div className="row g-2"> <div className="row mb-2">
<SelectMultiple <SelectMultiple
name="activityIds" name="activityIds"
label="Activities" label="Activities"

View File

@ -192,7 +192,7 @@ const TaskReportList = () => {
if (isLoading) return <TaskReportListSkeleton />; if (isLoading) return <TaskReportListSkeleton />;
if (isError) return <div>Loading....</div>; if (isError) return <div>Loading....</div>;
return ( return (
<div className="mt-2"> <div className="mt-2 table-responsive text-nowrap">
<table className="table"> <table className="table">
<thead> <thead>
<tr> <tr>

View File

@ -179,7 +179,7 @@ const ListViewContact = ({ data, Pagination }) => {
<tr style={{ height: "200px" }}> <tr style={{ height: "200px" }}>
<td <td
colSpan={contactList.length + 1} colSpan={contactList.length + 1}
className="text-center align-middle" className="text-center border-0 align-middle"
> >
No contacts found No contacts found
</td> </td>

View File

@ -18,7 +18,7 @@ import { defatEmployeeObj, employeeSchema } from "./EmployeeSchema";
import { useOrganizationsList } from "../../hooks/useOrganization"; import { useOrganizationsList } from "../../hooks/useOrganization";
import { ITEMS_PER_PAGE } from "../../utils/constants"; import { ITEMS_PER_PAGE } from "../../utils/constants";
const ManageEmployee = ({ employeeId, onClosed, IsAllEmployee }) => { const ManageEmployee = ({ employeeId, onClosed }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { mutate: updateEmployee, isPending } = useUpdateEmployee(); const { mutate: updateEmployee, isPending } = useUpdateEmployee();
const { const {
@ -72,7 +72,7 @@ const ManageEmployee = ({ employeeId, onClosed, IsAllEmployee }) => {
data.email = null; data.email = null;
} }
const payload = { ...data, IsAllEmployee }; const payload = { ...data };
if (employeeId) { if (employeeId) {
payload.id = employeeId; payload.id = employeeId;

View File

@ -117,6 +117,7 @@ const ExpenseFilterPanel = ({ onApply, handleGroupBy }) => {
endField="endDate" endField="endDate"
resetSignal={resetKey} resetSignal={resetKey}
defaultRange={false} defaultRange={false}
maxDate={new Date()}
/> />
</div> </div>

View File

@ -395,7 +395,7 @@ const ViewExpense = ({ ExpenseId }) => {
<DatePicker <DatePicker
name="reimburseDate" name="reimburseDate"
control={control} control={control}
minDate={data?.createdAt} minDate={data?.transactionDate}
maxDate={new Date()} maxDate={new Date()}
/> />
{errors.reimburseDate && ( {errors.reimburseDate && (

View File

@ -189,7 +189,8 @@ const Header = () => {
className="navbar-nav-right d-flex align-items-center justify-content-between" className="navbar-nav-right d-flex align-items-center justify-content-between"
id="navbar-collapse" id="navbar-collapse"
> >
{showProjectDropdown(location.pathname) && ( <div className="d-flex align-items-center">
{showProjectDropdown(location.pathname) && (
<div className="align-items-center"> <div className="align-items-center">
<i className="rounded-circle bx bx-building-house bx-sm-lg bx-md me-2"></i> <i className="rounded-circle bx bx-building-house bx-sm-lg bx-md me-2"></i>
<div className="btn-group"> <div className="btn-group">
@ -247,6 +248,7 @@ const Header = () => {
</div> </div>
</div> </div>
)} )}
</div>
<ul className="navbar-nav flex-row align-items-center ms-md-auto"> <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"> <li className="nav-item dropdown-shortcuts navbar-dropdown dropdown me-2 me-xl-0">

View File

@ -10,13 +10,15 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { spridSchema } from "./OrganizationSchema"; import { spridSchema } from "./OrganizationSchema";
import { OrgCardSkeleton } from "./OrganizationSkeleton"; import { OrgCardSkeleton } from "./OrganizationSkeleton";
import { useQueryClient } from "@tanstack/react-query";
// Zod schema: only allow exactly 4 digits // Zod schema: only allow exactly 4 digits
const OrgPickerFromSPId = ({ title, placeholder }) => { const OrgPickerFromSPId = ({ title, placeholder }) => {
const { onClose, startStep, flowType, onOpen, prevStep } = const { onClose, startStep, flowType, onOpen, prevStep,orgData } =
useOrganizationModal(); useOrganizationModal();
const clientQuery = useQueryClient()
const { const {
register, register,
handleSubmit, handleSubmit,
@ -35,8 +37,11 @@ const OrgPickerFromSPId = ({ title, placeholder }) => {
const onSubmit = (formdata) => { const onSubmit = (formdata) => {
setSPRID(formdata.spridSearchText); setSPRID(formdata.spridSearchText);
}; };
const handleOrg = (orgId) => {}; const handleCrateOrg = () => {
clientQuery.removeQueries({queryKey:["organization"]})
onOpen({ startStep: 4,orgData:null })
};
const SP = watch("spridSearchText"); const SP = watch("spridSearchText");
return ( return (
<div className="d-block"> <div className="d-block">
@ -126,7 +131,7 @@ const OrgPickerFromSPId = ({ title, placeholder }) => {
<button <button
type="button" type="button"
className="btn btn-sm btn-primary mt-3" className="btn btn-sm btn-primary mt-3"
onClick={() => onOpen({ startStep: 4 })} onClick={handleCrateOrg}
> >
<i className="bx bx-plus-circle me-2"></i> <i className="bx bx-plus-circle me-2"></i>
Create New Organization Create New Organization

View File

@ -142,7 +142,7 @@ const OrganizationsList = ({searchText}) => {
colSpan={organizationsColumns.length + 1} colSpan={organizationsColumns.length + 1}
className="text-center" className="text-center"
> >
<p className="fw-semibold">Not Found</p> <p className="fw-semibold">{isLoading ? "Loading....":"Not Found Organization"}</p>
</td> </td>
</tr> </tr>
)} )}

View File

@ -11,7 +11,8 @@ import { TasksRepository } from "../../repositories/ProjectRepository";
import showToast from "../../services/toastService"; import showToast from "../../services/toastService";
import { import {
useEmployeeForTaskAssign, useEmployeeForTaskAssign,
useProjectAssignedOrganizations, useProjectAssignedOrganizationsName,
useProjectAssignedServices,
useProjectDetails, useProjectDetails,
} from "../../hooks/useProjects"; } from "../../hooks/useProjects";
import eventBus from "../../services/eventBus"; import eventBus from "../../services/eventBus";
@ -95,9 +96,9 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
}, []); }, []);
const selectedProject = useSelectedProject(); const selectedProject = useSelectedProject();
const { data: serviceList, isLoading: isServiceLoading } = useServices(); const { data: serviceList, isLoading: isServiceLoading } = useProjectAssignedServices(selectedProject);
const { data: organizationList, isLoading: isOrgLoading } = const { data: organizationList, isLoading: isOrgLoading } =
useProjectAssignedOrganizations(selectedProject); useProjectAssignedOrganizationsName(selectedProject);
const { data: employees, isLoading: isEmployeeLoading } = const { data: employees, isLoading: isEmployeeLoading } =
useEmployeeForTaskAssign( useEmployeeForTaskAssign(
selectedProject, selectedProject,
@ -274,7 +275,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
) : ( ) : (
<> <>
<option value="">--Select Service--</option> <option value="">--Select Service--</option>
{serviceList?.data?.map((service,index) => ( {serviceList?.map((service,index) => (
<option key={`${service.id}-${index}`} value={service.id}> <option key={`${service.id}-${index}`} value={service.id}>
{service.name} {service.name}
</option> </option>

View File

@ -8,7 +8,7 @@ import {
useGroups, useGroups,
useWorkCategoriesMaster, useWorkCategoriesMaster,
} from "../../../hooks/masterHook/useMaster"; } from "../../../hooks/masterHook/useMaster";
import { useManageTask, useProjectAssignedOrganizations, useProjectAssignedServices } from "../../../hooks/useProjects"; import { useManageTask, useProjectAssignedOrganizationsName, useProjectAssignedServices } from "../../../hooks/useProjects";
import showToast from "../../../services/toastService"; import showToast from "../../../services/toastService";
import Label from "../../common/Label"; import Label from "../../common/Label";
import { useSelectedProject } from "../../../slices/apiDataManager"; import { useSelectedProject } from "../../../slices/apiDataManager";
@ -47,7 +47,7 @@ const TaskModel = ({ project, onSubmit, onClose }) => {
const projectId = useSelectedProject(); const projectId = useSelectedProject();
const { data: assignedServices, isLoading: servicesLoading } = useProjectAssignedServices(projectId); const { data: assignedServices, isLoading: servicesLoading } = useProjectAssignedServices(projectId);
const { data: assignedOrganizations, isLoading: orgLoading } = useProjectAssignedOrganizations(projectId); const { data: assignedOrganizations, isLoading: orgLoading } = useProjectAssignedOrganizationsName(projectId);

View File

@ -108,7 +108,7 @@ const WorkArea = ({ workArea, floor, forBuilding }) => {
<ProgressBar <ProgressBar
completedWork={formatNumber(workArea?.completedWork)} completedWork={formatNumber(workArea?.completedWork)}
plannedWork={formatNumber(workArea?.plannedWork)} 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>
</div> </div>

View File

@ -327,7 +327,7 @@ const ManageProjectInfo = ({ project, onClose }) => {
<i className="bx bx-sm bx-info-circle"></i> Not found PMC and <i className="bx bx-sm bx-info-circle"></i> Not found PMC and
Pomoter, find through SPRID or create new Pomoter, find through SPRID or create new
</span> </span>
<small className="cursor-pointer" onClick={handleOrganizaioFinder}> <small className="cursor-pointer" onClick={handleOrganizaioFinder} >
<i className="bx bx-plus-circle text-primary"></i> <i className="bx bx-plus-circle text-primary"></i>
</small> </small>
</div> </div>
@ -367,14 +367,14 @@ const ManageProjectInfo = ({ project, onClose }) => {
className="btn btn-label-secondary btn-sm me-2" className="btn btn-label-secondary btn-sm me-2"
onClick={handleCancel} onClick={handleCancel}
aria-label="Close" aria-label="Close"
disabled={isPending || isCreating} disabled={isPending || isCreating || loading}
> >
Cancel Cancel
</button> </button>
<button <button
type="submit" type="submit"
className="btn btn-primary btn-sm" className="btn btn-primary btn-sm"
disabled={isPending || isCreating} disabled={isPending || isCreating || loading}
> >
{isPending||isCreating ? "Please Wait..." : project ? "Update" : "Submit"} {isPending||isCreating ? "Please Wait..." : project ? "Update" : "Submit"}
</button> </button>

View File

@ -132,11 +132,15 @@ const ProjectListView = ({
return ( return (
<div className="card page-min-h py-4 px-6 shadow-sm"> <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"> <table className="table table-hover align-middle m-0">
<thead className="border-bottom"> <thead className="border-bottom ">
<tr> <tr>
{projectColumns.map((col) => ( {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} {col.label}
</th> </th>
))} ))}
@ -217,6 +221,7 @@ const ProjectListView = ({
))} ))}
</tbody> </tbody>
</table> </table>
</div>
{isLoading && ( {isLoading && (
<div className="py-4"> <div className="py-4">

View File

@ -2,7 +2,7 @@ import React, { useState } from "react";
import TeamEmployeeList from "./TeamEmployeeList"; import TeamEmployeeList from "./TeamEmployeeList";
import { useOrganization } from "../../../hooks/useDirectory"; import { useOrganization } from "../../../hooks/useDirectory";
import { useOrganizationsList } from "../../../hooks/useOrganization"; import { useOrganizationsList } from "../../../hooks/useOrganization";
import { useProjectAssignedOrganizations } from "../../../hooks/useProjects"; import { useProjectAssignedOrganizationsName } from "../../../hooks/useProjects";
import { useSelectedProject } from "../../../slices/apiDataManager"; import { useSelectedProject } from "../../../slices/apiDataManager";
const TeamAssignToProject = ({ closeModal }) => { const TeamAssignToProject = ({ closeModal }) => {
@ -10,13 +10,13 @@ const TeamAssignToProject = ({ closeModal }) => {
const [selectedOrg, setSelectedOrg] = useState(null); const [selectedOrg, setSelectedOrg] = useState(null);
const project = useSelectedProject(); const project = useSelectedProject();
const { data, isLoading, isError, error } = const { data, isLoading, isError, error } =
useProjectAssignedOrganizations(project); useProjectAssignedOrganizationsName(project);
return ( return (
<div className="container"> <div className="container">
<p className="fs-5 fs-seminbod ">Assign Employee To Project </p> <p className="fs-5 fs-seminbod ">Assign Employee To Project </p>
<div class="row align-items-center gx-5"> <div className="row align-items-center gx-5">
<div class="col"> <div className="col">
<div className="d-flex flex-grow-1 align-items-center gap-2"> <div className="d-flex flex-grow-1 align-items-center gap-2">
{isLoading ? ( {isLoading ? (
<select className="form-select form-select-sm w-100" disabled> <select className="form-select form-select-sm w-100" disabled>
@ -49,7 +49,7 @@ const TeamAssignToProject = ({ closeModal }) => {
)} )}
</div> </div>
</div> </div>
<div class="col"> <div className="col">
<div className="d-flex flex-grow-1 align-items-center gap-2"> <div className="d-flex flex-grow-1 align-items-center gap-2">
<label htmlFor="search" className="form-label mb-0 text-nowrap"> <label htmlFor="search" className="form-label mb-0 text-nowrap">
Search Employee Search Employee

View File

@ -2,13 +2,14 @@ import React, { useState, useEffect } from "react";
import Avatar from "../../common/Avatar"; import Avatar from "../../common/Avatar";
import { useDebounce } from "../../../utils/appUtils"; import { useDebounce } from "../../../utils/appUtils";
import { useSelectedProject } from "../../../slices/apiDataManager"; import { useSelectedProject } from "../../../slices/apiDataManager";
import { useOrganizationEmployees } from "../../../hooks/useOrganization";
import { import {
useEmployeesByProjectAllocated, useEmployeesByProjectAllocated,
useManageProjectAllocation, useManageProjectAllocation,
useProjectAssignedServices,
} from "../../../hooks/useProjects"; } from "../../../hooks/useProjects";
import useMaster, { useServices } from "../../../hooks/masterHook/useMaster"; import useMaster, { useServices } from "../../../hooks/masterHook/useMaster";
import showToast from "../../../services/toastService"; import showToast from "../../../services/toastService";
import { useOrganizationEmployees } from "../../../hooks/useOrganization";
const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => { const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
const selectedProject = useSelectedProject(); const selectedProject = useSelectedProject();
@ -29,7 +30,7 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
useEmployeesByProjectAllocated(selectedProject, null); useEmployeesByProjectAllocated(selectedProject, null);
const { data: jobRoles } = useMaster(); const { data: jobRoles } = useMaster();
const { data: services } = useServices(); const { data: services } = useProjectAssignedServices(selectedProject);
const [employees, setEmployees] = useState([]); const [employees, setEmployees] = useState([]);
@ -187,7 +188,7 @@ if (employees.length === 0) {
}`} }`}
> >
<option value="">Select Service</option> <option value="">Select Service</option>
{services?.data?.map((s) => ( {services?.map((s) => (
<option key={s.id} value={s.id}> <option key={s.id} value={s.id}>
{s.name} {s.name}
</option> </option>

View File

@ -272,7 +272,7 @@ const Teams = () => {
className="text-heading text-truncate cursor-pointer" className="text-heading text-truncate cursor-pointer"
> >
<span className="fw-normal"> <span className="fw-normal">
{emp.firstName} {emp.firstName}{" "}
{emp.lastName} {emp.lastName}
</span> </span>
</a> </a>

View File

@ -14,7 +14,7 @@ const SkeletonCell = ({ width = "100%", height = 20, style = {} }) => (
export const TenantTableSkeleton = ({ columns, rows = 5 }) => { export const TenantTableSkeleton = ({ columns, rows = 5 }) => {
return ( return (
<div className="card p-2 mt-3"> <div className="p-2 mt-3">
<div className="card-datatable text-nowrap table-responsive"> <div className="card-datatable text-nowrap table-responsive">
<table className="table border-top dataTable text-nowrap"> <table className="table border-top dataTable text-nowrap">
<thead> <thead>

View File

@ -137,7 +137,7 @@ const TenantsList = ({
return ( return (
<> <>
<div className="p-2 mt-3"> <div className="p-2 mt-3">
<div className="card-datatable text-nowrap table-responsive"> <div className=" text-nowrap table-responsive">
<table className="table border-top dataTable text-nowrap"> <table className="table border-top dataTable text-nowrap">
<thead> <thead>
<tr className="shadow-sm"> <tr className="shadow-sm">

View File

@ -3,8 +3,8 @@ import { useController, useFormContext, useWatch } from "react-hook-form";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
const DateRangePicker = ({ const DateRangePicker = ({
md, md=12,
sm, sm=6,
onRangeChange, onRangeChange,
DateDifference = 7, DateDifference = 7,
endDateMode = "yesterday", endDateMode = "yesterday",
@ -63,21 +63,22 @@ const DateRangePicker = ({
}; };
return ( return (
<div className={`col-${sm} col-sm-${md} px-1`}> <div className={`position-relative w-auto justify-content-center`}>
<input <input
type="text" 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" placeholder="From to End"
id="flatpickr-range" id="flatpickr-range"
ref={inputRef} ref={inputRef}
/> />
<i
className="bx bx-calendar calendar-icon cursor-pointer position-absolute top-50 end-0 translate-middle-y me-2 "
onClick={handleIconClick}
/>
</div>
<i
className="bx bx-calendar calendar-icon cursor-pointer position-relative top-50 translate-middle-y"
onClick={handleIconClick}
style={{ right: "22px", bottom: "-8px" }}
/>
</div>
); );
}; };

View File

@ -3,35 +3,65 @@ import React from "react";
const ProgressBar = ({ const ProgressBar = ({
plannedWork = 100, plannedWork = 100,
completedWork = 0, completedWork = 0,
height = "8px", height = "6px",
className = "mb-4", className = "mb-4",
rounded = true, rounded = true,
showLabel = true,
}) => { }) => {
const getProgress = (planned, completed) => { const getProgress = (planned, completed) => {
if (!planned || planned === 0) return "0%"; if (!planned || planned === 0) return 0;
return `${Math.min((completed / planned) * 100, 100).toFixed(2)}%`; return Math.min((completed / planned) * 100, 100);
}; };
const progressStyle = { const percentage = getProgress(plannedWork, completedWork);
width: getProgress(plannedWork, completedWork),
const progressBarStyle = {
width: ` ${percentage.toFixed(2)}%`,
transition: "width 0.4s ease",
};
const containerStyle = {
height,
display: "flex",
alignItems: "center",
gap: "8px",
}; };
return ( return (
<div <div
className={`progress ${className} ${rounded ? "rounded" : ""}`} className={`d-flex align-items-center ${className}`}
style={{ height }} style={containerStyle}
> >
<div <div className="flex-grow-1">
className={`progress-bar ${rounded ? "rounded" : ""}`} <div
role="progressbar" className={`progress ${rounded ? "rounded" : ""}`}
style={progressStyle} style={{ height, backgroundColor: "#f0f0f0" }}
aria-valuenow={completedWork} >
aria-valuemin="0" <div
aria-valuemax={plannedWork} className={`progress-bar ${rounded ? "rounded" : ""}`}
></div> role="progressbar"
style={progressBarStyle}
aria-valuenow={completedWork}
aria-valuemin="0"
aria-valuemax={plannedWork}
/>
</div>
</div>
{showLabel && (
<span
className="fw-semibold text-secondary"
style={{
minWidth: "45px",
textAlign: "right",
fontSize: "0.8rem",
}}
>
{percentage.toFixed(2)}%
</span>
)}
</div> </div>
); );
}; };
export default ProgressBar; export default ProgressBar;

View File

@ -60,6 +60,7 @@ const GalleryFilterPanel = ({ onApply }) => {
endField="endDate" endField="endDate"
resetSignal={resetKey} resetSignal={resetKey}
defaultRange={false} defaultRange={false}
maxDate={new Date()}
/> />
</div> </div>
<div className="mb-2"> <div className="mb-2">

View File

@ -41,7 +41,6 @@ const EditContactTag = ({ data, onClose }) => {
name: formData.name, name: formData.name,
description: formData.description, description: formData.description,
}; };
debugger
updateContactTag({ id: data?.id, payload }); updateContactTag({ id: data?.id, payload });
} }
// const onSubmit = (formdata) => { // const onSubmit = (formdata) => {

View File

@ -70,12 +70,12 @@ const MasterModal = ({ modaldata, closeModal }) => {
}; };
return ( return (
<> <div className="p-2 p-md-1">
<div className="text-center"> <div className="text-center">
<p className="fs-5 fw-semibold" >{`${masterType, " ", modalType}`}</p> <p className="fs-5 fw-semibold" >{`${masterType, " ", modalType}`}</p>
</div> </div>
{ modalComponents[modalType] || null} { modalComponents[modalType] || null}
</> </div>
); );
}; };

View File

@ -81,8 +81,8 @@ const ServiceGroups = ({ service }) => {
) : ( ) : (
<div className="accordion-body text-start m-0 p-0"> <div className="accordion-body text-start m-0 p-0">
<div className="dropdown-divider border"></div> <div className="dropdown-divider border"></div>
{!isLoading && groups?.data?.length === 0 && (<div className="text-center py-2"><p>No Group available.</p></div>)}
{groups?.data?.map((group) => { {groups && groups?.data?.map((group) => {
const isOpen = activeGroupId === group.id; const isOpen = activeGroupId === group.id;
return ( return (

View File

@ -570,7 +570,6 @@ export const useUpdateContactTag = (onSuccessCallback) => {
return useMutation({ return useMutation({
mutationFn: async ({ id, payload }) => { mutationFn: async ({ id, payload }) => {
debugger;
const response = await MasterRespository.updateContactTag(id, payload); const response = await MasterRespository.updateContactTag(id, payload);
return response.data; return response.data;
}, },
@ -704,13 +703,13 @@ export const useCreateService = (onSuccessCallback) => {
return useMutation({ return useMutation({
mutationFn: async (payload) => { mutationFn: async (payload) => {
debugger;
const resp = await MasterRespository.createService(payload); const resp = await MasterRespository.createService(payload);
debugger;
return resp.data; return resp.data;
}, },
onSuccess: (data) => { onSuccess: (data) => {
debugger;
queryClient.invalidateQueries({ queryKey: ["masterData", "Services"] }); queryClient.invalidateQueries({ queryKey: ["masterData", "Services"] });
showToast(data?.message || "Service added successfully", "success"); showToast(data?.message || "Service added successfully", "success");
@ -718,7 +717,7 @@ export const useCreateService = (onSuccessCallback) => {
if (onSuccessCallback) onSuccessCallback(data?.data); if (onSuccessCallback) onSuccessCallback(data?.data);
}, },
onError: (error) => { onError: (error) => {
debugger;
showToast( error?.response?.data?.message || error?.message || "Something went wrong", "error"); showToast( error?.response?.data?.message || error?.message || "Something went wrong", "error");
}, },
}); });
@ -759,7 +758,7 @@ export const useCreateActivityGroup = (onSuccessCallback) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: ["groups"], queryKey: ["groups"],
}); });
debugger
showToast( data?.message || showToast( data?.message ||
data?.response?.data?.message || "Activity Group created successfully.", data?.response?.data?.message || "Activity Group created successfully.",
"success" "success"

View File

@ -16,6 +16,7 @@ import {
toggleModal, toggleModal,
} from "../slices/localVariablesSlice.jsx"; } from "../slices/localVariablesSlice.jsx";
import { removeSession } from "../utils/authUtils.js"; import { removeSession } from "../utils/authUtils.js";
import { cacheProfileData } from "../slices/apiDataManager.jsx";
// ----------------------------Modal-------------------------- // ----------------------------Modal--------------------------
@ -84,6 +85,8 @@ export const useAuthModal = () => {
export const useLogout = () => { export const useLogout = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const naviget = useNavigate()
const dispatch = useDispatch()
return useMutation({ return useMutation({
mutationFn: async () => { mutationFn: async () => {
@ -96,9 +99,12 @@ export const useLogout = () => {
}, },
onSuccess: (data) => { onSuccess: (data) => {
queryClient.clear()
removeSession(); removeSession();
dispatch(cacheProfileData(null))
window.location.href = "/auth/login"; // window.location.href = "/auth/login";
naviget("/auth/login",{replace:true})
if (onSuccessCallBack) onSuccessCallBack(); if (onSuccessCallBack) onSuccessCallBack();
}, },

View File

@ -12,14 +12,14 @@ import { queryClient } from "../layouts/AuthLayout";
// Query --------------------------------------------------------------------------- // Query ---------------------------------------------------------------------------
export const useEmployee = (employeeId) => { export const useEmployee = (employeeId) => {
return useQuery({ return useQuery({
queryKey: ["employeeProfile", employeeId], queryKey: ["employeeProfile", employeeId],
queryFn: async () => { queryFn: async () => {
const res = await EmployeeRepository.getEmployeeProfile(employeeId) const res = await EmployeeRepository.getEmployeeProfile(employeeId);
return res.data; return res.data;
}, },
enabled:!!employeeId enabled: !!employeeId,
}); });
}; };
export const useAllEmployees = (showInactive) => { export const useAllEmployees = (showInactive) => {
@ -137,7 +137,6 @@ export const useEmployeesAllOrByProjectId = (
return res.data; return res.data;
} }
}; };
const { const {
data: employees = [], data: employees = [],
isLoading, 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 // ManageEmployee.jsx
export const useEmployeeProfile = (employeeId) => { export const useEmployeeProfile = (employeeId) => {
const isEnabled = !!employeeId; const isEnabled = !!employeeId;
@ -184,11 +195,11 @@ export const useEmployeeProfile = (employeeId) => {
}; };
}; };
export const useEmployeesName = (projectId, search,allEmployee) => { export const useEmployeesName = (projectId, search, allEmployee) => {
return useQuery({ return useQuery({
queryKey: ["employees", projectId, search,allEmployee], queryKey: ["employees", projectId, search, allEmployee],
queryFn: async () => queryFn: async () =>
await EmployeeRepository.getEmployeeName(projectId, search,allEmployee), await EmployeeRepository.getEmployeeName(projectId, search, allEmployee),
staleTime: 5 * 60 * 1000, // Optional: cache for 5 minutes staleTime: 5 * 60 * 1000, // Optional: cache for 5 minutes
}); });
@ -208,9 +219,6 @@ export const useEmployeesNameByProject = (projectId) => {
// Mutation------------------------------------------------------------------ // Mutation------------------------------------------------------------------
export const useUpdateEmployee = () => { export const useUpdateEmployee = () => {
const selectedProject = useSelector( const selectedProject = useSelector(
(store) => store.localVariables.projectId (store) => store.localVariables.projectId
@ -225,7 +233,7 @@ export const useUpdateEmployee = () => {
const isAllEmployee = variables.IsAllEmployee; const isAllEmployee = variables.IsAllEmployee;
// Cache invalidation // Cache invalidation
queryClient.invalidateQueries({ queryKey: ["employeeProfile",id] }); queryClient.invalidateQueries({ queryKey: ["employeeProfile", id] });
queryClient.invalidateQueries({ queryKey: ["allEmployees"] }); queryClient.invalidateQueries({ queryKey: ["allEmployees"] });
// queryClient.invalidateQueries(['employeeProfile', id]); // queryClient.invalidateQueries(['employeeProfile', id]);
queryClient.invalidateQueries({ queryKey: ["projectEmployees"] }); queryClient.invalidateQueries({ queryKey: ["projectEmployees"] });
@ -247,7 +255,6 @@ export const useUpdateEmployee = () => {
}); });
}; };
export const useSuspendEmployee = ({ export const useSuspendEmployee = ({
setIsDeleteModalOpen, setIsDeleteModalOpen,
setemployeeLodaing, setemployeeLodaing,
@ -300,7 +307,6 @@ export const useSuspendEmployee = ({
}); });
}; };
export const useUpdateEmployeeRoles = ({ export const useUpdateEmployeeRoles = ({
onClose, onClose,
resetForm, resetForm,
@ -334,4 +340,4 @@ export const useUpdateEmployeeRoles = ({
isError: mutation.isError, isError: mutation.isError,
error: mutation.error, error: mutation.error,
}; };
}; };

View File

@ -24,7 +24,7 @@ export const useOrganizationModal = () => {
dispatch( dispatch(
openOrgModal({ openOrgModal({
isOpen: true, isOpen: true,
orgData: options.orgData ?? orgData ?? null, orgData: options.hasOwnProperty("orgData") ? options.orgData : orgData,
startStep: options.startStep ?? startStep ?? 1, startStep: options.startStep ?? startStep ?? 1,
prevStep: options.prevStep ?? prevStep ?? 1, prevStep: options.prevStep ?? prevStep ?? 1,
flowType: options.flowType ?? flowType ?? "default", flowType: options.flowType ?? flowType ?? "default",
@ -138,8 +138,12 @@ export const useAssignOrgToProject = (onSuccessCallback) => {
useClient.invalidateQueries({ useClient.invalidateQueries({
queryKey: ["projectAssignedOrganiztions"], queryKey: ["projectAssignedOrganiztions"],
}); });
useClient.invalidateQueries({
queryKey: ["projectAssignedOrganiztionsName"],
});
useClient.invalidateQueries({ useClient.invalidateQueries({
queryKey: ["projectAssignedOrganization", projectId], queryKey: ["projectAssignedServices", projectId],
}); });
showToast("Organization successfully", "success"); showToast("Organization successfully", "success");
if (onSuccessCallback) onSuccessCallback(); if (onSuccessCallback) onSuccessCallback();
@ -181,6 +185,8 @@ export const useUpdateOrganization = (onSuccessCallback) => {
await OrganizationRepository.updateOrganizaion(orgId,payload), await OrganizationRepository.updateOrganizaion(orgId,payload),
onSuccess: (_, variables) => { onSuccess: (_, variables) => {
useClient.invalidateQueries({ queryKey: ["organizationList"] }); useClient.invalidateQueries({ queryKey: ["organizationList"] });
useClient.invalidateQueries({ queryKey: ["projectAssignedOrganiztionsName"] });
showToast("Organization Updated successfully", "success"); showToast("Organization Updated successfully", "success");
if (onSuccessCallback) onSuccessCallback(); if (onSuccessCallback) onSuccessCallback();
}, },

View File

@ -294,9 +294,21 @@ export const useProjectAssignedOrganizations = (projectId) => {
enabled: !!projectId, enabled: !!projectId,
}); });
}; };
export const useProjectAssignedOrganizationsName = (projectId) => {
return useQuery({
queryKey: ["projectAssignedOrganiztionsName", projectId],
queryFn: async () => {
const resp = await ProjectRepository.getProjectAssignedOrganizationsName(
projectId
);
return resp.data;
},
enabled: !!projectId,
});
};
export const useProjectAssignedServices = (projectId) => { export const useProjectAssignedServices = (projectId) => {
return useQuery({ return useQuery({
queryKey: ["projectAssignedOrganization", projectId], queryKey: ["projectAssignedServices", projectId],
queryFn: async () => { queryFn: async () => {
const resp = await ProjectRepository.getProjectAssignedServices( const resp = await ProjectRepository.getProjectAssignedServices(
projectId projectId

View File

@ -17,7 +17,7 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { REGULARIZE_ATTENDANCE } from "../../utils/constants"; import { REGULARIZE_ATTENDANCE } from "../../utils/constants";
import eventBus from "../../services/eventBus"; import eventBus from "../../services/eventBus";
import { import {
useProjectAssignedOrganizations, useProjectAssignedOrganizationsName,
useProjectName, useProjectName,
} from "../../hooks/useProjects"; } from "../../hooks/useProjects";
import GlobalModel from "../../components/common/GlobalModel"; import GlobalModel from "../../components/common/GlobalModel";
@ -47,7 +47,7 @@ const AttendancePage = () => {
}); });
const { data: organizations = [], isLoading: orgLoading } = const { data: organizations = [], isLoading: orgLoading } =
useProjectAssignedOrganizations(selectedProject); useProjectAssignedOrganizationsName(selectedProject);
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
markTime: "", markTime: "",
@ -131,7 +131,7 @@ const AttendancePage = () => {
<div className="nav-align-top nav-tabs-shadow "> <div className="nav-align-top nav-tabs-shadow ">
{/* Tabs */} {/* Tabs */}
<div className="nav-align-top nav-tabs-shadow bg-white border-bottom pt-5"> <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 */} {/* Tabs */}
<div className="col-12 col-md"> <div className="col-12 col-md">
<ul className="nav nav-tabs" role="tablist"> <ul className="nav nav-tabs" role="tablist">
@ -181,9 +181,10 @@ const AttendancePage = () => {
{/* Search + Organization filter */} {/* 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"> <div className="col-12 col-md-auto mt-2 mt-md-0 ms-md-auto d-flex gap-2 align-items-center">
{/* Organization Dropdown */} {/* Organization Dropdown */}
<select <div className="row">
<div className="col-12 col-sm-6 mb-2 mb-sm-0">
<select
className="form-select form-select-sm" className="form-select form-select-sm"
style={{ minWidth: "180px" }}
value={appliedFilters.selectedOrganization} value={appliedFilters.selectedOrganization}
onChange={(e) => onChange={(e) =>
setAppliedFilters((prev) => ({ setAppliedFilters((prev) => ({
@ -200,16 +201,21 @@ const AttendancePage = () => {
</option> </option>
))} ))}
</select> </select>
</div>
{/* Search Input */} <div className="col-12 col-sm-6">
{/* Search Input */}
<input <input
type="text" type="text"
className="form-control form-control-sm" className="form-control form-control-sm"
placeholder="Search Employee..." placeholder="Search Employee..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
style={{ minWidth: "200px" }}
/> />
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,6 +8,8 @@ import ReportTask from "../../components/Activities/ReportTask";
import TaskReportFilterPanel from "../../components/DailyProgressRport/TaskReportFilterPanel"; import TaskReportFilterPanel from "../../components/DailyProgressRport/TaskReportFilterPanel";
import { useFab } from "../../Context/FabContext"; import { useFab } from "../../Context/FabContext";
import SubTask from "../../components/Activities/SubTask"; import SubTask from "../../components/Activities/SubTask";
import { useProjectAssignedServices } from "../../hooks/useProjects";
import { useSelectedProject } from "../../slices/apiDataManager";
const DailyProgrssContext = createContext(); const DailyProgrssContext = createContext();
export const useDailyProgrssContext = () => { export const useDailyProgrssContext = () => {
@ -21,10 +23,11 @@ export const useDailyProgrssContext = () => {
}; };
const DailyProgrssReport = () => { const DailyProgrssReport = () => {
const selectedProject = useSelectedProject()
const [service, setService] = useState(""); const [service, setService] = useState("");
const [filter,setFilter] = useState('') const [filter,setFilter] = useState('')
const { setOffcanvasContent, setShowTrigger } = useFab(); 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 }); const [modal, setModal] = useState({ type: null, data: null });
@ -86,7 +89,7 @@ const DailyProgrssReport = () => {
/> />
<div className="card card-fullscreen p-5"> <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 <select
className="form-select form-select-sm" className="form-select form-select-sm"
value={service ?? ""} value={service ?? ""}
@ -99,7 +102,7 @@ const DailyProgrssReport = () => {
<option value=""> <option value="">
All Services All Services
</option> </option>
{data?.data?.map((service) => ( {data?.map((service) => (
<option key={service.id} value={service.id}> <option key={service.id} value={service.id}>
{service.name} {service.name}
</option> </option>

View File

@ -162,29 +162,33 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
</ul> </ul>
</div> </div>
<div className="mb-1 px-2 py-3"> <div className="mb-1 px-md-2 px-0 py-3">
<div className="d-flex align-items-center justify-content-between"> <div className="row">
<div className="d-flex align-items-center gap-3"> <div className="col-12 col-md-10 mb-2">
{activeTab === "notes" && ( {activeTab === "notes" && (
<input <div className="col-8 col-md-3">
<input
type="search" type="search"
className="form-control form-control-sm" className="form-control form-control-sm"
placeholder="Search notes..." placeholder="Search notes..."
value={searchNote} value={searchNote}
onChange={(e) => setSearchNote(e.target.value)} onChange={(e) => setSearchNote(e.target.value)}
/> />
</div>
)} )}
{activeTab === "contacts" && ( {activeTab === "contacts" && (
<div className="d-flex align-items-center gap-3"> <div className="d-flex align-items-center gap-3">
<div className="d-flex gap-2 align-items-center"> <div className="col-12 col-md-8 d-flex flex-row gap-2">
<input <div className="col-7 col-md-4">
<input
type="search" type="search"
className="form-control form-control-sm" className="form-control form-control-sm"
placeholder="Search contacts..." placeholder="Search contacts..."
value={searchContact} value={searchContact}
onChange={(e) => setsearchContact(e.target.value)} onChange={(e) => setsearchContact(e.target.value)}
/> />
</div>
<button <button
className={`btn btn-sm p-1 ${ className={`btn btn-sm p-1 ${
!gridView ? "btn-primary" : "btn-outline-primary" !gridView ? "btn-primary" : "btn-outline-primary"
@ -202,9 +206,7 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
> >
<i className="bx bx-grid-alt"></i> <i className="bx bx-grid-alt"></i>
</button> </button>
</div> <div className="form-check form-switch d-flex align-items-center d-none d-md-flex">
<div className="form-check form-switch d-flex align-items-center">
<input <input
type="checkbox" type="checkbox"
className="form-check-input" className="form-check-input"
@ -220,11 +222,30 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
{showActive ? "Active" : "Inactive"} Contacts {showActive ? "Active" : "Inactive"} Contacts
</label> </label>
</div> </div>
</div>
</div> </div>
)} )}
</div> </div>
<div className="col-12 col-md-2 d-flex justify-content-end align-items-center gap-2">
<div className="btn-group"> <div className={`form-check form-switch d-flex align-items-center ${activeTab === "contacts" ? " d-flex d-md-none m-0":"d-none" }`}>
<input
type="checkbox"
className="form-check-input"
role="switch"
id="inactiveEmployeesCheckbox"
checked={showActive}
onChange={(e) => setShowActive(e.target.checked)}
/>
<label
className="form-check-label ms-2"
htmlFor="inactiveEmployeesCheckbox"
>
{showActive ? "Active" : "Inactive"} Contacts
</label>
</div>
<div className=" btn-group">
<button <button
className="btn btn-sm btn-label-secondary dropdown-toggle" className="btn btn-sm btn-label-secondary dropdown-toggle"
type="button" type="button"
@ -244,6 +265,8 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
</li> </li>
</ul> </ul>
</div> </div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -7,6 +7,7 @@ import Breadcrumb from "../../components/common/Breadcrumb";
import ManageEmp from "../../components/Employee/ManageRole"; import ManageEmp from "../../components/Employee/ManageRole";
import { import {
useEmployeesAllOrByProjectId, useEmployeesAllOrByProjectId,
useEmployeesByOrganization,
useSuspendEmployee, useSuspendEmployee,
} from "../../hooks/useEmployees"; } from "../../hooks/useEmployees";
import { useProjectName, useProjects } from "../../hooks/useProjects"; import { useProjectName, useProjects } from "../../hooks/useProjects";
@ -36,6 +37,7 @@ import GlobalModel from "../../components/common/GlobalModel";
import usePagination from "../../hooks/usePagination"; import usePagination from "../../hooks/usePagination";
import { setProjectId } from "../../slices/localVariablesSlice"; import { setProjectId } from "../../slices/localVariablesSlice";
import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import Pagination from "../../components/common/Pagination";
const EmployeeList = () => { const EmployeeList = () => {
const selectedProjectId = useSelector( const selectedProjectId = useSelector(
@ -46,15 +48,14 @@ const EmployeeList = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [showInactive, setShowInactive] = useState(false); const [showInactive, setShowInactive] = useState(false);
const [showAllEmployees, setShowAllEmployees] = useState(false);
const Manage_Employee = useHasUserPermission(MANAGE_EMPLOYEES); const Manage_Employee = useHasUserPermission(MANAGE_EMPLOYEES);
const { employees, loading, setLoading, error, recallEmployeeData } = const {
useEmployeesAllOrByProjectId( data: employees,
showAllEmployees, isLoading: loading,
selectedProjectId, error,
showInactive refetch: recallEmployeeData,
); } = useEmployeesByOrganization(showInactive);
const [employeeList, setEmployeeList] = useState([]); const [employeeList, setEmployeeList] = useState([]);
const [modelConfig, setModelConfig] = useState(); const [modelConfig, setModelConfig] = useState();
@ -70,7 +71,6 @@ const EmployeeList = () => {
const [selectedEmpFordelete, setSelectedEmpFordelete] = useState(null); const [selectedEmpFordelete, setSelectedEmpFordelete] = useState(null);
const [employeeLodaing, setemployeeLodaing] = useState(false); const [employeeLodaing, setemployeeLodaing] = useState(false);
const ViewTeamMember = useHasUserPermission(VIEW_TEAM_MEMBERS); const ViewTeamMember = useHasUserPermission(VIEW_TEAM_MEMBERS);
const ViewAllEmployee = useHasUserPermission(VIEW_ALL_EMPLOYEES);
const { mutate: suspendEmployee, isPending: empLodaing } = useSuspendEmployee( const { mutate: suspendEmployee, isPending: empLodaing } = useSuspendEmployee(
{ {
setIsDeleteModalOpen, setIsDeleteModalOpen,
@ -157,7 +157,7 @@ const EmployeeList = () => {
const handleAllEmployeesToggle = (e) => { const handleAllEmployeesToggle = (e) => {
const isChecked = e.target.checked; const isChecked = e.target.checked;
setShowInactive(false); setShowInactive(false);
setShowAllEmployees(isChecked); // setShowAllEmployees(isChecked);
}; };
const handleEmployeeModel = (id) => { const handleEmployeeModel = (id) => {
@ -205,25 +205,16 @@ const EmployeeList = () => {
setCurrentPage((prevPage) => (prevPage !== 1 ? 1 : prevPage)); setCurrentPage((prevPage) => (prevPage !== 1 ? 1 : prevPage));
} }
}, [loading, employees, selectedProjectId, showAllEmployees]); }, [loading, employees, selectedProjectId, showInactive]);
useEffect(() => {
if (!showAllEmployees) {
recallEmployeeData(showInactive, selectedProjectId);
}
}, [selectedProjectId, showInactive, showAllEmployees, recallEmployeeData]);
const handler = useCallback( const handler = useCallback(
(msg) => { (msg) => {
if (employees.some((item) => item.id == msg.employeeId)) { if (employees.some((item) => item.id == msg.employeeId)) {
setEmployeeList([]); setEmployeeList([]);
recallEmployeeData( recallEmployeeData(showInactive);
showInactive,
showAllEmployees ? null : selectedProjectId
); // Use selectedProjectId here
} }
}, },
[employees, showInactive, showAllEmployees, selectedProjectId] // Add all relevant dependencies [employees, showInactive, showInactive, selectedProjectId] // Add all relevant dependencies
); );
useEffect(() => { useEffect(() => {
@ -254,7 +245,6 @@ const EmployeeList = () => {
<ManageEmployee <ManageEmployee
employeeId={selectedEmployeeId} employeeId={selectedEmployeeId}
onClosed={() => setShowModal(false)} onClosed={() => setShowModal(false)}
IsAllEmployee={showAllEmployees}
/> />
</GlobalModel> </GlobalModel>
)} )}
@ -293,59 +283,39 @@ const EmployeeList = () => {
{ViewTeamMember ? ( {ViewTeamMember ? (
// <div className="row"> // <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 className="card-datatable table-responsive pt-5 mx-5 py-10">
<div <div
id="DataTables_Table_0_wrapper" id="DataTables_Table_0_wrapper"
className="dataTables_wrapper dt-bootstrap5 no-footer" 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"> <div className="d-flex flex-wrap align-items-center justify-content-between gap-3 mb-3">
{/* Switches: All Employees + Inactive */} {/* Switches: All Employees + Inactive */}
<div className="d-flex flex-wrap align-items-center gap-3"> <div className="d-flex flex-wrap align-items-center gap-3">
{/* All Employees Switch */} {/* 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 */} {/* Show Inactive Employees Switch */}
{showAllEmployees && (
<div className="form-check form-switch text-start"> <div className="form-check form-switch text-start">
<input <input
type="checkbox" type="checkbox"
className="form-check-input" className="form-check-input"
role="switch" role="switch"
id="inactiveEmployeesCheckbox" id="inactiveEmployeesCheckbox"
checked={showInactive} checked={showInactive}
onChange={(e) => setShowInactive(e.target.checked)} onChange={(e) => setShowInactive(e.target.checked)}
/> />
<label <label
className="form-check-label ms-0" className="form-check-label ms-0"
htmlFor="inactiveEmployeesCheckbox" htmlFor="inactiveEmployeesCheckbox"
> >
Show Inactive Employees Show Inactive Employees
</label> </label>
</div> </div>
)}
</div> </div>
{/* Right side: Search + Export + Add Employee */} {/* 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 */} {/* Search Input - ALWAYS ENABLED */}
<div className="dataTables_filter"> <div className="dataTables_filter">
<label className="mb-0"> <label className="mb-0">
@ -431,7 +401,6 @@ const EmployeeList = () => {
className="datatables-users table border-top dataTable no-footer dtr-column text-nowrap" className="datatables-users table border-top dataTable no-footer dtr-column text-nowrap"
id="DataTables_Table_0" id="DataTables_Table_0"
aria-describedby="DataTables_Table_0_info" aria-describedby="DataTables_Table_0_info"
style={{ width: "100%" }}
ref={tableRef} ref={tableRef}
> >
<thead> <thead>
@ -522,28 +491,27 @@ const EmployeeList = () => {
</td> </td>
</tr> </tr>
)} )}
{/* Conditional messages for no data or no search results */}
{!loading && {!loading &&
displayData?.length === 0 && displayData?.length === 0 &&
searchText && (!searchText ) ? (
!showAllEmployees ? (
<tr> <tr>
<td colSpan={8}> <td colSpan={8} className="border-0 py-3">
<small className="muted"> <div className="py-4">
'{searchText}' employee not found No Data Found
</small>{" "} </div>
</td> </td>
</tr> </tr>
) : null} ) : null}
{!loading &&
{!loading &&
displayData?.length === 0 && displayData?.length === 0 &&
(!searchText || showAllEmployees) ? ( (searchText ) ? (
<tr> <tr>
<td <td colSpan={8} className="border-0 py-3">
colSpan={8} <div className="py-4">
style={{ paddingTop: "20px", textAlign: "center" }} {`No match record found ${searchText} `}
> </div>
No Data Found
</td> </td>
</tr> </tr>
) : null} ) : null}
@ -697,55 +665,12 @@ const EmployeeList = () => {
</tbody> </tbody>
</table> </table>
<div style={{ width: "1%" }}></div> {displayData?.length > 0 && (
<Pagination
{/* Pagination */} currentPage={currentPage}
{!loading && displayData.length > ITEMS_PER_PAGE && ( totalPages={totalPages}
<nav aria-label="Page"> onPageChange={paginate}
<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)}
>
&laquo;
</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)}
>
&raquo;
</button>
</li>
</ul>
</nav>
)} )}
</div> </div>
</div> </div>

View File

@ -103,7 +103,7 @@ const MasterPage = () => {
if (menuErrorFlag || isMasterError) if (menuErrorFlag || isMasterError)
return ( return (
<div className="d-flex flex-column align-items-center justify-content-center py-5"> <div className="d-flex flex-column align-items-center justify-content-center py-1 py-md-5">
<h4 className="mb-3"> <h4 className="mb-3">
<i className="fa-solid fa-triangle-exclamation fs-5" /> Oops, an error <i className="fa-solid fa-triangle-exclamation fs-5" /> Oops, an error
occurred occurred
@ -161,21 +161,17 @@ const MasterPage = () => {
data={[{ label: "Home", link: "/dashboard" }, { label: "Masters" }]} data={[{ label: "Home", link: "/dashboard" }, { label: "Masters" }]}
/> />
<div className="row"> <div className="row page-min-h">
<div className="card"> <div className="card">
<div <div
className="card-datatable table-responsive py-10 mx-5 " className="card-datatable table-responsive py-2 py-md-10 mx-1 mx-md-5 "
style={{ overflow: "hidden" }} style={{ overflow: "hidden" }}
> >
<div className="row mb-2"> <div className="row mb-2">
<div className="col-md-3 col-sm-6"> <div className="col-12 col-md-3">
<select <select
className="form-select py-1 px-2" className="form-select py-1 px-2"
style={{
fontSize: "0.875rem",
height: "32px",
width: "190px",
}}
value={selectedMaster} value={selectedMaster}
onChange={(e) => dispatch(changeMaster(e.target.value))} onChange={(e) => dispatch(changeMaster(e.target.value))}
> >
@ -190,8 +186,8 @@ const MasterPage = () => {
)} )}
</select> </select>
</div> </div>
<div className="col-md-9 col-sm-6 d-flex justify-content-end align-items-center gap-2"> <div className="col-12 col-md-9 d-flex justify-content-end align-items-center gap-2 mt-2 mt-md-0">
<div className="w-25"> <div className="col-6 col-md-3">
<input <input
type="search" type="search"
className="form-control form-control-sm" className="form-control form-control-sm"
@ -207,8 +203,8 @@ const MasterPage = () => {
handleModalData(selectedMaster, null, selectedMaster) handleModalData(selectedMaster, null, selectedMaster)
} }
> >
<i className="bx bx-plus-circle me-2"></i>Add{" "} <i className="bx bx-plus-circle me-2"></i> <span className="d-none d-md-inline-block">Add{" "}
{selectedMaster} {selectedMaster}</span>
</button> </button>
)} )}
</div> </div>

View File

@ -92,7 +92,8 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => {
{" "} {" "}
{selectedMaster === "Activity" ? "Activity" : "Name"} {selectedMaster === "Activity" ? "Activity" : "Name"}
</th> </th>
<th className="text-start"> <th className="text-start d-none d-md-table-cell">
{" "} {" "}
{selectedMaster === "Activity" {selectedMaster === "Activity"
? "Unit" ? "Unit"
@ -108,12 +109,12 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => {
<tbody> <tbody>
{currentItems.length > 0 ? ( {currentItems.length > 0 ? (
currentItems.map((item, index) => ( currentItems.map((item, index) => (
<tr key={index}> <tr key={index} >
<td style={{ width: "20px" }}> <td style={{ width: "20px" }} className="py-3">
<i className="bx bx-right-arrow-alt"></i> <i className="bx bx-right-arrow-alt"></i>
</td> </td>
{updatedColumns.map((col) => ( {updatedColumns.map((col) => (
<td className="text-start mx-2" key={col.key}> <td className={`text-start mx-2 py-3 ${col.key === "description" && "d-none d-md-table-cell"}`} key={col.key} >
{col.key === "description" ? ( {col.key === "description" ? (
item[col.key] !== undefined && item[col.key] !== undefined &&
item[col.key] !== null ? ( item[col.key] !== null ? (
@ -133,7 +134,7 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => {
)} )}
</td> </td>
))} ))}
<td className={!hasMasterPermission ? "d-none" : ""}> <td className={!hasMasterPermission ? "d-none" : "py-3"}>
{(selectedMaster === "Application Role" || {(selectedMaster === "Application Role" ||
selectedMaster === "Work Category") && selectedMaster === "Work Category") &&
item?.isSystem ? ( item?.isSystem ? (

View File

@ -85,6 +85,8 @@ const ProjectRepository = {
api.get(`/api/Project/get/assigned/services/${projectId}`), api.get(`/api/Project/get/assigned/services/${projectId}`),
getProjectAssignedOrganizations: (projectId) => getProjectAssignedOrganizations: (projectId) =>
api.get(`/api/Project/get/assigned/organization/${projectId}`), api.get(`/api/Project/get/assigned/organization/${projectId}`),
getProjectAssignedOrganizationsName: (projectId) =>
api.get(`/api/Project/get/assigned/organization/dropdown/${projectId}`),
getEmployeeForTaskAssign: (projectId, serviceId, organizationId) => { getEmployeeForTaskAssign: (projectId, serviceId, organizationId) => {
let url = `/api/Project/get/task/team/${projectId}`; let url = `/api/Project/get/task/team/${projectId}`;

View File

@ -91,4 +91,47 @@ const date = new Date(
Date.UTC(Number(year), Number(month) - 1, Number(day), 0, 0, 0) Date.UTC(Number(year), Number(month) - 1, Number(day), 0, 0, 0)
); );
return isNaN(date.getTime()) ? null : date.toISOString(); 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);
};

View File

@ -3,6 +3,11 @@ export const DURATION_TIME = 10; // minutes
export const ITEMS_PER_PAGE = 20; export const ITEMS_PER_PAGE = 20;
export const OTP_EXPIRY_SECONDS = 300; // OTP time 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 MANAGE_MASTER = "588a8824-f924-4955-82d8-fc51956cf323";
export const VIEW_MASTER = "5ffbafe0-7ab0-48b1-bb50-c1bf76b65f9d"; 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 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";