Compare commits

..

No commits in common. "fd143285629882a667ffa0a24c9d0c4eba01e77c" and "528d3b756ce46dbddba1f463acde7a7a680c80bb" have entirely different histories.

5 changed files with 153 additions and 227 deletions

View File

@ -21,13 +21,12 @@ export const DocumentContext = createContext();
export const useDocumentContext = () => { export const useDocumentContext = () => {
const context = useContext(DocumentContext); const context = useContext(DocumentContext);
if (!context) { if (!context) {
throw new Error( throw new Error("useDocumentContext must be used within an DocumentProvider");
"useDocumentContext must be used within an DocumentProvider"
);
} }
return context; return context;
}; };
export const getDocuementsStatus = (status) => { export const getDocuementsStatus = (status) => {
switch (status) { switch (status) {
case true: case true:
@ -47,22 +46,22 @@ export const getDocuementsStatus = (status) => {
}; };
const Documents = ({ Document_Entity, Entity }) => { const Documents = ({ Document_Entity, Entity }) => {
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
const [isActive, setIsActive] = useState(true);
const [filters, setFilter] = useState(); const [filters, setFilter] = useState();
const [isRefetching, setIsRefetching] = useState(false); const [isRefetching, setIsRefetching] = useState(false);
const [refetchFn, setRefetchFn] = useState(null); const [refetchFn, setRefetchFn] = useState(null);
const [DocumentEntity, setDocumentEntity] = useState(Document_Entity); const [DocumentEntity,setDocumentEntity] = useState(Document_Entity)
const { employeeId } = useParams(); const { employeeId } = useParams();
const [OpenDocument, setOpenDocument] = useState(false); const [OpenDocument,setOpenDocument] = useState(false)
const [ManageDoc, setManageDoc] = useState({ const [ManageDoc, setManageDoc] = useState({
document: null, document: null,
isOpen: false, isOpen: false,
}); });
const [viewDoc, setViewDoc] = useState({ const [viewDoc, setViewDoc] = useState({
document: null, document: null,
isOpen: false, isOpen: false,
}); });
const { setOffcanvasContent, setShowTrigger } = useFab(); const { setOffcanvasContent, setShowTrigger } = useFab();
const methods = useForm({ const methods = useForm({
@ -96,33 +95,34 @@ const Documents = ({ Document_Entity, Entity }) => {
viewDoc, viewDoc,
setViewDoc, setViewDoc,
setOpenDocument, setOpenDocument,
OpenDocument, OpenDocument
}; }
useEffect(() => { useEffect(()=>{
if (Document_Entity) { if(Document_Entity){
setDocumentEntity(Document_Entity); setDocumentEntity(Document_Entity)
} }
}, [Document_Entity]); },[Document_Entity])
return ( return (
<DocumentContext.Provider value={contextValues}> <DocumentContext.Provider value={contextValues}>
<div className="mt-5">
<div className="card d-flex p-2">
<div className="row align-items-center">
{/* Search */}
<div className="col-6 col-md-6 col-lg-3 mb-md-0">
<input
type="search"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
className="form-control form-control-sm"
placeholder="Search Document"
/>
</div>
{/* Actions */} <div className="mt-5">
<div className="col-6 col-md-6 col-lg-9 text-end"> <div className="card d-flex p-2">
{/* <span <div className="row align-items-center">
{/* Search */}
<div className="col-6 col-md-6 col-lg-3 mb-md-0">
<input
type="search"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
className="form-control form-control-sm"
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" className="text-tiny text-muted p-1 border-0 bg-none lead mx-3 cursor-pointer"
disabled={isRefetching} disabled={isRefetching}
onClick={() => { onClick={() => {
@ -138,96 +138,71 @@ const Documents = ({ Document_Entity, Entity }) => {
}`} }`}
></i> ></i>
</span> */} </span> */}
<label className="switch switch-sm">
<input
type="checkbox"
className="switch-input"
checked={isActive}
onChange={(e) => setIsActive(e.target.checked)}
/>
<span className="switch-toggle-slider">
<span className="switch-on"></span>
<span className="switch-off"></span>
</span>
<span className="switch-label">
{isActive ? "Active" : "In-Active"}
</span>
</label>
<button <button
type="button" type="button"
title="Add New Document" title="Add New Document"
className="p-1 bg-primary rounded-circle cursor-pointer" className="p-1 bg-primary rounded-circle cursor-pointer"
onClick={() => onClick={() => setManageDoc({
setManageDoc({ document: null,
document: null, isOpen: true,
isOpen: true, })}
}) >
} <i className="bx bx-plus fs-4 text-white"></i>
> </button>
<i className="bx bx-plus fs-4 text-white"></i>
</button>
</div>
</div> </div>
<DocumentsList
Document_Entity={DocumentEntity}
Entity={Entity}
filters={filters}
searchText={searchText}
setIsRefetching={setIsRefetching}
setRefetchFn={setRefetchFn}
isActive={isActive}
/>
</div> </div>
<DocumentsList
Document_Entity={DocumentEntity}
Entity={Entity}
filters={filters}
searchText={searchText}
setIsRefetching={setIsRefetching}
setRefetchFn={setRefetchFn}
/>
</div>
{ManageDoc.isOpen && ( {ManageDoc.isOpen && (
<GlobalModel <GlobalModel
isOpen={ManageDoc.isOpen} isOpen={ManageDoc.isOpen}
closeModal={() =>
setManageDoc({
document: null,
isOpen: false,
})
}
>
<ManageDocument
closeModal={() => closeModal={() =>
setManageDoc({ setManageDoc({
document: null, document: null,
isOpen: false, isOpen: false,
}) })
} }
> Document_Entity={DocumentEntity}
<ManageDocument Entity={Entity}
closeModal={() => />
setManageDoc({ </GlobalModel>
document: null, )}
isOpen: false,
})
}
Document_Entity={DocumentEntity}
Entity={Entity}
/>
</GlobalModel>
)}
{viewDoc.isOpen && ( {viewDoc.isOpen && (
<GlobalModel <GlobalModel size="lg" isOpen={viewDoc.isOpen} closeModal={()=>setViewDoc({
size="lg" document:null,
isOpen={viewDoc.isOpen} isOpen:false
closeModal={() => })}>
setViewDoc({ <ViewDocument />
document: null, </GlobalModel>
isOpen: false, )}
})
}
>
<ViewDocument />
</GlobalModel>
)}
{OpenDocument && ( {OpenDocument && (
<GlobalModel <GlobalModel isOpen={OpenDocument} closeModal={()=>setOpenDocument(false)}>
isOpen={OpenDocument} <DocumentViewerModal/>
closeModal={() => setOpenDocument(false)} </GlobalModel>
> )}
<DocumentViewerModal />
</GlobalModel> </div>
)}
</div>
</DocumentContext.Provider> </DocumentContext.Provider>
); );
}; };

View File

@ -6,12 +6,12 @@ import {
import { ITEMS_PER_PAGE } from "../../utils/constants"; import { ITEMS_PER_PAGE } from "../../utils/constants";
import Avatar from "../common/Avatar"; import Avatar from "../common/Avatar";
import { formatUTCToLocalTime } from "../../utils/dateUtils"; import { formatUTCToLocalTime } from "../../utils/dateUtils";
import Loader from "../common/Loader";
import { useDebounce } from "../../utils/appUtils"; import { useDebounce } from "../../utils/appUtils";
import { DocumentTableSkeleton } from "./DocumentSkeleton"; import { DocumentTableSkeleton } from "./DocumentSkeleton";
import { getDocuementsStatus, useDocumentContext } from "./Documents"; import { getDocuementsStatus, useDocumentContext } from "./Documents";
import Pagination from "../common/Pagination"; import Pagination from "../common/Pagination";
import ConfirmModal from "../common/ConfirmModal"; import ConfirmModal from "../common/ConfirmModal";
import { isPending } from "@reduxjs/toolkit";
const DocumentsList = ({ const DocumentsList = ({
Document_Entity, Document_Entity,
@ -20,14 +20,11 @@ const DocumentsList = ({
searchText, searchText,
setIsRefetching, setIsRefetching,
setRefetchFn, setRefetchFn,
isActive,
}) => { }) => {
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deletingId, setDeletingId] = useState(null); const [deletingId, setDeletingId] = useState(null);
const [restoringIds, setRestoringIds] = useState([]);
const debouncedSearch = useDebounce(searchText, 500); const debouncedSearch = useDebounce(searchText, 500);
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const { data, isError, isLoading, error, refetch, isFetching } = const { data, isError, isLoading, error, refetch, isFetching } =
useDocumentListByEntityId( useDocumentListByEntityId(
Document_Entity, Document_Entity,
@ -35,21 +32,21 @@ const DocumentsList = ({
ITEMS_PER_PAGE, ITEMS_PER_PAGE,
currentPage, currentPage,
filters, filters,
debouncedSearch, debouncedSearch
isActive
); );
// Pass the refetch function to parent when component mounts
useEffect(() => { useEffect(() => {
setRefetchFn(() => refetch); setRefetchFn(() => refetch);
}, [setRefetchFn, refetch]); }, [setRefetchFn, refetch]);
// Sync fetching status with parent
useEffect(() => { useEffect(() => {
setIsRefetching(isFetching); setIsRefetching(isFetching);
}, [isFetching, setIsRefetching]); }, [isFetching, setIsRefetching]);
const { setManageDoc, setViewDoc } = useDocumentContext(); const { setManageDoc, setViewDoc } = useDocumentContext();
const { mutate: ActiveInActive, isPending } = useActiveInActiveDocument(); const { mutate: ActiveInActive, isPending } = useActiveInActiveDocument();
const paginate = (page) => { const paginate = (page) => {
if (page >= 1 && page <= (data?.totalPages ?? 1)) { if (page >= 1 && page <= (data?.totalPages ?? 1)) {
setCurrentPage(page); setCurrentPage(page);
@ -69,8 +66,9 @@ const DocumentsList = ({
if (isFilterEmpty) return <div>No documents match your filter.</div>; if (isFilterEmpty) return <div>No documents match your filter.</div>;
const handleDelete = () => { const handleDelete = () => {
debugger;
ActiveInActive( ActiveInActive(
{ documentId: deletingId, isActive: !isActive }, { documentId: deletingId, isActive: false },
{ {
onSettled: () => { onSettled: () => {
setDeletingId(null); setDeletingId(null);
@ -79,21 +77,6 @@ const DocumentsList = ({
} }
); );
}; };
const handleRestore = (docId) => {
setRestoringIds((prev) => [...prev, docId]);
ActiveInActive(
{ documentId: docId, isActive: true },
{
onSettled: () => {
setRestoringIds((prev) => prev.filter((id) => id !== docId));
refetch();
},
}
);
};
const DocumentColumns = [ const DocumentColumns = [
{ {
key: "name", key: "name",
@ -111,6 +94,10 @@ const DocumentsList = ({
key: "uploadedBy", key: "uploadedBy",
label: "Uploaded By", label: "Uploaded By",
align: "text-start", align: "text-start",
getValue: (e) =>
`${e.uploadedBy?.firstName ?? ""} ${
e.uploadedBy?.lastName ?? ""
}`.trim() || "N/A",
customRender: (e) => ( customRender: (e) => (
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
<Avatar <Avatar
@ -119,31 +106,27 @@ const DocumentsList = ({
firstName={e.uploadedBy?.firstName} firstName={e.uploadedBy?.firstName}
lastName={e.uploadedBy?.lastName} lastName={e.uploadedBy?.lastName}
/> />
<span className="text-truncate ms-1"> <span className="text-truncate ">
{`${e.uploadedBy?.firstName ?? ""} ${ {`${e.uploadedBy?.firstName ?? ""} ${
e.uploadedBy?.lastName ?? "" e.uploadedBy?.lastName ?? ""
}`.trim() || "N/A"} }`.trim() || "N/A"}
</span> </span>
</div> </div>
), ),
getValue: (e) =>
`${e.uploadedBy?.firstName ?? ""} ${
e.uploadedBy?.lastName ?? ""
}`.trim() || "N/A",
}, },
{ {
key: "uploadedAt", key: "uploadedAt",
label: "Uploaded on", label: "Uploaded on",
getValue: (e) => formatUTCToLocalTime(e.uploadedAt), getValue: (e) => formatUTCToLocalTime(e?.uploadedAt),
align: "text-center",
isAlwaysVisible: true, isAlwaysVisible: true,
align: "text-center",
}, },
{ {
key: "Status", key: "Status",
label: "Status", label: "status",
getValue: (e) => getDocuementsStatus(e.isVerified), getValue: (e) => getDocuementsStatus(e.isVerified),
align: "text-center",
isAlwaysVisible: true, isAlwaysVisible: true,
align: "text-center",
}, },
]; ];
@ -151,10 +134,13 @@ const DocumentsList = ({
<> <>
{IsDeleteModalOpen && ( {IsDeleteModalOpen && (
<div <div
className="modal fade show" className={`modal fade show`}
tabIndex="-1" tabIndex="-1"
role="dialog" role="dialog"
style={{ display: "block", backgroundColor: "rgba(0,0,0,0.5)" }} style={{
display: "block",
backgroundColor: "rgba(0,0,0,0.5)",
}}
aria-hidden="false" aria-hidden="false"
> >
<ConfirmModal <ConfirmModal
@ -163,7 +149,7 @@ const DocumentsList = ({
message="Are you sure you want delete?" message="Are you sure you want delete?"
onSubmit={handleDelete} onSubmit={handleDelete}
onClose={() => setIsDeleteModalOpen(false)} onClose={() => setIsDeleteModalOpen(false)}
loading={!!isPending} loading={isPending}
paramData={deletingId} paramData={deletingId}
/> />
</div> </div>
@ -184,66 +170,48 @@ const DocumentsList = ({
</tr> </tr>
</thead> </thead>
<tbody className="text-start"> <tbody className="text-start">
{data?.data?.map((doc) => { {data?.data?.map((doc) => (
const isRestoring = restoringIds.includes(doc.id); <tr key={doc.id}>
{DocumentColumns.map((col) => (
return ( <td key={col.key} className={`sorting ${col.align}`}>
<tr key={doc.id}> {col.customRender
{DocumentColumns.map((col) => ( ? col.customRender(doc)
<td key={col.key} className={`sorting ${col.align}`}> : col.getValue(doc)}
{col.customRender
? col.customRender(doc)
: col.getValue(doc)}
</td>
))}
<td className="text-center">
{doc.isActive ? (
<div className="d-flex justify-content-center gap-2">
<i
className="bx bx-show text-primary cursor-pointer"
onClick={() =>
setViewDoc({ document: doc.id, isOpen: true })
}
></i>
<i
className="bx bx-edit text-secondary cursor-pointer"
onClick={() =>
setManageDoc({ document: doc.id, isOpen: true })
}
></i>
<i
className="bx bx-trash text-danger cursor-pointer"
onClick={() => {
setIsDeleteModalOpen(true);
setDeletingId(doc.id);
}}
></i>
</div>
) : isRestoring ? (
<div
className="spinner-border spinner-border-sm text-primary"
role="status"
>
<span className="visually-hidden">Loading...</span>
</div>
) : (
<i
className="bx bx-recycle me-1 text-primary cursor-pointer"
onClick={() => handleRestore(doc.id)}
></i>
)}
</td> </td>
</tr> ))}
); <td className="text-center">
})} <div className="d-flex justify-content-center gap-2">
<i
className="bx bx-show text-primary cursor-pointer"
onClick={() =>
setViewDoc({ document: doc?.id, isOpen: true })
}
></i>
<i
className="bx bx-edit text-secondary cursor-pointer"
onClick={() =>
setManageDoc({ document: doc?.id, isOpen: true })
}
></i>
<i
className="bx bx-trash text-danger cursor-pointer"
onClick={() => {
setIsDeleteModalOpen(true);
setDeletingId(doc?.id);
}}
></i>
</div>
</td>
</tr>
))}
</tbody> </tbody>
</table> </table>
{data?.data?.length > 0 && ( {data?.data?.length > 0 && (
<Pagination <Pagination
currentPage={currentPage} currentPage={currentPage}
totalPages={data.totalPages} totalPages={data?.totalPages}
onPageChange={paginate} onPageChange={paginate}
/> />
)} )}

View File

@ -87,34 +87,20 @@ const ManageDocument = ({ closeModal, Document_Entity, Entity }) => {
} }
); );
const onSubmit = (data) => { const onSubmit = (data) => {
const normalizeAttachment = (attachment) => { if (ManageDoc?.document) {
if (!attachment) return null; const DocumentPayload = {
return { ...data,
...attachment, id: DocData.id,
fileSize: Math.ceil(attachment.fileSize / 1024), tags: MergedTagsWithExistenStatus(data?.tags, DocData?.tags),
}; };
UpdateDocument({ documentId: DocData?.id, DocumentPayload });
} else {
const DocumentPayload = { ...data, entityId: Entity };
UploadDocument(DocumentPayload);
}
}; };
const payload = {
...data,
attachment: normalizeAttachment(data.attachment),
};
if (ManageDoc?.document) {
const DocumentPayload = {
...payload,
id: DocData.id,
tags: MergedTagsWithExistenStatus(data?.tags, DocData?.tags),
};
UpdateDocument({ documentId: DocData?.id, DocumentPayload });
} else {
const DocumentPayload = { ...payload, entityId: Entity };
UploadDocument(DocumentPayload);
}
};
const { const {
data: DocData, data: DocData,
isLoading: isDocLoading, isLoading: isDocLoading,

View File

@ -31,8 +31,7 @@ export const useDocumentListByEntityId = (
pageSize, pageSize,
pageNumber, pageNumber,
filter, filter,
searchString = "", searchString = ""
isActive
) => { ) => {
return useQuery({ return useQuery({
queryKey: [ queryKey: [
@ -43,7 +42,6 @@ export const useDocumentListByEntityId = (
pageNumber, pageNumber,
filter, filter,
searchString, searchString,
isActive
], ],
queryFn: async () => { queryFn: async () => {
const cleanedFilter = cleanFilter(filter); const cleanedFilter = cleanFilter(filter);
@ -53,8 +51,7 @@ export const useDocumentListByEntityId = (
pageSize, pageSize,
pageNumber, pageNumber,
cleanedFilter, cleanedFilter,
searchString, searchString
isActive
); );
return resp.data return resp.data

View File

@ -2,9 +2,9 @@ import { api } from "../utils/axiosClient";
export const DocumentRepository = { export const DocumentRepository = {
uploadDocument:(data)=> api.post(`/api/Document/upload`,data), uploadDocument:(data)=> api.post(`/api/Document/upload`,data),
getDocumentList:(entityTypeId,entityId,pageSize, pageNumber, filter,searchString,isActive)=>{ getDocumentList:(entityTypeId,entityId,pageSize, pageNumber, filter,searchString)=>{
const payloadJsonString = JSON.stringify(filter); const payloadJsonString = JSON.stringify(filter);
return api.get(`/api/Document/list/${entityTypeId}/entity/${entityId}/?pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}&isActive=${isActive}`) return api.get(`/api/Document/list/${entityTypeId}/entity/${entityId}/?pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`)
}, },
getDocumentById:(id)=>api.get(`/api/Document/get/details/${id}`), getDocumentById:(id)=>api.get(`/api/Document/get/details/${id}`),