From a52cf0bc39088888f28b41e2277958a517e27913 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Wed, 3 Sep 2025 15:16:37 +0530 Subject: [PATCH] allow to delete document category and type and edit type --- src/components/common/ConfirmModal.jsx | 87 ++++---- src/components/master/ManageDocumentType.jsx | 223 +++++++++++++++++++ src/components/master/MasterModal.jsx | 133 ++++------- src/hooks/masterHook/useMaster.js | 47 ++++ src/pages/master/MasterPage.jsx | 129 ++++++++--- src/pages/master/MasterTable.jsx | 12 +- src/repositories/MastersRepository.jsx | 3 + 7 files changed, 459 insertions(+), 175 deletions(-) create mode 100644 src/components/master/ManageDocumentType.jsx diff --git a/src/components/common/ConfirmModal.jsx b/src/components/common/ConfirmModal.jsx index cadee1bd..c6f9cf7a 100644 --- a/src/components/common/ConfirmModal.jsx +++ b/src/components/common/ConfirmModal.jsx @@ -1,70 +1,71 @@ -import React, { useState } from 'react'; +import React from "react"; -const ConfirmModal = ({ type, onSubmit, onClose, message, loading ,header, paramData}) => { - - const TypeofIcon = (type) => { - switch (type) { - case "delete": - return ; - default: - return null; +const ConfirmModal = ({ + type, + onSubmit, + onClose, + message, + loading, + header, + paramData, + isOpen = false, +}) => { + if (!isOpen) return null; + + const TypeofIcon = () => { + if (type === "delete") { + return ( + + ); } + return null; }; - const TypeofModal = (type) => { - switch (type) { - case "delete": - return "sm"; - case "other": - return "md"; - default: - return "sm"; - } - }; + const modalSize = type === "delete" ? "sm" : "md"; return ( -
-
+
+
-
- -
- -
- {header && < strong className='mb-0 font-weight-bold'>{header }} +
+ {header && {header}}
- -
-
{TypeofIcon(type)}
-
- {message} -
+
+ +
+
{TypeofIcon()}
+
+ {message} +
-
-
- -
diff --git a/src/components/master/ManageDocumentType.jsx b/src/components/master/ManageDocumentType.jsx new file mode 100644 index 00000000..51ea943e --- /dev/null +++ b/src/components/master/ManageDocumentType.jsx @@ -0,0 +1,223 @@ +import React, { useEffect } from "react"; +import { useForm, FormProvider } from "react-hook-form"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useCreateDocumentType, useDocumentCategories, useUpdateDocumentType } from "../../hooks/masterHook/useMaster"; +import { DOCUMENTS_ENTITIES } from "../../utils/constants"; + + +export const Document_Entity = Object.entries(DOCUMENTS_ENTITIES).map( + ([key, value]) => ({ key, value }) +); +const DocumentTypeSchema = z.object({ + name: z.string().min(1, { message: "Name is required" }), + regexExpression: z.string().optional(), + allowedContentType: z.string().min(1, { message: "Allowed content type is required" }), + maxSizeAllowedInMB: z + .number({ invalid_type_error: "Must be a number" }) + .min(1, { message: "Max size must be at least 1MB" }), + isValidationRequired: z.boolean().default(true), + isMandatory: z.boolean().default(true), + documentCategoryId: z.string().min(1, { message: "Document Category is required" }), +}); + +const ManageDocumentType = ({ data, onClose, documentCategories = [] }) => { + const methods = useForm({ + resolver: zodResolver(DocumentTypeSchema), + defaultValues: { + name: "", + regexExpression: "", + allowedContentType: "", + maxSizeAllowedInMB: 25, + isValidationRequired: true, + isMandatory: true, + documentCategoryId: "", + entityTypeId:"" + }, + }); + const { + register, + handleSubmit, + reset,watch, + formState: { errors }, + } = methods; + + + const selectedDocumentEntity = watch("entityTypeId") + const {DocumentCategories,isLoading} = useDocumentCategories(selectedDocumentEntity) + + + + const { mutate: createDocType, isPending: creating } = useCreateDocumentType(() => + onClose?.() + ); + const { mutate: updateDocType, isPending: updating } = useUpdateDocumentType(() => + onClose?.() + ); +const onSubmit = (payload) => { + const { entityTypeId, ...rest } = payload; + + if (data) { + updateDocType({ id: data.id, payload: { ...rest, id: data.id } }); + } else { + createDocType(rest); + } +}; + + + useEffect(() => { + if (data) { + reset({ + name: data.name ?? "", + regexExpression: data.regexExpression ?? "", + allowedContentType: data.allowedContentType ?? "", + maxSizeAllowedInMB: data.maxSizeAllowedInMB ?? 25, + isValidationRequired: data.isValidationRequired ?? true, + isMandatory: data.isMandatory ?? true, + documentCategoryId: data.documentCategory?.id ?? "", + entityTypeId:data.documentCategory?.entityTypeId ?? "" + }); + } + }, [data]); + + return ( + +
+
+

+ {data ? "Edit Document Type" : "Add Document Type"} +

+
+ + {/* Name */} +
+ + + {errors.name && {errors.name.message}} +
+ + {/* Regex Expression */} +
+ + +
+ + {/* Allowed Content Type */} +
+ + + {errors.allowedContentType && ( + {errors.allowedContentType.message} + )} +
+ + {/* Max Size */} +
+ + + {errors.maxSizeAllowedInMB && ( + {errors.maxSizeAllowedInMB.message} + )} +
+ + {/* Validation Required */} +
+ + +
+ + {/* Mandatory */} +
+ + +
+
+ + + {errors.entityTypeId && ( + {errors.entityTypeId.message} + )} +
+ {/* Category */} +
+ + + {errors.documentCategoryId && ( + {errors.documentCategoryId.message} + )} +
+ + {/* Buttons */} +
+ + +
+
+
+ ); +}; + +export default ManageDocumentType; diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index 3f644420..d6665767 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -1,119 +1,68 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import CreateRole from "./CreateRole"; -import DeleteMaster from "./DeleteMaster"; import EditRole from "./EditRole"; import CreateJobRole from "./CreateJobRole"; import EditJobRole from "./EditJobRole"; import CreateActivity from "./CreateActivity"; import EditActivity from "./EditActivity"; -import ConfirmModal from "../common/ConfirmModal"; -import { MasterRespository } from "../../repositories/MastersRepository"; -import { cacheData, getCachedData } from "../../slices/apiDataManager"; -import showToast from "../../services/toastService"; import CreateWorkCategory from "./CreateWorkCategory"; import EditWorkCategory from "./EditWorkCategory"; import CreateCategory from "./CreateContactCategory"; import CreateContactTag from "./CreateContactTag"; import EditContactCategory from "./EditContactCategory"; import EditContactTag from "./EditContactTag"; -import { useDeleteMasterItem } from "../../hooks/masterHook/useMaster"; import ManageExpenseType from "./ManageExpenseType"; import ManagePaymentMode from "./ManagePaymentMode"; import ManageExpenseStatus from "./ManageExpenseStatus"; import ManageDocumentCategory from "./ManageDocumentCategory"; - +import ManageDocumentType from "./ManageDocumentType"; const MasterModal = ({ modaldata, closeModal }) => { - const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); - const { mutate: deleteMasterItem, isPending } = useDeleteMasterItem(); - - const handleSelectedMasterDeleted = () => { - const { masterType, item, validateFn } = modaldata || {}; - if (!masterType || !item?.id) { - showToast("Missing master type or item", "error"); - return; - } - - deleteMasterItem( - { masterType, item, validateFn }, - { onSuccess: handleCloseDeleteModal } - ); - }; - - const handleCloseDeleteModal = () => { - setIsDeleteModalOpen(false); - closeModal(); - }; - - useEffect(() => { - if (modaldata?.modalType === "delete") { - setIsDeleteModalOpen(true); - } - }, [modaldata]); - - if (!modaldata?.modalType) { - closeModal(); + if (!modaldata?.modalType || modaldata.modalType === "delete") { + return null; } - if (modaldata.modalType === "delete" && isDeleteModalOpen) { - return ( -
- -
- ); - } + const { modalType, item, masterType } = modaldata; - const renderModalContent = () => { - const { modalType, item, masterType } = modaldata; - - const modalComponents = { - "Application Role": , - "Edit-Application Role": , - "Job Role": , - "Edit-Job Role": , - "Activity": , - "Edit-Activity": , - "Work Category": , - "Edit-Work Category": , - "Contact Category": , - "Edit-Contact Category": , - "Contact Tag": , - "Edit-Contact Tag": , - "Expense Type":, - "Edit-Expense Type":, - "Payment Mode":, - "Edit-Payment Mode":, - "Expense Status":, - "Edit-Expense Status":, - "Document Category":, - "Edit-Document Category": - }; - - return modalComponents[modalType] || null; + const modalComponents = { + "Application Role": ( + + ), + "Edit-Application Role": ( + + ), + "Job Role": , + "Edit-Job Role": , + "Activity": , + "Edit-Activity": , + "Work Category": , + "Edit-Work Category": , + "Contact Category": , + "Edit-Contact Category": ( + + ), + "Contact Tag": , + "Edit-Contact Tag": , + "Expense Type": , + "Edit-Expense Type": , + "Payment Mode": , + "Edit-Payment Mode": , + "Expense Status": , + "Edit-Expense Status": ( + + ), + "Document Category": , + "Edit-Document Category": ( + + ), + "Document Type": , + "Edit-Document Type": ( + + ), }; - const isLargeModal = ["Application Role", "Edit-Application Role"].includes( - modaldata.modalType - ); - - return ( - <> - {renderModalContent()} - - ); + return modalComponents[modalType] || null; }; export default MasterModal; diff --git a/src/hooks/masterHook/useMaster.js b/src/hooks/masterHook/useMaster.js index c7d23bc9..cae0f027 100644 --- a/src/hooks/masterHook/useMaster.js +++ b/src/hooks/masterHook/useMaster.js @@ -748,6 +748,7 @@ export const useCreateDocumentCatgory =(onSuccessCallback)=>{ onSuccess: ( data ) => { queryClient.invalidateQueries( {queryKey:[ "masterData", "Document Category" ]} ) + queryClient.invalidateQueries( {queryKey:[ "Document Category" ]} ) showToast( "Document Category added successfully", "success" ); if(onSuccessCallback) onSuccessCallback(data) }, @@ -770,6 +771,7 @@ export const useUpdateDocumentCategory =(onSuccessCallback)=>{ onSuccess: ( data ) => { queryClient.invalidateQueries( {queryKey:[ "masterData", "Document Category" ]} ) + queryClient.invalidateQueries( {queryKey:[ "Document Category" ]} ) showToast( "Document Category Updated successfully", "success" ); if(onSuccessCallback) onSuccessCallback(data) }, @@ -779,6 +781,51 @@ export const useUpdateDocumentCategory =(onSuccessCallback)=>{ } }) } + +// ------------------------------Document-Type----------------------------------- +export const useCreateDocumentType =(onSuccessCallback)=>{ + const queryClient = useQueryClient(); + + return useMutation( { + mutationFn: async ( payload ) => + { + const resp = await MasterRespository.createDocumentType(payload); + return resp.data; + }, + onSuccess: ( data ) => + { + queryClient.invalidateQueries( {queryKey:[ "masterData", "Document Type" ]} ) + showToast( "Document Type added successfully", "success" ); + if(onSuccessCallback) onSuccessCallback(data) + }, + onError: ( error ) => + { + showToast(error.message || "Something went wrong", "error"); + } + }) +} + +export const useUpdateDocumentType =(onSuccessCallback)=>{ + const queryClient = useQueryClient(); + + return useMutation( { + mutationFn: async ( {id,payload} ) => + { + const resp = await MasterRespository.updateDocumentType(id,payload); + return resp.data; + }, + onSuccess: ( data ) => + { + queryClient.invalidateQueries( {queryKey:[ "masterData", "Document Type" ]} ) + showToast( "Document Type Updated successfully", "success" ); + if(onSuccessCallback) onSuccessCallback(data) + }, + onError: ( error ) => + { + showToast(error.message || "Something went wrong", "error"); + } + }) +} // -Delete Master -------- export const useDeleteMasterItem = () => { const queryClient = useQueryClient(); diff --git a/src/pages/master/MasterPage.jsx b/src/pages/master/MasterPage.jsx index 3d6295c9..c73a1c29 100644 --- a/src/pages/master/MasterPage.jsx +++ b/src/pages/master/MasterPage.jsx @@ -4,35 +4,45 @@ import MasterModal from "../../components/master/MasterModal"; import { mastersList } from "../../data/masters"; import { useDispatch, useSelector } from "react-redux"; import { changeMaster } from "../../slices/localVariablesSlice"; -import useMaster, { useMasterMenu } from "../../hooks/masterHook/useMaster" +import useMaster, { + useDeleteMasterItem, + useMasterMenu, +} from "../../hooks/masterHook/useMaster"; import MasterTable from "./MasterTable"; import { getCachedData } from "../../slices/apiDataManager"; import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { MANAGE_MASTER } from "../../utils/constants"; import { useQueryClient } from "@tanstack/react-query"; import GlobalModel from "../../components/common/GlobalModel"; - +import ConfirmModal from "../../components/common/ConfirmModal"; const MasterPage = () => { - const {data,isLoading,isError,error:menuError} = useMasterMenu() - const [modalConfig, setModalConfig] = useState({ modalType: "", item: null, masterType: null }); - const [searchTerm, setSearchTerm] = useState(''); + const { data, isLoading, isError, error: menuError } = useMasterMenu(); + const [modalConfig, setModalConfig] = useState(null); + const [searchTerm, setSearchTerm] = useState(""); const [filteredResults, setFilteredResults] = useState([]); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const hasMasterPermission = useHasUserPermission(MANAGE_MASTER); const dispatch = useDispatch(); - const selectedMaster = useSelector((store) => store.localVariables.selectedMaster); + const selectedMaster = useSelector( + (store) => store.localVariables.selectedMaster + ); const queryClient = useQueryClient(); - const { data: masterData = [], loading, error, RecallApi,isError:isMasterError } = useMaster(); - + const { + data: masterData = [], + loading, + error, + RecallApi, + isError: isMasterError, + } = useMaster(); + const { mutate: deleteMasterItem, isPending } = useDeleteMasterItem(); const openModal = () => setIsCreateModalOpen(true); const closeModal = () => { setIsCreateModalOpen(false); setModalConfig(null); - }; const handleModalData = (modalType, item, masterType = selectedMaster) => { @@ -46,15 +56,17 @@ const MasterPage = () => { if (!masterData?.length) return; const results = masterData.filter((item) => - Object.values(item).some( - (field) => field?.toString().toLowerCase().includes(value) + Object.values(item).some((field) => + field?.toString().toLowerCase().includes(value) ) ); setFilteredResults(results); }; const displayData = useMemo(() => { if (searchTerm) return filteredResults; - return queryClient.getQueryData(["masterData", selectedMaster]) || masterData; + return ( + queryClient.getQueryData(["masterData", selectedMaster]) || masterData + ); }, [searchTerm, filteredResults, selectedMaster, masterData]); const columns = useMemo(() => { @@ -65,9 +77,12 @@ const MasterPage = () => { })); }, [displayData]); - useEffect(() => { - if (modalConfig) openModal(); - }, [modalConfig]); +useEffect(() => { + if (modalConfig?.modalType && modalConfig?.modalType !== "delete") { + openModal(); + } +}, [modalConfig]); + useEffect(() => { return () => { @@ -76,14 +91,44 @@ const MasterPage = () => { }; }, []); - if(isError || isMasterError) return
-

Oops, an error occurred

-

{error?.message || menuError?.message}

-
+ if (isError || isMasterError) + return ( +
+

+ Oops, an error + occurred +

+

{error?.message || menuError?.message}

+
+ ); return ( <> - {isCreateModalOpen && ( - setIsCreateModalOpen(false)} size={modalConfig.masterType === "Application Role" ? "lg":"md"}> + {modalConfig?.modalType === "delete" && ( + { + deleteMasterItem( + { + masterType: modalConfig.masterType, + item: modalConfig.item, + validateFn: modalConfig.validateFn, + }, + { onSuccess: closeModal } + ); + }} + onClose={closeModal} + loading={isPending} + isOpen={true} + /> + )} + {isCreateModalOpen && modalConfig?.modalType !== "delete" && ( + )} @@ -112,17 +157,24 @@ const MasterPage = () => { >
@@ -145,10 +197,13 @@ const MasterPage = () => { >
-
+
{" "}
- {" "} @@ -173,13 +232,17 @@ const MasterPage = () => {
- +
-
); diff --git a/src/pages/master/MasterTable.jsx b/src/pages/master/MasterTable.jsx index 1c4da079..a677588b 100644 --- a/src/pages/master/MasterTable.jsx +++ b/src/pages/master/MasterTable.jsx @@ -157,19 +157,17 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => { data-bs-target="#master-modal" className="btn p-0 dropdown-toggle hide-arrow" onClick={() => - handleModalData(`Edit-${selectedMaster}`, item) + handleModalData(`Edit-${selectedMaster}`, item, selectedMaster) } > diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index 2ac3337e..84f56aeb 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -48,6 +48,9 @@ export const MasterRespository = { api.delete(`/api/Master/payment-mode/delete/${id}`, (isActive = false)), "Expense Status": (id, isActive) => api.delete(`/api/Master/expenses-status/delete/${id}`, (isActive = false)), + "Document Type": (id) => api.delete(`/api/Master/document-type/delete/${id}`), + "Document Category": (id) => + api.delete(`/api/Master/document-category/delete/${id}`), getWorkCategory: () => api.get(`/api/master/work-categories`), createWorkCategory: (data) => api.post(`/api/master/work-category`, data),