diff --git a/src/components/Documents/DocumentFilterPanel.jsx b/src/components/Documents/DocumentFilterPanel.jsx new file mode 100644 index 00000000..0fa039fa --- /dev/null +++ b/src/components/Documents/DocumentFilterPanel.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +const DocumentFilterPanel = () => { + return ( +

filter

+ ) +} + +export default DocumentFilterPanel \ No newline at end of file diff --git a/src/components/Documents/Documents.jsx b/src/components/Documents/Documents.jsx index 5c8042d5..cb1cd96c 100644 --- a/src/components/Documents/Documents.jsx +++ b/src/components/Documents/Documents.jsx @@ -1,52 +1,84 @@ -import React, { useState } from 'react' -import GlobalModel from '../common/GlobalModel' -import NewDocument from './NewDocument' +import React, { useEffect, useState } from "react"; +import GlobalModel from "../common/GlobalModel"; +import NewDocument from "./NewDocument"; +import { DOCUMENTS_ENTITIES } from "../../utils/constants"; +import { useParams } from "react-router-dom"; +import DocumentsList from "./DocumentsList"; +import DocumentFilterPanel from "./DocumentFilterPanel"; +import { useFab } from "../../Context/FabContext"; +const Documents = ({ Document_Entity, Entity }) => { + const [searchText, setSearchText] = useState(""); + const [isRefetching, setIsRefetching] = useState(false); + const [refetchFn, setRefetchFn] = useState(null); + const { employeeId } = useParams(); + const [isUpload, setUpload] = useState(false); + const { setOffcanvasContent, setShowTrigger } = useFab(); -const Documents = () => { - const [isUpload,setUpload] =useState(false); + useEffect(() => { + setShowTrigger(true); + setOffcanvasContent("Document Filters", ); + return () => { + setShowTrigger(false); + setOffcanvasContent("", null); + }; + }, []); return ( -
- -
-
- {/* Search */} -
- -
- - {/* Actions */} -
- - Refresh - < i className={`bx bx-refresh ms-1 `}> - - - -
-
+
+
+
+ {/* Search */} +
+ setSearchText(e.target.value)} + className="form-control form-control-sm" + placeholder="Search Document" + />
- {isUpload && ( - setUpload(false)}> - setUpload(false)}/> - - )} - -
- ) -} -export default Documents \ No newline at end of file + {/* Actions */} +
+ refetchFn && refetchFn()}> + Refresh + + + + +
+
+ +
+ + {isUpload && ( + setUpload(false)}> + setUpload(false)} + Document_Entity={Document_Entity} + Entity={Entity} + /> + + )} +
+ ); +}; + +export default Documents; diff --git a/src/components/Documents/DocumentsList.jsx b/src/components/Documents/DocumentsList.jsx new file mode 100644 index 00000000..11bbb649 --- /dev/null +++ b/src/components/Documents/DocumentsList.jsx @@ -0,0 +1,142 @@ +import React, { useEffect } from "react"; +import { useDocumentListByEntityId } from "../../hooks/useDocument"; +import { ITEMS_PER_PAGE } from "../../utils/constants"; +import Avatar from "../common/Avatar"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import Loader from "../common/Loader"; +import { useDebounce } from "../../utils/appUtils"; + +export const getDocuementsStatus = (status) => { + switch (status) { + case true: + return ( + Verified + ); + case false: + return ( + Rejected + ); + case null: + default: + return ( + Pending + ); + } +}; +const DocumentsList = ({ Document_Entity, Entity,searchText ,setIsRefetching, + setRefetchFn,}) => { + const debouncedSearch = useDebounce(searchText, 500); + const { data, isError, isLoading, error,refetch,isFetching } = useDocumentListByEntityId( + Document_Entity, + Entity, + ITEMS_PER_PAGE, + 1,{},debouncedSearch + ); + + // Pass the refetch function to parent when component mounts + useEffect(() => { + setRefetchFn(() => refetch); + }, [setRefetchFn, refetch]); + + // Sync fetching status with parent + useEffect(() => { + setIsRefetching(isFetching); + }, [isFetching, setIsRefetching]); + + if (isLoading) return ; + if (isError) return

Error: {error?.message || "Something went wrong"}

; + + const DocumentColumns = [ + { + key: "name", + label: "Name", + getValue: (e) => e.name || "N/A", + align: "text-start", + }, + { + key: "documentType", + label: "Document Type", + getValue: (e) => e.documentType?.name || "N/A", + align: "text-start", + }, + { + key: "uploadedBy", + label: "Uploaded By", + align: "text-start", + getValue: (e) => + `${e.uploadedBy?.firstName ?? ""} ${ + e.uploadedBy?.lastName ?? "" + }`.trim() || "N/A", + customRender: (e) => ( +
+ + + {`${e.uploadedBy?.firstName ?? ""} ${ + e.uploadedBy?.lastName ?? "" + }`.trim() || "N/A"} + +
+ ), + }, + { + key: "uploadedAt", + label: "Uploaded on", + getValue: (e) => formatUTCToLocalTime(e?.uploadedAt), + isAlwaysVisible: true, + align: "text-center", + }, + { + key: "Status", + label: "status", + getValue: (e) => getDocuementsStatus(e.isVerified), + isAlwaysVisible: true, + align: "text-center", + }, + ]; + + return ( +
+ + + + {DocumentColumns.map((col) => ( + + ))} + + + + + {data?.map((doc) => ( + + {DocumentColumns.map((col) => ( + + ))} + + + ))} + +
+ {col.label} + + Action +
+ {col.customRender ? col.customRender(doc) : col.getValue(doc)} + +
+ + + + + +
+
+
+ ); +}; + +export default DocumentsList; diff --git a/src/components/Documents/NewDocument.jsx b/src/components/Documents/NewDocument.jsx index 7bdfa352..180ee24e 100644 --- a/src/components/Documents/NewDocument.jsx +++ b/src/components/Documents/NewDocument.jsx @@ -3,7 +3,6 @@ import React, { useEffect, useState } from "react"; import { useForm, FormProvider } from "react-hook-form"; import { defaultDocumentValues, DocumentPayloadSchema } from "./DocumentSchema"; import Label from "../common/Label"; -import { DOCUMENTS_ENTITIES } from "../../utils/constants"; import { useDocumentCategories, useDocumentTypes, @@ -11,7 +10,6 @@ import { import TagInput from "../common/TagInput"; import { useUploadDocument } from "../../hooks/useDocument"; import showToast from "../../services/toastService"; -import { useParams } from "react-router-dom"; const toBase64 = (file) => new Promise((resolve, reject) => { @@ -21,8 +19,8 @@ const toBase64 = (file) => reader.onerror = (err) => reject(err); }); -const NewDocument = ({closeModal}) => { - const { employeeId } = useParams(); +const NewDocument = ({closeModal,Document_Entity,Entity}) => { + const [selectedType, setSelectedType] = useState(null); const [selectedCategory, setSelectedCategory] = useState(null); const [schema, setSchema] = useState(() => DocumentPayloadSchema({})); @@ -44,7 +42,7 @@ const NewDocument = ({closeModal}) => { closeModal(); }); const onSubmit = (data) => { - const DocumentPayload = { ...data, entityId: employeeId }; + const DocumentPayload = { ...data, entityId: Entity }; UploadDocument(DocumentPayload); }; @@ -54,7 +52,7 @@ const NewDocument = ({closeModal}) => { // This hooks calling api base Entity(Employee) and Category const { DocumentCategories, isLoading } = useDocumentCategories( - DOCUMENTS_ENTITIES.EmployeeEntity + Document_Entity ); const categoryId = watch("documentCategoryId"); diff --git a/src/components/Employee/EmpDashboard.jsx b/src/components/Employee/EmpDashboard.jsx index b25f6803..85c97836 100644 --- a/src/components/Employee/EmpDashboard.jsx +++ b/src/components/Employee/EmpDashboard.jsx @@ -9,7 +9,6 @@ const EmpDashboard = ({ profile }) => { refetch, } = useProjectsAllocationByEmployee(profile?.id); - console.log(projectList); return ( <>
diff --git a/src/components/Employee/EmpDocuments.jsx b/src/components/Employee/EmpDocuments.jsx index 39d6e900..b4c8046b 100644 --- a/src/components/Employee/EmpDocuments.jsx +++ b/src/components/Employee/EmpDocuments.jsx @@ -2,11 +2,14 @@ import React, { useState, useEffect } from "react"; import { ComingSoonPage } from "../../pages/Misc/ComingSoonPage"; import DocumentPage from "../../pages/Documents/DocumentPage"; import Documents from "../Documents/Documents"; +import { useParams } from "react-router-dom"; +import { DOCUMENTS_ENTITIES } from "../../utils/constants"; const EmpDocuments = ({ profile, loggedInUser }) => { + const {employeeId} = useParams() return ( <> - + ); }; diff --git a/src/components/Project/ProjectDocuments.jsx b/src/components/Project/ProjectDocuments.jsx new file mode 100644 index 00000000..061a25ec --- /dev/null +++ b/src/components/Project/ProjectDocuments.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import Documents from "../Documents/Documents"; +import { useSelectedproject } from "../../slices/apiDataManager"; +import { DOCUMENTS_ENTITIES } from "../../utils/constants"; +const ProjectDocuments = () => { + const selectedProject = useSelectedproject() + return ( + <> + + + ); +}; + +export default ProjectDocuments; diff --git a/src/components/Project/ProjectNav.jsx b/src/components/Project/ProjectNav.jsx index 0a7f960e..633be7d7 100644 --- a/src/components/Project/ProjectNav.jsx +++ b/src/components/Project/ProjectNav.jsx @@ -67,15 +67,15 @@ const ProjectNav = ({ onPillClick, activePill }) => {
  • { e.preventDefault(); // Prevent page reload - onPillClick("imagegallary"); + onPillClick("documents"); }} > - project Setup + Documents
  • diff --git a/src/hooks/useDocument.js b/src/hooks/useDocument.js index 4885359b..c9262491 100644 --- a/src/hooks/useDocument.js +++ b/src/hooks/useDocument.js @@ -1,20 +1,46 @@ - - -//----------------------- MUTATION ------------------------- - -import { useMutation } from "@tanstack/react-query" +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import showToast from "../services/toastService"; import { DocumentRepository } from "../repositories/DocumentRepository"; +// ----------------------Query------------------------------- +const cleanFilter = (filter) => { + const cleaned = { ...filter }; + + ["uploadedByIds", "documentCategoryIds", "documentTypeIds", "documentTagIds"].forEach((key) => { + if (Array.isArray(cleaned[key]) && cleaned[key].length === 0) { + delete cleaned[key]; + } + + }); + + return cleaned; +}; +export const useDocumentListByEntityId=(entityTypeId,entityId,pageSize, pageNumber, filter,searchString="")=>{ + return useQuery({ + queryKey:["DocumentList",entityTypeId,entityId,pageSize, pageNumber, filter,searchString], + queryFn:async()=>{ + const cleanedFilter = cleanFilter(filter); + const resp = await DocumentRepository.getDocumentList(entityTypeId,entityId,pageSize, pageNumber,cleanedFilter,searchString); + return resp.data; + }, + enabled:!!entityTypeId && !! entityId + }) +} + +//----------------------- MUTATION ------------------------- + export const useUploadDocument =(onSuccessCallBack)=>{ + const queryClient = useQueryClient() return useMutation(({ mutationFn:async(DocumentPayload)=>DocumentRepository.uploadDocument(DocumentPayload), onSuccess:(data,variables)=>{ + queryClient.invalidateQueries({queryKey:["DocumentList"]}); if(onSuccessCallBack) onSuccessCallBack() }, onError: (error) => { + console.log(error) showToast( - error.message || "Something went wrong please try again !", + error.response.data.message || "Something went wrong please try again !", "error" ); }, diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx index 75f7705b..78b2b662 100644 --- a/src/pages/project/ProjectDetails.jsx +++ b/src/pages/project/ProjectDetails.jsx @@ -16,9 +16,7 @@ import { useSelectedproject, } from "../../slices/apiDataManager"; import "./ProjectDetails.css"; -import { - useProjectDetails, -} from "../../hooks/useProjects"; +import { useProjectDetails } from "../../hooks/useProjects"; import { ComingSoonPage } from "../Misc/ComingSoonPage"; import Directory from "../Directory/Directory"; import eventBus from "../../services/eventBus"; @@ -26,19 +24,20 @@ import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChar import { useProjectName } from "../../hooks/useProjects"; import AttendanceOverview from "../../components/Dashboard/AttendanceChart"; import { setProjectId } from "../../slices/localVariablesSlice"; +import ProjectDocument from "../../components/Project/ProjectDocuments"; +import ProjectDocuments from "../../components/Project/ProjectDocuments"; const ProjectDetails = () => { - - const projectId = useSelectedproject() + const projectId = useSelectedproject(); const { projectNames, fetchData } = useProjectName(); - const dispatch = useDispatch() + const dispatch = useDispatch(); useEffect(() => { if (projectId == null) { dispatch(setProjectId(projectNames[0]?.id)); } - }, [projectNames]) + }, [projectNames]); const { projects_Details, @@ -49,12 +48,15 @@ const ProjectDetails = () => { // const [activePill, setActivePill] = useState("profile"); const [activePill, setActivePill] = useState(() => { - return localStorage.getItem("lastActiveProjectTab") || "profile"; -}); + return localStorage.getItem("lastActiveProjectTab") || "profile"; + }); const handler = useCallback( (msg) => { - if (msg.keyword === "Update_Project" && projects_Details?.id === msg.response.id) { + if ( + msg.keyword === "Update_Project" && + projects_Details?.id === msg.response.id + ) { refetch(); } }, @@ -66,11 +68,10 @@ const ProjectDetails = () => { return () => eventBus.off("project", handler); }, [handler]); - const handlePillClick = (pillKey) => { - setActivePill(pillKey); - localStorage.setItem("lastActiveProjectTab", pillKey); // ✅ Save to localStorage -}; - + const handlePillClick = (pillKey) => { + setActivePill(pillKey); + localStorage.setItem("lastActiveProjectTab", pillKey); // ✅ Save to localStorage + }; const renderContent = () => { if (projectLoading || !projects_Details) return ; @@ -85,9 +86,14 @@ const ProjectDetails = () => {
    - -
    - + +
    + {" "} + +
    @@ -103,14 +109,10 @@ const ProjectDetails = () => { ); case "infra": - return ( - - ); + return ; case "workplan": - return ( - - ); + return ; case "directory": return ( @@ -118,6 +120,12 @@ const ProjectDetails = () => { ); + case "documents": + return ( +
    + +
    + ); default: return ; @@ -142,4 +150,4 @@ const ProjectDetails = () => { ); }; -export default ProjectDetails; \ No newline at end of file +export default ProjectDetails;