Added Document Managment feature #388
9
src/components/Documents/DocumentFilterPanel.jsx
Normal file
9
src/components/Documents/DocumentFilterPanel.jsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const DocumentFilterPanel = () => {
|
||||||
|
return (
|
||||||
|
<h1>filter</h1>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocumentFilterPanel
|
||||||
@ -1,52 +1,84 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useEffect, useState } from "react";
|
||||||
import GlobalModel from '../common/GlobalModel'
|
import GlobalModel from "../common/GlobalModel";
|
||||||
import NewDocument from './NewDocument'
|
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 = () => {
|
useEffect(() => {
|
||||||
const [isUpload,setUpload] =useState(false);
|
setShowTrigger(true);
|
||||||
|
setOffcanvasContent("Document Filters", <DocumentFilterPanel />);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
setShowTrigger(false);
|
||||||
|
setOffcanvasContent("", null);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<div className=''>
|
<div className="mt-5">
|
||||||
|
<div className="card d-flex p-2">
|
||||||
<div className="card d-flex p-2">
|
<div className="row align-items-center">
|
||||||
<div className="row align-items-center">
|
{/* Search */}
|
||||||
{/* Search */}
|
<div className="col-6 col-md-6 col-lg-3 mb-md-0">
|
||||||
<div className="col-6 col-md-6 col-lg-3 mb-md-0">
|
<input
|
||||||
<input
|
type="search"
|
||||||
type="search"
|
value={searchText}
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
placeholder="Search Document"
|
placeholder="Search Document"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Actions */}
|
|
||||||
<div className="col-6 col-md-6 col-lg-9 text-end">
|
|
||||||
<span className="text-tiny text-muted p-1 border-0 bg-none lead mx-3 cursor-pointer">
|
|
||||||
Refresh
|
|
||||||
< i className={`bx bx-refresh ms-1 `}></i>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
title="Add New Document"
|
|
||||||
className="p-1 bg-primary rounded-circle cursor-pointer"
|
|
||||||
onClick={()=>setUpload(true)}
|
|
||||||
>
|
|
||||||
<i className="bx bx-plus fs-4 text-white"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{isUpload && (
|
|
||||||
<GlobalModel isOpen={isUpload} closeModal={()=>setUpload(false)}>
|
|
||||||
<NewDocument closeModal={()=>setUpload(false)}/>
|
|
||||||
</GlobalModel>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Documents
|
{/* Actions */}
|
||||||
|
<div className="col-6 col-md-6 col-lg-9 text-end">
|
||||||
|
<span className="text-tiny text-muted p-1 border-0 bg-none lead mx-3 cursor-pointer" disabled={isRefetching}
|
||||||
|
onClick={() => refetchFn && refetchFn()}>
|
||||||
|
Refresh
|
||||||
|
<i className={`bx bx-refresh ms-1 ${
|
||||||
|
isRefetching ? "bx-spin" : ""
|
||||||
|
}`}></i>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
title="Add New Document"
|
||||||
|
className="p-1 bg-primary rounded-circle cursor-pointer"
|
||||||
|
onClick={() => setUpload(true)}
|
||||||
|
>
|
||||||
|
<i className="bx bx-plus fs-4 text-white"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DocumentsList
|
||||||
|
Document_Entity={Document_Entity}
|
||||||
|
Entity={Entity}
|
||||||
|
searchText={searchText}
|
||||||
|
setIsRefetching={setIsRefetching}
|
||||||
|
setRefetchFn={setRefetchFn}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isUpload && (
|
||||||
|
<GlobalModel isOpen={isUpload} closeModal={() => setUpload(false)}>
|
||||||
|
<NewDocument
|
||||||
|
closeModal={() => setUpload(false)}
|
||||||
|
Document_Entity={Document_Entity}
|
||||||
|
Entity={Entity}
|
||||||
|
/>
|
||||||
|
</GlobalModel>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Documents;
|
||||||
|
|||||||
142
src/components/Documents/DocumentsList.jsx
Normal file
142
src/components/Documents/DocumentsList.jsx
Normal file
@ -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 (
|
||||||
|
<span className="badge rounded-pill bg-label-success">Verified</span>
|
||||||
|
);
|
||||||
|
case false:
|
||||||
|
return (
|
||||||
|
<span className="badge rounded-pill bg-label-danger">Rejected</span>
|
||||||
|
);
|
||||||
|
case null:
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<span className="badge rounded-pill bg-label-primary">Pending</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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 <Loader />;
|
||||||
|
if (isError) return <p>Error: {error?.message || "Something went wrong"}</p>;
|
||||||
|
|
||||||
|
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) => (
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<Avatar
|
||||||
|
size="xs"
|
||||||
|
classAvatar="m-0"
|
||||||
|
firstName={e.uploadedBy?.firstName}
|
||||||
|
lastName={e.uploadedBy?.lastName}
|
||||||
|
/>
|
||||||
|
<span className="text-truncate ">
|
||||||
|
{`${e.uploadedBy?.firstName ?? ""} ${
|
||||||
|
e.uploadedBy?.lastName ?? ""
|
||||||
|
}`.trim() || "N/A"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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 (
|
||||||
|
<div className="table-responsive">
|
||||||
|
<table className="table border-top dataTable text-nowrap">
|
||||||
|
<thead>
|
||||||
|
<tr className="shadow-sm">
|
||||||
|
{DocumentColumns.map((col) => (
|
||||||
|
<th key={col.key} className={`sorting ${col.align}`}>
|
||||||
|
{col.label}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
<th className="sticky-action-column bg-white text-center">
|
||||||
|
Action
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-start">
|
||||||
|
{data?.map((doc) => (
|
||||||
|
<tr key={doc.id}>
|
||||||
|
{DocumentColumns.map((col) => (
|
||||||
|
<td key={col.key} className={`sorting ${col.align}`}>
|
||||||
|
{col.customRender ? col.customRender(doc) : col.getValue(doc)}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
<td className="text-center">
|
||||||
|
<div className="d-flex justify-content-center gap-2">
|
||||||
|
<i className="bx bx-show text-primary cursor-pointer"></i>
|
||||||
|
|
||||||
|
<i className="bx bx-edit text-secondary cursor-pointer"></i>
|
||||||
|
|
||||||
|
<i className="bx bx-trash text-danger cursor-pointer"></i>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DocumentsList;
|
||||||
@ -3,7 +3,6 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { useForm, FormProvider } from "react-hook-form";
|
import { useForm, FormProvider } from "react-hook-form";
|
||||||
import { defaultDocumentValues, DocumentPayloadSchema } from "./DocumentSchema";
|
import { defaultDocumentValues, DocumentPayloadSchema } from "./DocumentSchema";
|
||||||
import Label from "../common/Label";
|
import Label from "../common/Label";
|
||||||
import { DOCUMENTS_ENTITIES } from "../../utils/constants";
|
|
||||||
import {
|
import {
|
||||||
useDocumentCategories,
|
useDocumentCategories,
|
||||||
useDocumentTypes,
|
useDocumentTypes,
|
||||||
@ -11,7 +10,6 @@ import {
|
|||||||
import TagInput from "../common/TagInput";
|
import TagInput from "../common/TagInput";
|
||||||
import { useUploadDocument } from "../../hooks/useDocument";
|
import { useUploadDocument } from "../../hooks/useDocument";
|
||||||
import showToast from "../../services/toastService";
|
import showToast from "../../services/toastService";
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
|
|
||||||
const toBase64 = (file) =>
|
const toBase64 = (file) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
@ -21,8 +19,8 @@ const toBase64 = (file) =>
|
|||||||
reader.onerror = (err) => reject(err);
|
reader.onerror = (err) => reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
const NewDocument = ({closeModal}) => {
|
const NewDocument = ({closeModal,Document_Entity,Entity}) => {
|
||||||
const { employeeId } = useParams();
|
|
||||||
const [selectedType, setSelectedType] = useState(null);
|
const [selectedType, setSelectedType] = useState(null);
|
||||||
const [selectedCategory, setSelectedCategory] = useState(null);
|
const [selectedCategory, setSelectedCategory] = useState(null);
|
||||||
const [schema, setSchema] = useState(() => DocumentPayloadSchema({}));
|
const [schema, setSchema] = useState(() => DocumentPayloadSchema({}));
|
||||||
@ -44,7 +42,7 @@ const NewDocument = ({closeModal}) => {
|
|||||||
closeModal();
|
closeModal();
|
||||||
});
|
});
|
||||||
const onSubmit = (data) => {
|
const onSubmit = (data) => {
|
||||||
const DocumentPayload = { ...data, entityId: employeeId };
|
const DocumentPayload = { ...data, entityId: Entity };
|
||||||
UploadDocument(DocumentPayload);
|
UploadDocument(DocumentPayload);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -54,7 +52,7 @@ const NewDocument = ({closeModal}) => {
|
|||||||
|
|
||||||
// This hooks calling api base Entity(Employee) and Category
|
// This hooks calling api base Entity(Employee) and Category
|
||||||
const { DocumentCategories, isLoading } = useDocumentCategories(
|
const { DocumentCategories, isLoading } = useDocumentCategories(
|
||||||
DOCUMENTS_ENTITIES.EmployeeEntity
|
Document_Entity
|
||||||
);
|
);
|
||||||
|
|
||||||
const categoryId = watch("documentCategoryId");
|
const categoryId = watch("documentCategoryId");
|
||||||
|
|||||||
@ -9,7 +9,6 @@ const EmpDashboard = ({ profile }) => {
|
|||||||
refetch,
|
refetch,
|
||||||
} = useProjectsAllocationByEmployee(profile?.id);
|
} = useProjectsAllocationByEmployee(profile?.id);
|
||||||
|
|
||||||
console.log(projectList);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
|
|||||||
@ -2,11 +2,14 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { ComingSoonPage } from "../../pages/Misc/ComingSoonPage";
|
import { ComingSoonPage } from "../../pages/Misc/ComingSoonPage";
|
||||||
import DocumentPage from "../../pages/Documents/DocumentPage";
|
import DocumentPage from "../../pages/Documents/DocumentPage";
|
||||||
import Documents from "../Documents/Documents";
|
import Documents from "../Documents/Documents";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { DOCUMENTS_ENTITIES } from "../../utils/constants";
|
||||||
|
|
||||||
const EmpDocuments = ({ profile, loggedInUser }) => {
|
const EmpDocuments = ({ profile, loggedInUser }) => {
|
||||||
|
const {employeeId} = useParams()
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Documents/>
|
<Documents Document_Entity={DOCUMENTS_ENTITIES.EmployeeEntity} Entity={employeeId} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
14
src/components/Project/ProjectDocuments.jsx
Normal file
14
src/components/Project/ProjectDocuments.jsx
Normal file
@ -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 (
|
||||||
|
<>
|
||||||
|
<Documents Document_Entity={DOCUMENTS_ENTITIES.ProjectEntity} Entity={selectedProject} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProjectDocuments;
|
||||||
@ -67,15 +67,15 @@ const ProjectNav = ({ onPillClick, activePill }) => {
|
|||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<a
|
<a
|
||||||
className={`nav-link ${
|
className={`nav-link ${
|
||||||
activePill === "imagegallary" ? "active" : ""
|
activePill === "documents" ? "active" : ""
|
||||||
} fs-6`}
|
} fs-6`}
|
||||||
href="#"
|
href="#"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault(); // Prevent page reload
|
e.preventDefault(); // Prevent page reload
|
||||||
onPillClick("imagegallary");
|
onPillClick("documents");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i className='bx bxs-cog bx-sm me-1_5'></i> <span className="d-none d-md-inline">project Setup</span>
|
<i className='bx bxs-cog bx-sm me-1_5'></i> <span className="d-none d-md-inline">Documents</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,46 @@
|
|||||||
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||||
|
|
||||||
//----------------------- MUTATION -------------------------
|
|
||||||
|
|
||||||
import { useMutation } from "@tanstack/react-query"
|
|
||||||
import showToast from "../services/toastService";
|
import showToast from "../services/toastService";
|
||||||
import { DocumentRepository } from "../repositories/DocumentRepository";
|
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)=>{
|
export const useUploadDocument =(onSuccessCallBack)=>{
|
||||||
|
const queryClient = useQueryClient()
|
||||||
return useMutation(({
|
return useMutation(({
|
||||||
mutationFn:async(DocumentPayload)=>DocumentRepository.uploadDocument(DocumentPayload),
|
mutationFn:async(DocumentPayload)=>DocumentRepository.uploadDocument(DocumentPayload),
|
||||||
onSuccess:(data,variables)=>{
|
onSuccess:(data,variables)=>{
|
||||||
|
queryClient.invalidateQueries({queryKey:["DocumentList"]});
|
||||||
if(onSuccessCallBack) onSuccessCallBack()
|
if(onSuccessCallBack) onSuccessCallBack()
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
console.log(error)
|
||||||
showToast(
|
showToast(
|
||||||
error.message || "Something went wrong please try again !",
|
error.response.data.message || "Something went wrong please try again !",
|
||||||
"error"
|
"error"
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -16,9 +16,7 @@ import {
|
|||||||
useSelectedproject,
|
useSelectedproject,
|
||||||
} from "../../slices/apiDataManager";
|
} from "../../slices/apiDataManager";
|
||||||
import "./ProjectDetails.css";
|
import "./ProjectDetails.css";
|
||||||
import {
|
import { useProjectDetails } from "../../hooks/useProjects";
|
||||||
useProjectDetails,
|
|
||||||
} from "../../hooks/useProjects";
|
|
||||||
import { ComingSoonPage } from "../Misc/ComingSoonPage";
|
import { ComingSoonPage } from "../Misc/ComingSoonPage";
|
||||||
import Directory from "../Directory/Directory";
|
import Directory from "../Directory/Directory";
|
||||||
import eventBus from "../../services/eventBus";
|
import eventBus from "../../services/eventBus";
|
||||||
@ -26,19 +24,20 @@ import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChar
|
|||||||
import { useProjectName } from "../../hooks/useProjects";
|
import { useProjectName } from "../../hooks/useProjects";
|
||||||
import AttendanceOverview from "../../components/Dashboard/AttendanceChart";
|
import AttendanceOverview from "../../components/Dashboard/AttendanceChart";
|
||||||
import { setProjectId } from "../../slices/localVariablesSlice";
|
import { setProjectId } from "../../slices/localVariablesSlice";
|
||||||
|
import ProjectDocument from "../../components/Project/ProjectDocuments";
|
||||||
|
import ProjectDocuments from "../../components/Project/ProjectDocuments";
|
||||||
|
|
||||||
const ProjectDetails = () => {
|
const ProjectDetails = () => {
|
||||||
|
const projectId = useSelectedproject();
|
||||||
const projectId = useSelectedproject()
|
|
||||||
|
|
||||||
const { projectNames, fetchData } = useProjectName();
|
const { projectNames, fetchData } = useProjectName();
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (projectId == null) {
|
if (projectId == null) {
|
||||||
dispatch(setProjectId(projectNames[0]?.id));
|
dispatch(setProjectId(projectNames[0]?.id));
|
||||||
}
|
}
|
||||||
}, [projectNames])
|
}, [projectNames]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
projects_Details,
|
projects_Details,
|
||||||
@ -49,12 +48,15 @@ const ProjectDetails = () => {
|
|||||||
|
|
||||||
// const [activePill, setActivePill] = useState("profile");
|
// const [activePill, setActivePill] = useState("profile");
|
||||||
const [activePill, setActivePill] = useState(() => {
|
const [activePill, setActivePill] = useState(() => {
|
||||||
return localStorage.getItem("lastActiveProjectTab") || "profile";
|
return localStorage.getItem("lastActiveProjectTab") || "profile";
|
||||||
});
|
});
|
||||||
|
|
||||||
const handler = useCallback(
|
const handler = useCallback(
|
||||||
(msg) => {
|
(msg) => {
|
||||||
if (msg.keyword === "Update_Project" && projects_Details?.id === msg.response.id) {
|
if (
|
||||||
|
msg.keyword === "Update_Project" &&
|
||||||
|
projects_Details?.id === msg.response.id
|
||||||
|
) {
|
||||||
refetch();
|
refetch();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -66,11 +68,10 @@ const ProjectDetails = () => {
|
|||||||
return () => eventBus.off("project", handler);
|
return () => eventBus.off("project", handler);
|
||||||
}, [handler]);
|
}, [handler]);
|
||||||
|
|
||||||
const handlePillClick = (pillKey) => {
|
const handlePillClick = (pillKey) => {
|
||||||
setActivePill(pillKey);
|
setActivePill(pillKey);
|
||||||
localStorage.setItem("lastActiveProjectTab", pillKey); // ✅ Save to localStorage
|
localStorage.setItem("lastActiveProjectTab", pillKey); // ✅ Save to localStorage
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
if (projectLoading || !projects_Details) return <Loader />;
|
if (projectLoading || !projects_Details) return <Loader />;
|
||||||
@ -85,9 +86,14 @@ const ProjectDetails = () => {
|
|||||||
<ProjectOverview project={projectId} />
|
<ProjectOverview project={projectId} />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-lg-8 col-md-7 mt-5">
|
<div className="col-lg-8 col-md-7 mt-5">
|
||||||
<ProjectProgressChart ShowAllProject="false" DefaultRange="1M" />
|
<ProjectProgressChart
|
||||||
<div className="mt-5"> <AttendanceOverview /></div>
|
ShowAllProject="false"
|
||||||
|
DefaultRange="1M"
|
||||||
|
/>
|
||||||
|
<div className="mt-5">
|
||||||
|
{" "}
|
||||||
|
<AttendanceOverview />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@ -103,14 +109,10 @@ const ProjectDetails = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
case "infra":
|
case "infra":
|
||||||
return (
|
return <ProjectInfra data={projects_Details} onDataChange={refetch} />;
|
||||||
<ProjectInfra data={projects_Details} onDataChange={refetch} />
|
|
||||||
);
|
|
||||||
|
|
||||||
case "workplan":
|
case "workplan":
|
||||||
return (
|
return <WorkPlan data={projects_Details} onDataChange={refetch} />;
|
||||||
<WorkPlan data={projects_Details} onDataChange={refetch} />
|
|
||||||
);
|
|
||||||
|
|
||||||
case "directory":
|
case "directory":
|
||||||
return (
|
return (
|
||||||
@ -118,6 +120,12 @@ const ProjectDetails = () => {
|
|||||||
<Directory IsPage={false} prefernceContacts={projects_Details.id} />
|
<Directory IsPage={false} prefernceContacts={projects_Details.id} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
case "documents":
|
||||||
|
return (
|
||||||
|
<div className="row">
|
||||||
|
<ProjectDocuments />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return <ComingSoonPage />;
|
return <ComingSoonPage />;
|
||||||
@ -142,4 +150,4 @@ const ProjectDetails = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ProjectDetails;
|
export default ProjectDetails;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user