setShowModal(false)}>
+ setShowModal(false)}
+ >
setShowModal(false)}
+ onClose={() => setShowModal(false)}
/>
)}
@@ -167,7 +181,6 @@ const WorkItem = ({
: "NA"}
-
{hasWorkItem
@@ -195,9 +208,7 @@ const WorkItem = ({
{hasWorkItem
? `${
- NewWorkItem?.todaysAssigned ??
- workItem?.todaysAssigned ??
- "0"
+ NewWorkItem?.todaysAssigned ?? workItem?.todaysAssigned ?? "0"
}`
: "NA"}
@@ -235,7 +246,7 @@ const WorkItem = ({
{/* Desktop (md and up): inline icons */}
- {!projectId &&
+ {isTaskPlanning &&
ManageAndAssignTak &&
PlannedWork !== CompletedWork && (
setShowModal(true)}
+ onClick={() => setShowModal(true)}
role="button"
>
- {!projectId &&
+ {isTaskPlanning &&
ManageAndAssignTak &&
PlannedWork !== CompletedWork && (
@@ -293,7 +304,7 @@ const WorkItem = ({
setShowModal(true) }
+ onClick={() => setShowModal(true)}
>
Edit
diff --git a/src/components/Project/ProjectCard.jsx b/src/components/Project/ProjectCard.jsx
index 015312fe..17b19572 100644
--- a/src/components/Project/ProjectCard.jsx
+++ b/src/components/Project/ProjectCard.jsx
@@ -57,7 +57,7 @@ const ProjectCard = ({ projectData, recall }) => {
const handleClose = () => setShowModal(false);
const handleViewProject = () => {
- navigate(`/projects/${projectData.id}`);
+ navigate(`/projects/details`);
};
const handleFormSubmit = (updatedProject) => {
@@ -98,7 +98,7 @@ const ProjectCard = ({ projectData, recall }) => {
+ { ReportTaskRights &&
{
>
Report
- {task.reportedDate && (
+ }
+ {(ApprovedTaskRights && task.reportedDate ) && (
{
+ const selectedProject = useSelector(
+ (store) => store.localVariables.projectId
+ );
+
+ const dispatch = useDispatch()
+ const { projectNames, loading: projectLoading, fetchData } = useProjectName();
+ useEffect(() => {
+ if(selectedProject == null){
+ dispatch(setProjectId(projectNames[0]?.id));
+ }
+ },[])
return (
<>
diff --git a/src/pages/Directory/Directory.jsx b/src/pages/Directory/Directory.jsx
index ca95181f..8f72ac81 100644
--- a/src/pages/Directory/Directory.jsx
+++ b/src/pages/Directory/Directory.jsx
@@ -260,7 +260,7 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
}, [prefernceContacts]);
return (
-
+
{IsPage && (
{
- const [images, setImages] = useState([]);
- const [allImagesData, setAllImagesData] = useState([]);
- const [pageNumber, setPageNumber] = useState(1);
- const [hasMore, setHasMore] = useState(true);
const selectedProjectId = useSelector((store) => store.localVariables.projectId);
+
+ const dispatch = useDispatch()
+ const { projectNames, loading: projectLoading, fetchData } = useProjectName();
+ useEffect(() => {
+ if(selectedProjectId == null){
+ dispatch(setProjectId(projectNames[0]?.id));
+ }
+ },[])
+
+ const {
+ images,
+ allImagesData,
+ pageNumber,
+ setPageNumber,
+ hasMore,
+ loading,
+ loadingMore,
+ fetchImages,
+ resetGallery,
+ } = useImageGallery(selectedProjectId);
+
const { openModal } = useModal();
const yesterday = moment().subtract(1, 'days').format('YYYY-MM-DD');
@@ -58,8 +76,6 @@ const ImageGallery = () => {
const [isFilterPanelOpen, setIsFilterPanelOpen] = useState(false);
const [hoveredImage, setHoveredImage] = useState(null);
- const [loading, setLoading] = useState(true);
- const [loadingMore, setLoadingMore] = useState(false);
const imageGroupRefs = useRef({});
const loaderRef = useRef(null);
@@ -91,71 +107,18 @@ const ImageGallery = () => {
useEffect(() => {
if (!selectedProjectId) {
- setImages([]);
- setAllImagesData([]);
- setLoading(false);
- setHasMore(false);
+ resetGallery();
return;
}
- setImages([]);
- setPageNumber(1);
- setHasMore(true);
- setLoading(true);
-
- setAllImagesData([]);
+ resetGallery();
fetchImages(1, appliedFilters, true);
}, [selectedProjectId, appliedFilters]);
- const fetchImages = useCallback(async (page, filters) => {
- if (!selectedProjectId) return;
-
- try {
- if (page === 1) {
- setLoading(true);
- } else {
- setLoadingMore(true);
- }
-
- const res = await ImageGalleryAPI.ImagesGet(selectedProjectId, filters, page, PAGE_SIZE);
- const newBatches = res.data || [];
- const receivedCount = newBatches.length;
-
- setImages((prevImages) => {
- const uniqueNewBatches = newBatches.filter(
- (newBatch) => !prevImages.some((prevBatch) => prevBatch.batchId === newBatch.batchId)
- );
- return [...prevImages, ...uniqueNewBatches];
- });
-
- setAllImagesData((prevAllImages) => {
- const uniqueAllImages = newBatches.filter(
- (newBatch) => !prevAllImages.some((prevBatch) => prevBatch.batchId === newBatch.batchId)
- );
- return [...prevAllImages, ...uniqueAllImages];
- });
-
- setHasMore(receivedCount === PAGE_SIZE);
- } catch (err) {
- console.error("Error fetching images:", err);
- if (page === 1) {
- setImages([]);
- setAllImagesData([]);
- }
- setHasMore(false);
- } finally {
- setLoading(false);
- setLoadingMore(false);
- }
- }, [selectedProjectId]);
-
useEffect(() => {
const handleExternalEvent = (data) => {
if (selectedProjectId === data.projectId) {
- setImages([]);
- setAllImagesData([]);
- setPageNumber(1);
- setHasMore(true);
+ resetGallery();
fetchImages(1, appliedFilters, true);
}
};
@@ -196,20 +159,13 @@ const ImageGallery = () => {
if (pageNumber > 1) {
fetchImages(pageNumber, appliedFilters);
}
- }, [pageNumber, fetchImages, appliedFilters]);
+ }, [pageNumber]);
const getUniqueValuesWithIds = useCallback((idKey, nameKey) => {
const map = new Map();
allImagesData.forEach(batch => {
- let id;
- if (idKey === "floorIds") {
- id = batch.floorIds;
- } else {
- id = batch[idKey];
- }
-
+ let id = idKey === "floorIds" ? batch.floorIds : batch[idKey];
const name = batch[nameKey];
-
if (id && name && !map.has(id)) {
map.set(id, name);
}
@@ -229,7 +185,7 @@ const ImageGallery = () => {
}
});
});
- return Array.from(uniqueUsersMap.entries()).sort((a, b) => a[1].localeCompare(b[1]));
+ return Array.from(uniqueUsersMap.entries()).sort((a, b) => a[1].localeCompare(b[1]));
}, [allImagesData]);
const buildings = getUniqueValuesWithIds("buildingId", "buildingName");
@@ -272,30 +228,12 @@ const ImageGallery = () => {
const handleApplyFilters = useCallback(() => {
const payload = {
- buildingIds:
- selectedFilters.building.length > 0
- ? selectedFilters.building.map((item) => item[0])
- : null,
- floorIds:
- selectedFilters.floor.length > 0
- ? selectedFilters.floor.map((item) => item[0])
- : null,
- workAreaIds:
- selectedFilters.workArea.length > 0
- ? selectedFilters.workArea.map((item) => item[0])
- : null,
- workCategoryIds:
- selectedFilters.workCategory.length > 0
- ? selectedFilters.workCategory.map((item) => item[0])
- : null,
- activityIds:
- selectedFilters.activity.length > 0
- ? selectedFilters.activity.map((item) => item[0])
- : null,
- uploadedByIds:
- selectedFilters.uploadedBy.length > 0
- ? selectedFilters.uploadedBy.map((item) => item[0])
- : null,
+ buildingIds: selectedFilters.building.length ? selectedFilters.building.map((item) => item[0]) : null,
+ floorIds: selectedFilters.floor.length ? selectedFilters.floor.map((item) => item[0]) : null,
+ workAreaIds: selectedFilters.workArea.length ? selectedFilters.workArea.map((item) => item[0]) : null,
+ workCategoryIds: selectedFilters.workCategory.length ? selectedFilters.workCategory.map((item) => item[0]) : null,
+ activityIds: selectedFilters.activity.length ? selectedFilters.activity.map((item) => item[0]) : null,
+ uploadedByIds: selectedFilters.uploadedBy.length ? selectedFilters.uploadedBy.map((item) => item[0]) : null,
startDate: selectedFilters.startDate || null,
endDate: selectedFilters.endDate || null,
};
@@ -303,30 +241,23 @@ const ImageGallery = () => {
const areFiltersChanged = Object.keys(payload).some(key => {
const oldVal = appliedFilters[key];
const newVal = payload[key];
-
if (Array.isArray(oldVal) && Array.isArray(newVal)) {
if (oldVal.length !== newVal.length) return true;
const oldSet = new Set(oldVal);
const newSet = new Set(newVal);
- if (oldSet.size !== newSet.size) return true;
for (const item of newSet) {
if (!oldSet.has(item)) return true;
}
return false;
}
- if ((oldVal === null && newVal === "") || (oldVal === "" && newVal === null)) {
- return false;
- }
+ if ((oldVal === null && newVal === "") || (oldVal === "" && newVal === null)) return false;
return oldVal !== newVal;
});
if (areFiltersChanged) {
setAppliedFilters(payload);
- setImages([]);
- setPageNumber(1);
- setHasMore(true);
+ resetGallery();
}
- // Removed setIsFilterPanelOpen(false); to keep the drawer open
}, [selectedFilters, appliedFilters]);
const handleClearAllFilters = useCallback(() => {
@@ -353,9 +284,7 @@ const ImageGallery = () => {
endDate: null,
};
setAppliedFilters(initialStateApplied);
- setImages([]);
- setPageNumber(1);
- setHasMore(true);
+ resetGallery();
}, []);
const scrollLeft = useCallback((key) => {
@@ -382,22 +311,18 @@ const ImageGallery = () => {
Clear
)}
- {type !== "dateRange" &&
- selectedFilters[type] &&
- selectedFilters[type].length > 0 && (
- {
- e.stopPropagation();
- setSelectedFilters((prev) => ({ ...prev, [type]: [] }));
- }}
- >
- Clear
-
- )}
-
- {collapsedFilters[type] ? '+' : '-'}
-
+ {type !== "dateRange" && selectedFilters[type]?.length > 0 && (
+ {
+ e.stopPropagation();
+ setSelectedFilters((prev) => ({ ...prev, [type]: [] }));
+ }}
+ >
+ Clear
+
+ )}
+ {collapsedFilters[type] ? '+' : '-'}
{!collapsedFilters[type] && (
@@ -406,31 +331,22 @@ const ImageGallery = () => {
) : (
- items.map((item) => {
- const itemId = item[0];
- const itemName = item[1];
- const isChecked = selectedFilters[type].some(
- (selectedItem) => selectedItem[0] === itemId
- );
-
- return (
-
- toggleFilter(type, itemId, itemName)}
- />
- {itemName}
-
- );
- })
+ items.map(([itemId, itemName]) => (
+
+ item[0] === itemId)}
+ onChange={() => toggleFilter(type, itemId, itemName)}
+ />
+ {itemName}
+
+ ))
)}
)}
@@ -438,40 +354,25 @@ const ImageGallery = () => {
);
return (
-
-
+
+
setIsFilterPanelOpen(!isFilterPanelOpen)}
ref={filterButtonRef}
>
- {isFilterPanelOpen ? (
-
- ) : (
- <> >
- )}
+ {isFilterPanelOpen ? : }
{loading && pageNumber === 1 ? (
-
+
) : images.length > 0 ? (
images.map((batch) => {
const firstDoc = batch.documents[0];
- const userName = `${firstDoc?.uploadedBy?.firstName || ""} ${firstDoc?.uploadedBy?.lastName || ""
- }`.trim();
- const date = formatUTCToLocalTime(firstDoc?.uploadedAt)
-
-
-
+ const userName = `${firstDoc?.uploadedBy?.firstName || ""} ${firstDoc?.uploadedBy?.lastName || ""}`.trim();
+ const date = formatUTCToLocalTime(firstDoc?.uploadedAt);
const showScrollButtons = batch.documents.length > SCROLL_THRESHOLD;
return (
@@ -479,29 +380,15 @@ const ImageGallery = () => {
-
+
-
- {userName}
-
-
- {date}
-
+ {userName}
+ {date}
-
-
- {batch.buildingName} > {batch.floorName} >{" "}
- {batch.workAreaName || "Unknown"} >{" "}
- {batch.activityName}
-
+
{batch.buildingName} > {batch.floorName} > {batch.workAreaName || "Unknown"} > {batch.activityName}
{batch.workCategoryName && (
@@ -511,107 +398,53 @@ const ImageGallery = () => {
)}
-
- {showScrollButtons && (
-
scrollLeft(batch.batchId)}
- >
- ‹
-
- )}
-
(imageGroupRefs.current[batch.batchId] = el)}
- >
+ {showScrollButtons &&
scrollLeft(batch.batchId)}>‹ }
+
(imageGroupRefs.current[batch.batchId] = el)}>
{batch.documents.map((doc, idx) => {
const hoverDate = moment(doc.uploadedAt).format("DD-MM-YYYY");
const hoverTime = moment(doc.uploadedAt).format("hh:mm A");
return (
-
- openModal(
)
- }
- onMouseEnter={() => setHoveredImage(doc)}
- onMouseLeave={() => setHoveredImage(null)}
- >
+
openModal(
)} onMouseEnter={() => setHoveredImage(doc)} onMouseLeave={() => setHoveredImage(null)}>
{hoveredImage === doc && (
-
- Date: {hoverDate}
-
-
- Time: {hoverTime}
-
-
- Activity: {batch.activityName}
-
+
Date: {hoverDate}
+
Time: {hoverTime}
+
Activity: {batch.activityName}
)}
);
})}
- {showScrollButtons && (
-
scrollRight(batch.batchId)}
- >
- ›
-
- )}
+ {showScrollButtons &&
scrollRight(batch.batchId)}>› }
);
})
) : (
- !loading &&
- No images match the selected filters.
-
+ !loading &&
No images match the selected filters.
)}
-
{loadingMore && hasMore &&
}
- {!hasMore && !loading && images.length > 0 && (
-
You've reached the end of the images.
- )}
+ {!hasMore && !loading && images.length > 0 &&
You've reached the end of the images.
}
-
+
-
- Filters
-
- setIsFilterPanelOpen(false)}
- aria-label="Close"
- >
+ Filters
+ setIsFilterPanelOpen(false)} aria-label="Close" />
-
- Clear All
-
-
- Apply Filters
-
-
+
Clear All
+
Apply Filters
+
{renderFilterCategory("Date Range", [], "dateRange")}
{renderFilterCategory("Building", buildings, "building")}
@@ -620,12 +453,10 @@ const ImageGallery = () => {
{renderFilterCategory("Activity", activities, "activity")}
{renderFilterCategory("Uploaded By (User)", uploadedByUsers, "uploadedBy")}
{renderFilterCategory("Work Category", workCategories, "workCategory")}
-
-
);
};
-export default ImageGallery;
\ No newline at end of file
+export default ImageGallery;
diff --git a/src/pages/Gallary/ImagePop.css b/src/pages/Gallary/ImagePop.css
index dfdb549c..367758f6 100644
--- a/src/pages/Gallary/ImagePop.css
+++ b/src/pages/Gallary/ImagePop.css
@@ -1,103 +1,149 @@
+/* Image Modal Overlay */
.image-modal-overlay {
position: fixed;
top: 0;
left: 0;
- z-index: 9999; /* High z-index to ensure it's on top */
+ z-index: 9999;
width: 100%;
height: 100%;
- background-color: rgba(0, 0, 0, 0.85); /* Dark semi-transparent background */
+ background-color: rgba(0, 0, 0, 0.85);
display: flex;
justify-content: center;
align-items: center;
}
+/* Main Modal Content Box */
.image-modal-content {
background: #fff;
padding: 24px;
- max-width: 90%; /* Responsive max-width */
- max-height: 100%; /* Responsive max-height */
+ max-width: 50%;
+ max-height: 95vh; /* Limits the modal's height to 95% of viewport height */
border-radius: 12px;
position: relative;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
text-align: center;
- display: flex; /* Use flexbox for internal layout */
+ display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
+ overflow-y: auto; /* Enables vertical scrolling */
+
+ /* --- HIDE SCROLLBAR FOR MAIN MODAL CONTENT --- */
+ /* For Webkit browsers (Chrome, Safari, Edge) */
+ &::-webkit-scrollbar {
+ width: 0px; /* Hide vertical scrollbar */
+ height: 0px; /* Hide horizontal scrollbar, though unlikely needed here */
+ }
+ /* For Firefox */
+ scrollbar-width: none; /* Hide scrollbar in Firefox */
+ /* For Internet Explorer and Edge (legacy) */
+ -ms-overflow-style: none;
+ /* --- END HIDE SCROLLBAR --- */
}
+/* Image Styles */
.modal-image {
max-width: 100%;
- max-height: 70vh; /* Limits image height to 70% of viewport height */
+ max-height: 70vh;
+ width: auto;
border-radius: 10px;
- object-fit: contain; /* Ensures the whole image is visible without cropping */
+ object-fit: contain;
margin-bottom: 20px;
- flex-shrink: 0; /* Prevent image from shrinking if content is too large */
+ flex-shrink: 0;
}
-.image-details {
+/* Scrollable Container for Text Details */
+.image-details-scroll-container {
+ width: 100%;
+ flex-grow: 1;
+ max-height: calc(95vh - 70vh - (24px * 2) - 20px); /* Approximate calculation for text area height */
+ overflow-y: auto; /* Enables vertical scrolling for details */
text-align: left;
+ padding-right: 5px; /* Add some padding so text doesn't touch the hidden scrollbar area */
+
+ /* --- HIDE SCROLLBAR FOR TEXT DETAILS SECTION --- */
+ /* For Webkit browsers (Chrome, Safari, Edge) */
+ &::-webkit-scrollbar {
+ width: 0px; /* Hide vertical scrollbar */
+ height: 0px; /* Hide horizontal scrollbar */
+ }
+ /* For Firefox */
+ scrollbar-width: none; /* Hide scrollbar in Firefox */
+ /* For Internet Explorer and Edge (legacy) */
+ -ms-overflow-style: none;
+ /* --- END HIDE SCROLLBAR --- */
+}
+
+/* Image Details Section (inside the scroll container) */
+.image-details {
color: #444;
font-size: 14px;
line-height: 1.4;
margin: 0;
padding: 0;
- width: 100%; /* Ensure details section takes full width */
+ width: 100%;
+ text-align: left;
}
.image-details p {
- margin: 4px 0; /* Reduce vertical space between lines in details */
+ margin: 4px 0;
+ white-space: normal;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ text-overflow: initial;
+ text-align: left;
}
+/* Close Button */
.close-button {
position: absolute;
- top: 1px; /* Position relative to the modal content */
+ top: 1px;
right: 8px;
font-size: 30px;
background: none;
border: none;
- color: black; /* White color for visibility on dark overlay */
+ color: black;
cursor: pointer;
padding: 0;
line-height: 1;
- z-index: 10000; /* Ensure it's above everything else */
+ z-index: 10000;
}
-/* Styles for the navigation buttons */
+/* Navigation Buttons */
.nav-button {
position: absolute;
- top: 50%; /* Vertically center them */
- transform: translateY(-50%); /* Adjust for perfect vertical centering */
- background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background */
+ top: 50%;
+ transform: translateY(-50%);
+ background-color: rgba(0, 0, 0, 0.5);
color: white;
border: none;
padding: 10px 15px;
font-size: 30px;
cursor: pointer;
- z-index: 1000; /* Ensure buttons are above the image */
- border-radius: 50%; /* Make them circular */
+ z-index: 1000;
+ border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
- transition: background-color 0.3s ease; /* Smooth hover effect */
+ transition: background-color 0.3s ease;
}
.nav-button:hover {
- background-color: rgba(0, 0, 0, 0.8); /* Darker on hover */
+ background-color: rgba(0, 0, 0, 0.8);
}
.nav-button.prev-button {
- left: 0px; /* Position left arrow */
+ left: 0px;
}
.nav-button.next-button {
- right: 0px; /* Position right arrow */
+ right: 0px;
}
-/* Style for disabled buttons (optional) */
+/* Disabled Button Style */
.nav-button:disabled {
opacity: 0.5;
cursor: not-allowed;
-}
+}
\ No newline at end of file
diff --git a/src/pages/authentication/ChangePassword.jsx b/src/pages/authentication/ChangePassword.jsx
index 5191d6c3..a501c5eb 100644
--- a/src/pages/authentication/ChangePassword.jsx
+++ b/src/pages/authentication/ChangePassword.jsx
@@ -69,22 +69,17 @@ const ChangePasswordPage = ({ onClose }) => {
className="modal d-flex align-items-center justify-content-center show"
tabIndex="-1"
role="dialog"
- style={{ display: "flex", backgroundColor: "rgba(0,0,0,0.5)" }}
+ style={{ backgroundColor: "rgba(0,0,0,0.5)" }}
>
-
- {" "}
+
-
-
-
- {/* Close Button */}
Change Password
@@ -119,7 +114,6 @@ const ChangePasswordPage = ({ onClose }) => {
)}
- {/*
*/}
New Password
@@ -180,14 +174,14 @@ const ChangePasswordPage = ({ onClose }) => {
)}
- {/*
*/}
-
+
+
Your password must have at least 8 characters and include a lower
- case latter, an uppercase letter, a number, and a special
+ case letter, an uppercase letter, a number, and a special
character.
{/* Action Buttons */}
-
+
{
);
};
-export default ChangePasswordPage;
+export default ChangePasswordPage;
\ No newline at end of file
diff --git a/src/pages/authentication/LoginWithOtp.jsx b/src/pages/authentication/LoginWithOtp.jsx
index e0328b1b..2157b4e1 100644
--- a/src/pages/authentication/LoginWithOtp.jsx
+++ b/src/pages/authentication/LoginWithOtp.jsx
@@ -18,9 +18,9 @@ const otpSchema = z.object({
const LoginWithOtp = () => {
const navigate = useNavigate();
- const [ loading, setLoading ] = useState( false );
- const [ timeLeft, setTimeLeft ] = useState( 0 );
-
+ const [loading, setLoading] = useState(false);
+ const [timeLeft, setTimeLeft] = useState(0);
+
const inputRefs = useRef([]);
@@ -29,68 +29,97 @@ const LoginWithOtp = () => {
handleSubmit,
formState: { errors, isSubmitted },
getValues,
+ setValue,
+ trigger,
} = useForm({
resolver: zodResolver(otpSchema),
});
const onSubmit = async (data) => {
const finalOtp = data.otp1 + data.otp2 + data.otp3 + data.otp4;
- const username = localStorage.getItem( "otpUsername" );
+ const username = localStorage.getItem("otpUsername");
setLoading(true);
try {
- let requestedData = {
- email: username,
- otp:finalOtp
- }
- const response = await AuthRepository.verifyOTP( requestedData )
-
- localStorage.setItem("jwtToken", response.data.token);
- localStorage.setItem("refreshToken", response.data.refreshToken);
- setLoading( false );
- localStorage.removeItem( "otpUsername" );
- localStorage.removeItem( "otpSentTime" );
- navigate( "/dashboard" );
-
+ let requestedData = {
+ email: username,
+ otp: finalOtp
+ }
+ const response = await AuthRepository.verifyOTP(requestedData)
+
+ localStorage.setItem("jwtToken", response.data.token);
+ localStorage.setItem("refreshToken", response.data.refreshToken);
+ setLoading(false);
+ localStorage.removeItem("otpUsername");
+ localStorage.removeItem("otpSentTime");
+ navigate("/dashboard");
+
} catch (err) {
- showToast( "Invalid or expired OTP.", "error" );
-
- setLoading(false);
+ showToast("Invalid or expired OTP.", "error");
+
+ setLoading(false);
}
};
-const formatTime = (seconds) => {
- const min = Math.floor(seconds / 60).toString().padStart(2, "0");
- const sec = (seconds % 60).toString().padStart(2, "0");
- return `${min}:${sec}`;
-};
+ const formatTime = (seconds) => {
+ const min = Math.floor(seconds / 60).toString().padStart(2, "0");
+ const sec = (seconds % 60).toString().padStart(2, "0");
+ return `${min}:${sec}`;
+ };
-useEffect(() => {
- const otpSentTime = localStorage.getItem("otpSentTime");
- const now = Date.now();
+ // Time Logic for OTP expiry
+ useEffect(() => {
+ const otpSentTime = localStorage.getItem("otpSentTime");
+ const now = Date.now();
- if (otpSentTime) {
- const elapsed = Math.floor((now - Number(otpSentTime)) / 1000); // in seconds
- const remaining = Math.max(OTP_EXPIRY_SECONDS - elapsed, 0); // prevent negatives
- setTimeLeft(remaining);
- }
-}, []);
-useEffect(() => {
- if (timeLeft <= 0) return;
+ if (otpSentTime) {
+ const elapsed = Math.floor((now - Number(otpSentTime)) / 1000); //in seconds
+ const remaining = Math.max(OTP_EXPIRY_SECONDS - elapsed, 0); //prevent negatives
+ setTimeLeft(remaining);
+ }
+ }, []);
- const timer = setInterval(() => {
- setTimeLeft((prev) => {
- if (prev <= 1) {
- clearInterval(timer);
- localStorage.removeItem( "otpSentTime" );
- localStorage.removeItem("otpUsername");
- return 0;
+
+ useEffect(() => {
+ if (timeLeft <= 0) return;
+
+ const timer = setInterval(() => {
+ setTimeLeft((prev) => {
+ if (prev <= 1) {
+ clearInterval(timer);
+ localStorage.removeItem("otpSentTime");
+ localStorage.removeItem("otpUsername");
+ return 0;
+ }
+ return prev - 1;
+ });
+ }, 1000);
+
+ return () => clearInterval(timer);
+ }, [timeLeft]);
+
+ // Handle Paste Event
+ const handlePaste = (e) => {
+ e.preventDefault();
+
+ const pastedData = e.clipboardData.getData("text/plain").trim();
+ if (pastedData.match(/^\d{4}$/)) {
+ for (let i = 0; i < pastedData.length; i++) {
+ setValue(`otp${i + 1}`, pastedData[i], { shouldValidate: true });
+
+ if (inputRefs.current[i + 1]) {
+ inputRefs.current[i + 1].focus();
+ }
}
- return prev - 1;
- });
- }, 1000);
+ trigger(["otp1", "otp2", "otp3", "otp4"]);
+ } else {
+ showToast("Invalid OTP format pasted. Please enter 4 digits")
- return () => clearInterval(timer);
-}, [timeLeft]);
+ for (let i = 0; i < 4; i++) {
+ setValue(`otp${i + 1}`, "")
+
+ }
+ }
+ }
return (
@@ -109,9 +138,8 @@ useEffect(() => {
key={num}
type="text"
maxLength={1}
- className={`form-control text-center ${
- errors[`otp${num}`] ? "is-invalid" : ""
- }`}
+ className={`form-control text-center ${errors[`otp${num}`] ? "is-invalid" : ""
+ }`}
ref={(el) => {
inputRefs.current[idx] = el;
ref(el);
@@ -121,6 +149,9 @@ useEffect(() => {
onChange(e);
if (/^\d$/.test(val) && idx < 3) {
inputRefs.current[idx + 1]?.focus();
+
+ } else if (val === "" && idx > 0) {
+ inputRefs.current[idx - 1]?.focus();
}
}}
onKeyDown={(e) => {
@@ -132,6 +163,8 @@ useEffect(() => {
inputRefs.current[idx - 1]?.focus();
}
}}
+
+ onPaste={idx === 0 ? handlePaste : undefined}
style={{ width: "40px", height: "40px", fontSize: "15px" }}
{...rest}
/>
@@ -163,17 +196,17 @@ useEffect(() => {
>
This OTP will expire in {formatTime(timeLeft)}
- ) : (
-
-
+ ) : (
+
+
)}
diff --git a/src/pages/employee/AttendancesEmployeeRecords.jsx b/src/pages/employee/AttendancesEmployeeRecords.jsx
index 1908ef21..bac57a09 100644
--- a/src/pages/employee/AttendancesEmployeeRecords.jsx
+++ b/src/pages/employee/AttendancesEmployeeRecords.jsx
@@ -85,7 +85,7 @@ const AttendancesEmployeeRecords = ({ employee }) => {
const currentDate = new Date().toLocaleDateString("en-CA");
const { currentPage, totalPages, currentItems, paginate } = usePagination(
sortedFinalList,
- 10
+ 20
);
useEffect(() => {
@@ -141,13 +141,12 @@ const AttendancesEmployeeRecords = ({ employee }) => {
id="DataTables_Table_0_length"
>
-
+
setIsRefreshing(!isRefreshing)}
@@ -224,7 +223,7 @@ const AttendancesEmployeeRecords = ({ employee }) => {
)}
- {!loading && data.length > 5 && (
+ {!loading && sortedFinalList.length > 20 && (
{
{[...Array(totalPages)].map((_, index) => (
{
))}
{
const selectedProjectId = useSelector(
(store) => store.localVariables.projectId
);
+ const { projectNames, loading: projectLoading, fetchData } = useProjectName();
+
+ const dispatch = useDispatch();
const [showInactive, setShowInactive] = useState(false);
const [showAllEmployees, setShowAllEmployees] = useState(false);
@@ -44,8 +56,8 @@ const EmployeeList = () => {
);
const [employeeList, setEmployeeList] = useState([]);
- const [ modelConfig, setModelConfig ] = useState();
- const [EmpForManageRole,setEmpForManageRole] = useState(null)
+ const [modelConfig, setModelConfig] = useState();
+ const [EmpForManageRole, setEmpForManageRole] = useState(null);
// const [currentPage, setCurrentPage] = useState(1);
// const [itemsPerPage] = useState(ITEMS_PER_PAGE);
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
@@ -55,132 +67,70 @@ const EmployeeList = () => {
const [selectedEmployeeId, setSelecedEmployeeId] = useState(null);
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [selectedEmpFordelete, setSelectedEmpFordelete] = useState(null);
- const [ employeeLodaing, setemployeeLodaing ] = useState( false );
- const {
- mutate: suspendEmployee,
- isPending: empLodaing
-} = useSuspendEmployee({
- setIsDeleteModalOpen,
- setemployeeLodaing
-} );
-
-
+ const [employeeLodaing, setemployeeLodaing] = useState(false);
+ const ViewTeamMember = useHasUserPermission(VIEW_TEAM_MEMBERS);
+ const ViewAllEmployee = useHasUserPermission(VIEW_ALL_EMPLOYEES);
+ const { mutate: suspendEmployee, isPending: empLodaing } = useSuspendEmployee(
+ {
+ setIsDeleteModalOpen,
+ setemployeeLodaing,
+ }
+ );
+ useEffect(() => {
+ if (selectedProjectId === null) {
+ dispatch(setProjectId(projectNames[0]?.id));
+ }
+ }, [selectedProjectId]);
const navigate = useNavigate();
-
const applySearchFilter = (data, text) => {
- if (!text) {
- return data;
- }
+ if (!text) {
+ return data;
+ }
- const lowercasedText = text.toLowerCase().trim();
+ const lowercasedText = text.toLowerCase().trim();
- return data.filter((item) => {
- const firstName = item.firstName || "";
- const middleName = item.middleName || "";
- const lastName = item.lastName || "";
+ return data.filter((item) => {
+ const firstName = item.firstName || "";
+ const middleName = item.middleName || "";
+ const lastName = item.lastName || "";
- const fullName = `${firstName} ${middleName} ${lastName}`
- .toLowerCase()
- .trim()
- .replace(/\s+/g, " ");
+ const fullName = `${firstName} ${middleName} ${lastName}`
+ .toLowerCase()
+ .trim()
+ .replace(/\s+/g, " ");
- const email = item.email?.toLowerCase() || "";
- const phoneNumber = item.phoneNumber?.toLowerCase() || "";
- const jobRole = item.jobRole?.toLowerCase() || "";
-
- return (
- fullName.includes(lowercasedText) ||
- email.includes(lowercasedText) ||
- phoneNumber.includes(lowercasedText) ||
- jobRole.includes(lowercasedText)
- );
- });
-};
+ const email = item.email?.toLowerCase() || "";
+ const phoneNumber = item.phoneNumber?.toLowerCase() || "";
+ const jobRole = item.jobRole?.toLowerCase() || "";
+ return (
+ fullName.includes(lowercasedText) ||
+ email.includes(lowercasedText) ||
+ phoneNumber.includes(lowercasedText) ||
+ jobRole.includes(lowercasedText)
+ );
+ });
+ };
const handleSearch = (e) => {
const value = e.target.value;
setSearchText(value);
setCurrentPage(1);
};
-useEffect(() => {
- const filtered = applySearchFilter(employeeList, searchText);
- setFilteredData(filtered);
-}, [searchText, employeeList]);
-
- const displayData = searchText ? filteredData : employeeList;
- const { currentPage, totalPages, currentItems, paginate,setCurrentPage } = usePagination(
- displayData,
- ITEMS_PER_PAGE
- );
- const openModal = () => {
+ const displayData = searchText ? filteredData : employeeList;
+ const { currentPage, totalPages, currentItems, paginate, setCurrentPage } =
+ usePagination(displayData, ITEMS_PER_PAGE);
+ const openModal = () => {
setIsCreateModalOpen(true);
- };
-
- // const closeModal = () => {
- // setIsCreateModalOpen(false);
-
- // const modalElement = document.getElementById("managerole-modal");
- // if (modalElement && !showModal) {
- // modalElement.classList.remove("show");
- // modalElement.style.display = "none";
- // document.body.classList.remove("modal-open");
- // document.querySelector(".modal-backdrop")?.remove();
- // }
- // setShowModal(false);
- // clearCacheKey("employeeProfile");
- // recallEmployeeData(showInactive, showAllEmployees ? null : selectedProjectId); // Use selectedProjectId here
- // };
- // const handleShow = () => setShowModal(true);
- // const handleClose = () => setShowModal( false );
-
-useEffect(() => {
- if (!loading && Array.isArray(employees)) {
- const sorted = [...employees].sort((a, b) => {
- const nameA = `${a.firstName || ""}${a.middleName || ""}${a.lastName || ""}`.toLowerCase();
- const nameB = `${b.firstName || ""}${b.middleName || ""}${b.lastName || ""}`.toLowerCase();
- return nameA?.localeCompare(nameB);
- });
-
- setEmployeeList((prevList) => {
- const prevJSON = JSON.stringify(prevList);
- const nextJSON = JSON.stringify(sorted);
- if (prevJSON !== nextJSON) {
- return sorted;
- }
- return prevList;
- });
-
- setFilteredData((prev) => {
- const prevJSON = JSON.stringify(prev);
- const nextJSON = JSON.stringify(sorted);
- if (prevJSON !== nextJSON) {
- return sorted;
- }
- return prev;
- });
-
- // set currentPage to 1 only if needed
- setCurrentPage((prevPage) => (prevPage !== 1 ? 1 : prevPage));
- }
-}, [loading, employees, selectedProjectId, showAllEmployees]);
-
-
-
+ };
const handleConfigData = (config) => {
setModelConfig(config);
};
- // useEffect(() => {
- // if (modelConfig !== null) {
- // openModal();
- // }
- // }, [modelConfig, isCreateModalOpen]);
-
const tableRef = useRef(null);
const handleExport = (type) => {
if (!currentItems || currentItems.length === 0) return;
@@ -205,15 +155,17 @@ useEffect(() => {
const handleToggle = (e) => {
setShowInactive(e.target.checked);
- recallEmployeeData(e.target.checked, showAllEmployees ? null : selectedProjectId); // Use selectedProjectId here
+ recallEmployeeData(
+ e.target.checked,
+ showAllEmployees ? null : selectedProjectId
+ ); // Use selectedProjectId here
};
-const handleAllEmployeesToggle = (e) => {
- const isChecked = e.target.checked;
- setShowInactive(false);
- setShowAllEmployees(isChecked);
-
-};
+ const handleAllEmployeesToggle = (e) => {
+ const isChecked = e.target.checked;
+ setShowInactive(false);
+ setShowAllEmployees(isChecked);
+ };
const handleEmployeeModel = (id) => {
setSelecedEmployeeId(id);
@@ -224,6 +176,43 @@ const handleAllEmployeesToggle = (e) => {
setSelectedEmpFordelete(employee);
setIsDeleteModalOpen(true);
};
+ useEffect(() => {
+ const filtered = applySearchFilter(employeeList, searchText);
+ setFilteredData(filtered);
+ }, [searchText, employeeList]);
+ useEffect(() => {
+ if (!loading && Array.isArray(employees)) {
+ const sorted = [...employees].sort((a, b) => {
+ const nameA = `${a.firstName || ""}${a.middleName || ""}${
+ a.lastName || ""
+ }`.toLowerCase();
+ const nameB = `${b.firstName || ""}${b.middleName || ""}${
+ b.lastName || ""
+ }`.toLowerCase();
+ return nameA?.localeCompare(nameB);
+ });
+
+ setEmployeeList((prevList) => {
+ const prevJSON = JSON.stringify(prevList);
+ const nextJSON = JSON.stringify(sorted);
+ if (prevJSON !== nextJSON) {
+ return sorted;
+ }
+ return prevList;
+ });
+
+ setFilteredData((prev) => {
+ const prevJSON = JSON.stringify(prev);
+ const nextJSON = JSON.stringify(sorted);
+ if (prevJSON !== nextJSON) {
+ return sorted;
+ }
+ return prev;
+ });
+
+ setCurrentPage((prevPage) => (prevPage !== 1 ? 1 : prevPage));
+ }
+ }, [loading, employees, selectedProjectId, showAllEmployees]);
useEffect(() => {
if (!showAllEmployees) {
@@ -233,36 +222,49 @@ const handleAllEmployeesToggle = (e) => {
const handler = useCallback(
(msg) => {
- if(employees.some((item) => item.id == msg.employeeId)){
+ if (employees.some((item) => item.id == msg.employeeId)) {
setEmployeeList([]);
- recallEmployeeData(showInactive, showAllEmployees ? null : selectedProjectId); // Use selectedProjectId here
+ recallEmployeeData(
+ showInactive,
+ showAllEmployees ? null : selectedProjectId
+ ); // Use selectedProjectId here
}
- },[employees, showInactive, showAllEmployees, selectedProjectId] // Add all relevant dependencies
+ },
+ [employees, showInactive, showAllEmployees, selectedProjectId] // Add all relevant dependencies
);
useEffect(() => {
- eventBus.on("employee",handler);
- return () => eventBus.off("employee",handler)
- },[handler])
-
+ eventBus.on("employee", handler);
+ return () => eventBus.off("employee", handler);
+ }, [handler]);
return (
<>
{EmpForManageRole && (
- setEmpForManageRole( null )}>
- setEmpForManageRole(null)} />
-
- )}
-
- {showModal && (
- setShowModal(false)}>
- setShowModal( false )}
- IsAllEmployee={showAllEmployees}
- />
+ setEmpForManageRole(null)}
+ >
+ setEmpForManageRole(null)}
+ />
- )}
+ )}
+
+ {showModal && (
+ setShowModal(false)}
+ >
+ setShowModal(false)}
+ IsAllEmployee={showAllEmployees}
+ />
+
+ )}
{IsDeleteModalOpen && (
{
{ label: "Employees", link: null },
]}
>
-
-
-
-
-
- {/* Switches: All Employees + Inactive */}
-
- {/* All Employees Switch */}
-
-
-
- All Employees
-
+ {ViewTeamMember ? (
+
+
+
+
+
+ {/* Switches: All Employees + Inactive */}
+
- {/* Show Inactive Employees Switch */}
- {showAllEmployees && (
-
-
-
- Show Inactive Employees
+ {/* Right side: Search + Export + Add Employee */}
+
+ {/* Search Input - ALWAYS ENABLED */}
+
+
+
- )}
-
-
- {/* Right side: Search + Export + Add Employee */}
-
- {/* Search Input - ALWAYS ENABLED */}
-
-
-
-
-
-
- {/* Export Dropdown */}
-
-
- {/* Add Employee Button */}
- {Manage_Employee && (
-
handleEmployeeModel(null)}
- >
-
- Add New Employee
-
- )}
-
-
-
-
-
-
-
- Name
-
-
- Email
-
-
- Contact
-
-
- Role
-
-
-
- Joining Date
-
-
- Status
-
-
- Actions
-
-
-
-
- {loading && (
-
-
- Loading...
-
-
- )}
- {/* Conditional messages for no data or no search results */}
- {!loading && displayData?.length === 0 && searchText && !showAllEmployees ? (
-
-
-
- '{searchText}' employee not found
- {" "}
-
-
- ) : null}
- {!loading && displayData?.length === 0 && (!searchText || showAllEmployees) ? (
-
-
+
- No Data Found
-
+ Export
+
+
+
+
+ {/* Add Employee Button */}
+ {Manage_Employee && (
+ handleEmployeeModel(null)}
+ >
+
+
+ Add New Employee
+
+
+ )}
+
+
+
+
+
+
+
+ Name
+
+
+ Email
+
+
+ Contact
+
+
+ Role
+
+
+
+ Joining Date
+
+
+ Status
+
+
+ Actions
+
- ) : null}
-
- {/* Render current items */}
- {currentItems && !loading && currentItems.map((item) => (
-
-
-
-
-
- {item.email ? (
-
-
- {item.email}
-
- ) : (
-
- NA
-
- )}
-
-
-
-
- {item.phoneNumber}
-
-
-
-
-
- {item.jobRole || "Not Assign Yet"}
-
-
-
-
- {moment(item.joiningDate)?.format("DD-MMM-YYYY")}
+
+
+ {loading && (
+
+
+ Loading...
-
- {showInactive ? (
-
- Inactive
-
- ) : (
-
- Active
-
- )}
+
+ )}
+ {/* Conditional messages for no data or no search results */}
+ {!loading &&
+ displayData?.length === 0 &&
+ searchText &&
+ !showAllEmployees ? (
+
+
+
+ '{searchText}' employee not found
+ {" "}
- {Manage_Employee && (
-
-
-
-
-
-
-
+ ) : null}
+ {!loading &&
+ displayData?.length === 0 &&
+ (!searchText || showAllEmployees) ? (
+
+
+ No Data Found
+
+
+ ) : null}
+
+ {/* Render current items */}
+ {currentItems &&
+ !loading &&
+ currentItems.map((item) => (
+
+
+
- )}
-
- ))}
-
-
+
+ {item.email ? (
+
+
+ {item.email}
+
+ ) : (
+
+ NA
+
+ )}
+
+
+
+
+ {item.phoneNumber}
+
+
+
+
+
+ {item.jobRole || "Not Assign Yet"}
+
+
-
+
+ {moment(item.joiningDate)?.format("DD-MMM-YYYY")}
+
+
+ {showInactive ? (
+
+ Inactive
+
+ ) : (
+
+ Active
+
+ )}
+
+ {Manage_Employee && (
+
+
+
+
+
+
+
+ navigate(`/employee/${item.id}`)
+ }
+ className="dropdown-item py-1"
+ >
+ {" "}
+ View
+
+ {
+ handleEmployeeModel(item.id);
+ }}
+ >
+ Edit
+
+ {!item.isSystem && (
+ <>
+
+ handleOpenDelete(item.id)
+ }
+ >
+ {" "}
+ Suspend
+
+
+ setEmpForManageRole(item.id)
+ }
+ >
+ {" "}
+ Manage Role
+
+ >
+ )}
+
+
+
+ )}
+
+ ))}
+
+
- {/* Pagination */}
- {!loading && displayData.length > ITEMS_PER_PAGE && (
-
-
-
- paginate(currentPage - 1)}
- >
- «
-
-
+
- {[...Array(totalPages)]?.map((_, index) => (
+ {/* Pagination */}
+ {!loading && displayData.length > ITEMS_PER_PAGE && (
+
+
+ paginate(currentPage - 1)}
+ >
+ «
+
+
+
+ {[...Array(totalPages)]?.map((_, index) => (
+
+ paginate(index + 1)}
+ >
+ {index + 1}
+
+
+ ))}
+
+
paginate(index + 1)}
+ onClick={() => paginate(currentPage + 1)}
>
- {index + 1}
+ »
- ))}
-
-
- paginate(currentPage + 1)}
- >
- »
-
-
-
-
- )}
+
+
+ )}
+
-
+ ) : (
+
+
+
+
+ Access Denied: You don't have permission to perform this action.
+ !
+
+
+
+ )}
>
);
};
-export default EmployeeList;
\ No newline at end of file
+export default EmployeeList;
diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx
index f34586aa..ff1be7bb 100644
--- a/src/pages/project/ProjectDetails.jsx
+++ b/src/pages/project/ProjectDetails.jsx
@@ -1,11 +1,9 @@
-import { useParams } from "react-router-dom";
+import { useSelector } from "react-redux"; // Import useSelector
import React, { useState, useEffect, useCallback } from "react";
-import ActivityTimeline from "../../components/Project/ActivityTimeline";
import ProjectOverview from "../../components/Project/ProjectOverview";
import AboutProject from "../../components/Project/AboutProject";
import ProjectNav from "../../components/Project/ProjectNav";
-import ProjectBanner from "../../components/Project/ProjectBanner";
import Teams from "../../components/Project/Teams";
import ProjectInfra from "../../components/Project/ProjectInfra";
import Loader from "../../components/common/Loader";
@@ -16,172 +14,116 @@ import {
clearCacheKey,
getCachedData,
} from "../../slices/apiDataManager";
-import ProjectRepository from "../../repositories/ProjectRepository";
-import { ActivityeRepository } from "../../repositories/MastersRepository";
import "./ProjectDetails.css";
import {
- useEmployeesByProjectAllocated,
useProjectDetails,
} from "../../hooks/useProjects";
-import { useDispatch } from "react-redux";
-import { setProjectId } from "../../slices/localVariablesSlice";
import { ComingSoonPage } from "../Misc/ComingSoonPage";
import Directory from "../Directory/Directory";
import eventBus from "../../services/eventBus";
import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChart";
const ProjectDetails = () => {
- let { projectId } = useParams();
- const {
- projects_Details,
- loading: projectLoading,
- error: ProjectError,
- refetch
- } = useProjectDetails(projectId);
- const dispatch = useDispatch();
- const [project, setProject] = useState(null);
- // const [projectDetails, setProjectDetails] = useState(null);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState("");
- // const fetchData = async () => {
- // const project_cache = getCachedData("projectInfo");
- // if (!project_cache || project_cache?.projectId !== projectId) {
- // ProjectRepository.getProjectByprojectId(projectId)
- // .then((response) => {
- // setProjectDetails(response.data);
- // setProject(response.data);
- // cacheData("projectInfo", { projectId, data: response.data });
- // setLoading(false);
- // })
- // .catch((error) => {
- // console.error(error);
- // setError("Failed to fetch data.");
- // setLoading(false);
- // });
- // } else {
- // setProjectDetails(project_cache.data);
- // setProject(project_cache.data);
- // setLoading(false);
- // }
- // };
+
+ const projectId = useSelector((store) => store.localVariables.projectId);
+
+ const {
+ projects_Details,
+ loading: projectLoading,
+ error: projectError,
+ refetch,
+ } = useProjectDetails(projectId);
const [activePill, setActivePill] = useState("profile");
- const handlePillClick = (pillKey) => {
- setActivePill(pillKey);
- };
- const handleDataChange = (data) => {
- fetchData();
- };
-
- const renderContent = () => {
- if (projectLoading) return
;
- switch (activePill) {
- case "profile": {
- return (
-
- );
- }
- case "teams": {
- return (
-
-
- {/* Teams */}
-
- {/* Teams */}
-
-
- );
- break;
- }
- case "infra": {
- return (
-
- );
- break;
- }
- case "workplan": {
- return (
-
- );
- break;
- }
- case "directory": {
- return (
-
-
-
- );
- }
-
- default:
- return
;
- }
- };
-
- useEffect(() => {
- dispatch(setProjectId(projectId));
-
- }, [projects_Details, projectId]);
const handler = useCallback(
(msg) => {
- if (msg.keyword === "Update_Project" && projects_Details.id === msg.response.id) {
- refetch()
+ if (msg.keyword === "Update_Project" && projects_Details?.id === msg.response.id) {
+ refetch();
}
},
- [projects_Details, handleDataChange]
+ [projects_Details, refetch]
);
+
useEffect(() => {
eventBus.on("project", handler);
return () => eventBus.off("project", handler);
}, [handler]);
+ const handlePillClick = (pillKey) => {
+ setActivePill(pillKey);
+ };
+
+ const renderContent = () => {
+ if (projectLoading || !projects_Details) return
;
+
+ switch (activePill) {
+ case "profile":
+ return (
+ <>
+
+ >
+ );
+
+ case "teams":
+ return (
+
+ );
+
+ case "infra":
+ return (
+
+ );
+
+ case "workplan":
+ return (
+
+ );
+
+ case "directory":
+ return (
+
+
+
+ );
+
+ default:
+ return
;
+ }
+ };
+
return (
- <>
- {}
-
-
+
+
-
- {projectLoading &&
Loading....
}
- {/* {!projectLoading && project && (
-
- )} */}
-
-
-
-
-
-
- {renderContent()}
+
- >
+
+ {renderContent()}
+
);
};
-export default ProjectDetails;
+export default ProjectDetails;
\ No newline at end of file
diff --git a/src/pages/project/ProjectList.jsx b/src/pages/project/ProjectList.jsx
index 7733a60a..75b8f1f4 100644
--- a/src/pages/project/ProjectList.jsx
+++ b/src/pages/project/ProjectList.jsx
@@ -5,11 +5,6 @@ import Breadcrumb from "../../components/common/Breadcrumb";
import ProjectRepository from "../../repositories/ProjectRepository";
import { useProjects, useCreateProject } from "../../hooks/useProjects";
import showToast from "../../services/toastService";
-// import {
-// getCachedData,
-// cacheData,
-// clearCacheKey,
-// } from "../../slices/apiDataManager";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { useProfile } from "../../hooks/useProfile";
import { ITEMS_PER_PAGE, MANAGE_PROJECT } from "../../utils/constants";
@@ -20,11 +15,15 @@ import { defaultCheckBoxAppearanceProvider } from "pdf-lib";
import { useMutation } from "@tanstack/react-query";
import usePagination from "../../hooks/usePagination";
import GlobalModel from "../../components/common/GlobalModel";
+import { useDispatch, useSelector } from "react-redux";
+import { setProjectId } from "../../slices/localVariablesSlice";
const ProjectList = () => {
const { profile: loginUser } = useProfile();
const [listView, setListView] = useState(false);
const [showModal, setShowModal] = useState(false);
+ const selectedProject = useSelector((store)=>store.localVariables.projectId)
+ const dispatch = useDispatch()
const { projects, loading, error, refetch } = useProjects();
const [projectList, setProjectList] = useState([]);
@@ -75,6 +74,10 @@ const ProjectList = () => {
};
useEffect(() => {
+ if(selectedProject == null){
+ dispatch(setProjectId(projects[0]?.id));
+ }
+
if (!loading && projects) {
sortingProject(projects);
}
diff --git a/src/pages/project/ProjectListView.jsx b/src/pages/project/ProjectListView.jsx
index 9deead71..a9ffc60d 100644
--- a/src/pages/project/ProjectListView.jsx
+++ b/src/pages/project/ProjectListView.jsx
@@ -62,7 +62,7 @@ const ProjectListView = ({ projectData, recall }) => {
const handleClose = () => setShowModal(false);
const handleViewProject = () => {
- navigate(`/projects/${projectData.id}`);
+ navigate(`/projects/details`);
};
const handleFormSubmit = (updatedProject) => {
@@ -89,7 +89,7 @@ const ProjectListView = ({ projectData, recall }) => {
navigate(`/projects/${projectInfo.id}`)}
+ onClick={() => navigate(`/projects/details`)}
>
{projectInfo.shortName
? `${projectInfo.name} (${projectInfo.shortName})`
@@ -162,7 +162,7 @@ const ProjectListView = ({ projectData, recall }) => {
navigate(`/projects/${projectInfo.id}`)}
+ onClick={() => navigate(`/projects/details`)}
>
View details
@@ -193,4 +193,4 @@ const ProjectListView = ({ projectData, recall }) => {
);
};
-export default ProjectListView;
+export default ProjectListView;
\ No newline at end of file
diff --git a/src/pages/Gallary/ImageGalleryAPI.jsx b/src/repositories/ImageGalleryAPI.jsx
similarity index 72%
rename from src/pages/Gallary/ImageGalleryAPI.jsx
rename to src/repositories/ImageGalleryAPI.jsx
index 9ca09bfe..5f75dbed 100644
--- a/src/pages/Gallary/ImageGalleryAPI.jsx
+++ b/src/repositories/ImageGalleryAPI.jsx
@@ -1,9 +1,8 @@
-import { api } from "../../utils/axiosClient";
+import { api } from "../utils/axiosClient";
export const ImageGalleryAPI = {
ImagesGet: (projectId, filter, pageNumber, pageSize) => {
const payloadJsonString = JSON.stringify(filter);
- // Corrected API endpoint with pagination parameters
return api.get(`/api/image/images/${projectId}?filter=${payloadJsonString}&pageNumber=${pageNumber}&pageSize=${pageSize}`);
},
-};
\ No newline at end of file
+};
diff --git a/src/router/AppRoutes.jsx b/src/router/AppRoutes.jsx
index 7c364ca9..5ce5b6d4 100644
--- a/src/router/AppRoutes.jsx
+++ b/src/router/AppRoutes.jsx
@@ -63,7 +63,7 @@ const router = createBrowserRouter(
{ path: "/", element: },
{ path: "/dashboard", element: },
{ path: "/projects", element: },
- { path: "/projects/:projectId", element: },
+ { path: "/projects/details", element: },
{ path: "/project/manage/:projectId", element: },
{ path: "/employees", element: },
{ path: "/employee/:employeeId", element: },
diff --git a/src/slices/localVariablesSlice.jsx b/src/slices/localVariablesSlice.jsx
index ae0f104c..9bd787c6 100644
--- a/src/slices/localVariablesSlice.jsx
+++ b/src/slices/localVariablesSlice.jsx
@@ -5,7 +5,7 @@ const localVariablesSlice = createSlice({
initialState: {
selectedMaster:"Application Role",
regularizationCount:0,
- projectId: "",
+ projectId: null,
reload:false
},
diff --git a/src/utils/constants.jsx b/src/utils/constants.jsx
index fca560af..a8dc1c2d 100644
--- a/src/utils/constants.jsx
+++ b/src/utils/constants.jsx
@@ -11,6 +11,11 @@ export const VIEW_PROJECTS = "6ea44136-987e-44ba-9e5d-1cf8f5837ebc"
export const MANAGE_EMPLOYEES = "a97d366a-c2bb-448d-be93-402bd2324566"
+export const VIEW_ALL_EMPLOYEES = "60611762-7f8a-4fb5-b53f-b1139918796b"
+
+export const VIEW_TEAM_MEMBERS = "b82d2b7e-0d52-45f3-997b-c008ea460e7f"
+
+
export const MANAGE_PROJECT_INFRA = "cf2825ad-453b-46aa-91d9-27c124d63373"
export const VIEW_PROJECT_INFRA = "8d7cc6e3-9147-41f7-aaa7-fa507e450bd4"
@@ -24,6 +29,8 @@ export const INFRASTRUCTURE = "9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c";
export const MANAGE_TASK = "08752f33-3b29-4816-b76b-ea8a968ed3c5"
+export const APPROVE_TASK = "db4e40c5-2ba9-4b6d-b8a6-a16a250ff99c"
+
export const VIEW_TASK = "9fcc5f87-25e3-4846-90ac-67a71ab92e3c"
export const ASSIGN_REPORT_TASK = "6a32379b-8b3f-49a6-8c48-4b7ac1b55dc2"
diff --git a/src/utils/dateUtils.jsx b/src/utils/dateUtils.jsx
index 0105d4aa..4d6643b6 100644
--- a/src/utils/dateUtils.jsx
+++ b/src/utils/dateUtils.jsx
@@ -69,4 +69,13 @@ export const formatNumber = (num) => {
};
export const formatUTCToLocalTime = (datetime) =>{
return moment.utc(datetime).local().format("MMMM DD, YYYY [at] hh:mm A");
+}
+
+export const getCompletionPercentage = (completedWork, plannedWork)=> {
+ if (!plannedWork || plannedWork === 0) return 0;
+
+ const percentage = (completedWork / plannedWork) * 100;
+ const clamped = Math.min(Math.max(percentage, 0), 100);
+
+ return clamped.toFixed(2);
}
\ No newline at end of file