diff --git a/src/components/Documents/DocumentSchema.js b/src/components/Documents/DocumentSchema.js index adb82152..5dc0117d 100644 --- a/src/components/Documents/DocumentSchema.js +++ b/src/components/Documents/DocumentSchema.js @@ -34,13 +34,13 @@ export const TagSchema = z.object({ isActive: z.boolean().default(true), }); - export const DocumentPayloadSchema = (docConfig = {}) => { const { isMandatory, regexExpression, allowedContentType, maxSizeAllowedInMB, + isUpdateForm, } = docConfig; let documentIdSchema = z.string(); @@ -58,20 +58,31 @@ export const DocumentPayloadSchema = (docConfig = {}) => { ); } + // Base attachment schema + let attachmentSchema = AttachmentSchema( + allowedContentType, + maxSizeAllowedInMB + ).nullable(); + + // If not update form, require attachment + if (!isUpdateForm) { + attachmentSchema = attachmentSchema.refine((val) => val !== null, { + message: "Attachment is required", + }); + } + return z.object({ name: z.string().min(1, "Name is required"), documentId: documentIdSchema, description: z.string().min(1, { message: "Description is required" }), - // entityId: z.string().min(1, { message: "Please Select Document Entity" }), - documentTypeId: z.string().min(1, { message: "Please Select Document Type" }), + documentTypeId: z + .string() + .min(1, { message: "Please Select Document Type" }), documentCategoryId: z .string() .min(1, { message: "Please Select Document Category" }), - attachment: AttachmentSchema(allowedContentType, maxSizeAllowedInMB).nullable().refine( - (val) => val !== null, - { message: "Attachment is required" } -), - tags: z.array(TagSchema).optional().default([]), + attachment: attachmentSchema, + tags: z.array(TagSchema).optional().default([]), }); }; @@ -83,14 +94,15 @@ export const defaultDocumentValues = { // entityId: "", documentTypeId: "", documentCategoryId: "", - attachment: { - fileName: "", - base64Data: "", - contentType: "", - fileSize: 0, - description: "", - isActive: true, - }, + // attachment: { + // fileName: "", + // base64Data: "", + // contentType: "", + // fileSize: 0, + // description: "", + // isActive: true, + // }, + attachment:null, tags: [], }; diff --git a/src/components/Documents/Documents.jsx b/src/components/Documents/Documents.jsx index 5099cc1d..bd74ca16 100644 --- a/src/components/Documents/Documents.jsx +++ b/src/components/Documents/Documents.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { createContext, useContext, useEffect, useState } from "react"; import GlobalModel from "../common/GlobalModel"; import NewDocument from "./ManageDocument"; import { DOCUMENTS_ENTITIES } from "../../utils/constants"; @@ -14,13 +14,28 @@ import { import { zodResolver } from "@hookform/resolvers/zod"; import ManageDocument from "./ManageDocument"; +// Context +export const DocumentContext = createContext(); +export const useDocumentContext = () => { + const context = useContext(DocumentContext); + if (!context) { + throw new Error("useExpenseContext must be used within an ExpenseProvider"); + } + return context; +}; + const Documents = ({ Document_Entity, Entity }) => { const [searchText, setSearchText] = useState(""); const [filters, setFilter] = useState(); const [isRefetching, setIsRefetching] = useState(false); const [refetchFn, setRefetchFn] = useState(null); const { employeeId } = useParams(); - const [isUpload, setUpload] = useState(false); + const [ManageDoc, setManageDoc] = useState({ + document: null, + isOpen: false, + }); + + const { setOffcanvasContent, setShowTrigger } = useFab(); const methods = useForm({ @@ -47,9 +62,16 @@ const Documents = ({ Document_Entity, Entity }) => { setOffcanvasContent("", null); }; }, []); - + + const contextValues = { + ManageDoc, + setManageDoc, + } + return ( -
+ + +
{/* Search */} @@ -86,7 +108,10 @@ const Documents = ({ Document_Entity, Entity }) => { type="button" title="Add New Document" className="p-1 bg-primary rounded-circle cursor-pointer" - onClick={() => setUpload(true)} + onClick={() => setManageDoc({ + document: null, + isOpen: true, + })} > @@ -102,16 +127,31 @@ const Documents = ({ Document_Entity, Entity }) => { />
- {isUpload && ( - setUpload(false)}> + {ManageDoc.isOpen && ( + + setManageDoc({ + document: null, + isOpen: false, + }) + } + > setUpload(false)} + closeModal={() => + setManageDoc({ + document: null, + isOpen: false, + }) + } Document_Entity={Document_Entity} Entity={Entity} /> )}
+ + ); }; diff --git a/src/components/Documents/DocumentsList.jsx b/src/components/Documents/DocumentsList.jsx index 23dd6504..69e7d0d1 100644 --- a/src/components/Documents/DocumentsList.jsx +++ b/src/components/Documents/DocumentsList.jsx @@ -6,6 +6,7 @@ import { formatUTCToLocalTime } from "../../utils/dateUtils"; import Loader from "../common/Loader"; import { useDebounce } from "../../utils/appUtils"; import { DocumentTableSkeleton } from "./DocumentSkeleton"; +import { useDocumentContext } from "./Documents"; export const getDocuementsStatus = (status) => { switch (status) { @@ -53,6 +54,8 @@ const DocumentsList = ({ setIsRefetching(isFetching); }, [isFetching, setIsRefetching]); + const {setManageDoc} = useDocumentContext() + // check no data scenarios const noData = !isLoading && !isError && data?.length === 0; const isSearchEmpty = noData && !!debouncedSearch; @@ -146,7 +149,7 @@ const DocumentsList = ({
- + setManageDoc({document:doc?.id,isOpen:true})}>
diff --git a/src/components/Documents/ManageDocument.jsx b/src/components/Documents/ManageDocument.jsx index e712c0b3..a285c55e 100644 --- a/src/components/Documents/ManageDocument.jsx +++ b/src/components/Documents/ManageDocument.jsx @@ -8,8 +8,13 @@ import { useDocumentTypes, } from "../../hooks/masterHook/useMaster"; import TagInput from "../common/TagInput"; -import { useUploadDocument } from "../../hooks/useDocument"; +import { + useDocumentDetails, + useUpdateDocument, + useUploadDocument, +} from "../../hooks/useDocument"; import showToast from "../../services/toastService"; +import { useDocumentContext } from "./Documents"; const toBase64 = (file) => new Promise((resolve, reject) => { @@ -19,11 +24,14 @@ const toBase64 = (file) => reader.onerror = (err) => reject(err); }); -const ManageDocument = ({closeModal,Document_Entity,Entity}) => { - +const ManageDocument = ({ closeModal, Document_Entity, Entity }) => { + const { ManageDoc } = useDocumentContext(); + const isUpdateForm = Boolean(ManageDoc?.document); const [selectedType, setSelectedType] = useState(null); const [selectedCategory, setSelectedCategory] = useState(null); - const [schema, setSchema] = useState(() => DocumentPayloadSchema({})); + const [schema, setSchema] = useState(() => + DocumentPayloadSchema({ isUpdateForm }) + ); const methods = useForm({ resolver: zodResolver(schema), defaultValues: defaultDocumentValues, @@ -41,19 +49,35 @@ const ManageDocument = ({closeModal,Document_Entity,Entity}) => { showToast("Document Uploaded Successfully", "success"); closeModal(); }); + const { mutate: UpdateDocument, isPending: isUpdatinDoc } = useUpdateDocument( + () => { + showToast("Document Updated Successfully", "success"); + closeModal(); + } + ); const onSubmit = (data) => { - const DocumentPayload = { ...data, entityId: Entity }; - UploadDocument(DocumentPayload); + if (ManageDoc?.document) { + const DocumentPayload = { ...DocData, ...data }; + UpdateDocument({ documentId: DocData?.id, DocumentPayload }); + } else { + const DocumentPayload = { ...data, entityId: Entity }; + UploadDocument(DocumentPayload); + } }; + const { + data: DocData, + isLoading: isDocLoading, + isError: isDocError, + DocError, + } = useDocumentDetails(ManageDoc?.document); const file = watch("attachment"); const documentTypeId = watch("documentTypeId"); // This hooks calling api base Entity(Employee) and Category - const { DocumentCategories, isLoading } = useDocumentCategories( - Document_Entity - ); + const { DocumentCategories, isLoading } = + useDocumentCategories(Document_Entity); const categoryId = watch("documentCategoryId"); const { DocumentTypes, isLoading: isTypeLoading } = useDocumentTypes( @@ -63,26 +87,29 @@ const ManageDocument = ({closeModal,Document_Entity,Entity}) => { // Update schema whenever document type changes useEffect(() => { if (!documentTypeId) return; - const type = DocumentTypes?.find((t) => t.id === documentTypeId); - setSelectedType(type || null); - if (type) { - const newSchema = DocumentPayloadSchema({ - isMandatory: type.isMandatory ?? false, - regexExpression: type.regexExpression ?? null, - allowedContentType: type.allowedContentType ?? [ - "application/pdf", - "image/jpeg", - "image/png", - ], - maxSizeAllowedInMB: type.maxSizeAllowedInMB ?? 25, - }); + const type = DocumentTypes?.find( + (t) => String(t.id) === String(documentTypeId) + ); + if (!type) return; - setSchema(() => newSchema); + const newSchema = DocumentPayloadSchema({ + isMandatory: type.isMandatory ?? false, + regexExpression: type.regexExpression ?? null, + allowedContentType: type.allowedContentType ?? [ + "application/pdf", + "image/jpeg", + "image/png", + ], + maxSizeAllowedInMB: type.maxSizeAllowedInMB ?? 25, + isUpdateForm, + }); - reset({ ...methods.getValues() }, { keepValues: true }); - } - }, [documentTypeId, DocumentTypes, reset]); + setSchema(() => newSchema); + + methods.reset(methods.getValues(), { keepValues: true }); + methods.formState.errors; // triggers revalidation + }, [documentTypeId, DocumentTypes, isUpdateForm]); // File Upload const onFileChange = async (e) => { @@ -127,11 +154,33 @@ const ManageDocument = ({closeModal,Document_Entity,Entity}) => { : "" ) .join(",") || ""; + + useEffect(() => { + if (DocData) { + reset({ + ...defaultDocumentValues, + name: DocData?.name ?? "", + documentCategoryId: DocData?.documentType?.documentCategory?.id + ? String(DocData.documentType.documentCategory.id) + : "", + documentTypeId: DocData?.documentType?.id + ? String(DocData.documentType.id) + : "", + documentId: DocData?.documentId ?? "", + description: DocData?.description ?? "", + attachment: DocData?.attachment ?? null, + tags: DocData?.tags ?? [], + }); + } + }, [DocData, reset]); + + if (isDocLoading) return
Loading...
; + if (isDocError) return
{DocError.message}
; return (

Upload New Document

-
+ {/* Document Name */}
{/* Type */} - {categoryId && (
- - + {isTypeLoading && ( + + )} + {DocumentTypes?.map((type) => ( + + ))} + + {errors.documentTypeId && ( +
+ {errors.documentTypeId.message} +
)} - {DocumentTypes?.map((type) => ( - - ))} - - {errors.documentTypeId && ( -
{errors.documentTypeId.message}
- )} -
)} - +
+ )} {/* Document ID */}
@@ -308,7 +360,7 @@ const ManageDocument = ({closeModal,Document_Entity,Entity}) => { type="reset" className="btn btn-secondary btn-sm" disabled={isPending} - onClick={()=>closeModal()} + onClick={closeModal} > Cancel diff --git a/src/hooks/useDocument.js b/src/hooks/useDocument.js index c4e10b3d..f76c52de 100644 --- a/src/hooks/useDocument.js +++ b/src/hooks/useDocument.js @@ -42,8 +42,13 @@ export const useDocumentFilterEntities =(entityTypeId)=>{ export const useDocumentDetails =(documentId)=>{ return useQuery({ queryKey:["Document",documentId], - queryFn:async()=> await DocumentRepository.getDocumentById(documentId) + queryFn:async()=> { + const resp = await DocumentRepository.getDocumentById(documentId); + return resp.data; + }, + enabled:!!documentId }) + } //----------------------- MUTATION ------------------------- @@ -68,7 +73,7 @@ export const useUploadDocument =(onSuccessCallBack)=>{ export const useUpdateDocument =(onSuccessCallBack)=>{ const queryClient = useQueryClient() return useMutation(({ - mutationFn:async(documentId,DocumentPayload)=>DocumentRepository.UpdateDocument(documentId,DocumentPayload), + mutationFn:async({documentId,DocumentPayload})=>DocumentRepository.UpdateDocument(documentId,DocumentPayload), onSuccess:(data,variables)=>{ queryClient.invalidateQueries({queryKey:["DocumentList"]}); if(onSuccessCallBack) onSuccessCallBack() diff --git a/src/repositories/DocumentRepository.jsx b/src/repositories/DocumentRepository.jsx index ffc93461..be75b9eb 100644 --- a/src/repositories/DocumentRepository.jsx +++ b/src/repositories/DocumentRepository.jsx @@ -6,10 +6,9 @@ export const DocumentRepository = { const payloadJsonString = JSON.stringify(filter); return api.get(`/api/Document/list/${entityTypeId}/entity/${entityId}/?pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`) }, - getDocumentById:(id)=>api.get(`/api/Document/${id}`), + getDocumentById:(id)=>api.get(`/api/Document/get/details/${id}`), getFilterEntities:(entityTypeId)=>api.get(`/api/Document/get/filter/${entityTypeId}`), - UpdateDocument:(documentId,data)=>api.get(`/api/Expense/edit/${documentId}`,data) - -} + UpdateDocument:(documentId,data)=>api.put(`/api/Document/edit/${documentId}`,data) +} \ No newline at end of file