enable activating or deactivating existing documents

This commit is contained in:
pramod mahajan 2025-09-03 11:18:12 +05:30
parent e8459d3671
commit fd14328562
4 changed files with 202 additions and 142 deletions

View File

@ -21,12 +21,13 @@ export const DocumentContext = createContext();
export const useDocumentContext = () => {
const context = useContext(DocumentContext);
if (!context) {
throw new Error("useDocumentContext must be used within an DocumentProvider");
throw new Error(
"useDocumentContext must be used within an DocumentProvider"
);
}
return context;
};
export const getDocuementsStatus = (status) => {
switch (status) {
case true:
@ -46,22 +47,22 @@ export const getDocuementsStatus = (status) => {
};
const Documents = ({ Document_Entity, Entity }) => {
const [searchText, setSearchText] = useState("");
const [isActive, setIsActive] = useState(true);
const [filters, setFilter] = useState();
const [isRefetching, setIsRefetching] = useState(false);
const [refetchFn, setRefetchFn] = useState(null);
const [DocumentEntity,setDocumentEntity] = useState(Document_Entity)
const [DocumentEntity, setDocumentEntity] = useState(Document_Entity);
const { employeeId } = useParams();
const [OpenDocument,setOpenDocument] = useState(false)
const [OpenDocument, setOpenDocument] = useState(false);
const [ManageDoc, setManageDoc] = useState({
document: null,
isOpen: false,
});
const [viewDoc, setViewDoc] = useState({
const [viewDoc, setViewDoc] = useState({
document: null,
isOpen: false,
});
const { setOffcanvasContent, setShowTrigger } = useFab();
const methods = useForm({
@ -95,34 +96,33 @@ const Documents = ({ Document_Entity, Entity }) => {
viewDoc,
setViewDoc,
setOpenDocument,
OpenDocument
}
OpenDocument,
};
useEffect(()=>{
if(Document_Entity){
setDocumentEntity(Document_Entity)
useEffect(() => {
if (Document_Entity) {
setDocumentEntity(Document_Entity);
}
},[Document_Entity])
}, [Document_Entity]);
return (
<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>
<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="col-6 col-md-6 col-lg-9 text-end">
{/* <span
{/* 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={() => {
@ -138,71 +138,96 @@ const Documents = ({ Document_Entity, Entity }) => {
}`}
></i>
</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
type="button"
title="Add New Document"
className="p-1 bg-primary rounded-circle cursor-pointer"
onClick={() => setManageDoc({
document: null,
isOpen: true,
})}
>
<i className="bx bx-plus fs-4 text-white"></i>
</button>
<button
type="button"
title="Add New Document"
className="p-1 bg-primary rounded-circle cursor-pointer"
onClick={() =>
setManageDoc({
document: null,
isOpen: true,
})
}
>
<i className="bx bx-plus fs-4 text-white"></i>
</button>
</div>
</div>
<DocumentsList
Document_Entity={DocumentEntity}
Entity={Entity}
filters={filters}
searchText={searchText}
setIsRefetching={setIsRefetching}
setRefetchFn={setRefetchFn}
isActive={isActive}
/>
</div>
<DocumentsList
Document_Entity={DocumentEntity}
Entity={Entity}
filters={filters}
searchText={searchText}
setIsRefetching={setIsRefetching}
setRefetchFn={setRefetchFn}
/>
</div>
{ManageDoc.isOpen && (
<GlobalModel
isOpen={ManageDoc.isOpen}
closeModal={() =>
setManageDoc({
document: null,
isOpen: false,
})
}
>
<ManageDocument
{ManageDoc.isOpen && (
<GlobalModel
isOpen={ManageDoc.isOpen}
closeModal={() =>
setManageDoc({
document: null,
isOpen: false,
})
}
Document_Entity={DocumentEntity}
Entity={Entity}
/>
</GlobalModel>
)}
>
<ManageDocument
closeModal={() =>
setManageDoc({
document: null,
isOpen: false,
})
}
Document_Entity={DocumentEntity}
Entity={Entity}
/>
</GlobalModel>
)}
{viewDoc.isOpen && (
<GlobalModel size="lg" isOpen={viewDoc.isOpen} closeModal={()=>setViewDoc({
document:null,
isOpen:false
})}>
<ViewDocument />
</GlobalModel>
)}
{viewDoc.isOpen && (
<GlobalModel
size="lg"
isOpen={viewDoc.isOpen}
closeModal={() =>
setViewDoc({
document: null,
isOpen: false,
})
}
>
<ViewDocument />
</GlobalModel>
)}
{OpenDocument && (
<GlobalModel isOpen={OpenDocument} closeModal={()=>setOpenDocument(false)}>
<DocumentViewerModal/>
</GlobalModel>
)}
</div>
{OpenDocument && (
<GlobalModel
isOpen={OpenDocument}
closeModal={() => setOpenDocument(false)}
>
<DocumentViewerModal />
</GlobalModel>
)}
</div>
</DocumentContext.Provider>
);
};

View File

@ -6,12 +6,12 @@ import {
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";
import { DocumentTableSkeleton } from "./DocumentSkeleton";
import { getDocuementsStatus, useDocumentContext } from "./Documents";
import Pagination from "../common/Pagination";
import ConfirmModal from "../common/ConfirmModal";
import { isPending } from "@reduxjs/toolkit";
const DocumentsList = ({
Document_Entity,
@ -20,11 +20,14 @@ const DocumentsList = ({
searchText,
setIsRefetching,
setRefetchFn,
isActive,
}) => {
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deletingId, setDeletingId] = useState(null);
const [restoringIds, setRestoringIds] = useState([]);
const debouncedSearch = useDebounce(searchText, 500);
const [currentPage, setCurrentPage] = useState(1);
const { data, isError, isLoading, error, refetch, isFetching } =
useDocumentListByEntityId(
Document_Entity,
@ -32,21 +35,21 @@ const DocumentsList = ({
ITEMS_PER_PAGE,
currentPage,
filters,
debouncedSearch
debouncedSearch,
isActive
);
// Pass the refetch function to parent when component mounts
useEffect(() => {
setRefetchFn(() => refetch);
}, [setRefetchFn, refetch]);
// Sync fetching status with parent
useEffect(() => {
setIsRefetching(isFetching);
}, [isFetching, setIsRefetching]);
const { setManageDoc, setViewDoc } = useDocumentContext();
const { mutate: ActiveInActive, isPending } = useActiveInActiveDocument();
const paginate = (page) => {
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
setCurrentPage(page);
@ -66,9 +69,8 @@ const DocumentsList = ({
if (isFilterEmpty) return <div>No documents match your filter.</div>;
const handleDelete = () => {
debugger;
ActiveInActive(
{ documentId: deletingId, isActive: false },
{ documentId: deletingId, isActive: !isActive },
{
onSettled: () => {
setDeletingId(null);
@ -77,6 +79,21 @@ 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 = [
{
key: "name",
@ -94,10 +111,6 @@ const DocumentsList = ({
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
@ -106,27 +119,31 @@ const DocumentsList = ({
firstName={e.uploadedBy?.firstName}
lastName={e.uploadedBy?.lastName}
/>
<span className="text-truncate ">
<span className="text-truncate ms-1">
{`${e.uploadedBy?.firstName ?? ""} ${
e.uploadedBy?.lastName ?? ""
}`.trim() || "N/A"}
</span>
</div>
),
getValue: (e) =>
`${e.uploadedBy?.firstName ?? ""} ${
e.uploadedBy?.lastName ?? ""
}`.trim() || "N/A",
},
{
key: "uploadedAt",
label: "Uploaded on",
getValue: (e) => formatUTCToLocalTime(e?.uploadedAt),
isAlwaysVisible: true,
getValue: (e) => formatUTCToLocalTime(e.uploadedAt),
align: "text-center",
isAlwaysVisible: true,
},
{
key: "Status",
label: "status",
label: "Status",
getValue: (e) => getDocuementsStatus(e.isVerified),
isAlwaysVisible: true,
align: "text-center",
isAlwaysVisible: true,
},
];
@ -134,13 +151,10 @@ const DocumentsList = ({
<>
{IsDeleteModalOpen && (
<div
className={`modal fade show`}
className="modal fade show"
tabIndex="-1"
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"
>
<ConfirmModal
@ -149,7 +163,7 @@ const DocumentsList = ({
message="Are you sure you want delete?"
onSubmit={handleDelete}
onClose={() => setIsDeleteModalOpen(false)}
loading={isPending}
loading={!!isPending}
paramData={deletingId}
/>
</div>
@ -170,48 +184,66 @@ const DocumentsList = ({
</tr>
</thead>
<tbody className="text-start">
{data?.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)}
{data?.data?.map((doc) => {
const isRestoring = restoringIds.includes(doc.id);
return (
<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">
{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 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>
))}
</tr>
);
})}
</tbody>
</table>
{data?.data?.length > 0 && (
<Pagination
currentPage={currentPage}
totalPages={data?.totalPages}
totalPages={data.totalPages}
onPageChange={paginate}
/>
)}

View File

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

View File

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