diff --git a/public/assets/css/core-extend.css b/public/assets/css/core-extend.css index 8ec6df19..7823d036 100644 --- a/public/assets/css/core-extend.css +++ b/public/assets/css/core-extend.css @@ -10,7 +10,21 @@ .table_header_border { border-bottom:2px solid var(--bs-table-border-color) ; } +.text-gary-80 { +color:var(--bs-gray-500) +} +.text-royalblue{ +color: #1796e3; +} + +.text-md { +font-size: 2rem; +} + +.text-md-b { +font-weight: normal; +} .text-xxs { font-size: 0.55rem; } /* 8px */ .text-xs { font-size: 0.75rem; } /* 12px */ diff --git a/src/ModalProvider.jsx b/src/ModalProvider.jsx index c4e0779f..bb217139 100644 --- a/src/ModalProvider.jsx +++ b/src/ModalProvider.jsx @@ -4,17 +4,20 @@ import OrganizationModal from "./components/Organization/OrganizationModal"; import { useAuthModal, useModal } from "./hooks/useAuth"; import SwitchTenant from "./pages/authentication/SwitchTenant"; import ChangePasswordPage from "./pages/authentication/ChangePassword"; +import NewCollection from "./components/collections/ManageCollection"; const ModalProvider = () => { const { isOpen, onClose } = useOrganizationModal(); const { isOpen: isAuthOpen } = useAuthModal(); const {isOpen:isChangePass} = useModal("ChangePassword") + const {isOpen:isCollectionNew} = useModal("newCollection"); return ( <> {isOpen && } {isAuthOpen && } {isChangePass && } + {isCollectionNew && } ); }; diff --git a/src/components/Expenses/PreviewDocument.jsx b/src/components/Expenses/PreviewDocument.jsx index 9c0f5c49..ce554ca9 100644 --- a/src/components/Expenses/PreviewDocument.jsx +++ b/src/components/Expenses/PreviewDocument.jsx @@ -1,27 +1,53 @@ -import { useState } from 'react'; +import { useState } from "react"; const PreviewDocument = ({ imageUrl }) => { const [loading, setLoading] = useState(true); + const [rotation, setRotation] = useState(0); return ( -
+ <> +
+ setRotation((prev) => prev + 90)} + > +
+
+ {loading && ( -
- Loading... -
+
Loading...
)} - Full View setLoading(false)} - /> + +
+ Full View setLoading(false)} + /> +
+ +
+ +
+ ); }; diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index 2a459b94..3c4b5f86 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -26,23 +26,29 @@ const Header = () => { const dispatch = useDispatch(); const { data, loading } = useMaster(); const navigate = useNavigate(); - const { onOpen } = useAuthModal() - const { onOpen: changePass } = useModal("ChangePassword"); + const {onOpen} = useAuthModal() + const { onOpen:changePass } = useModal("ChangePassword"); const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT); - const { mutate: logout, isPending: logouting } = useLogout() + const { mutate : logout,isPending:logouting} = useLogout() const isDashboardPath = /^\/dashboard$/.test(location.pathname) || /^\/$/.test(location.pathname); const isProjectPath = /^\/projects$/.test(location.pathname); + const isCollectionPath = + /^\/collection$/.test(location.pathname) || /^\/$/.test(location.pathname); const showProjectDropdown = (pathname) => { - // Paths where we DON'T want to show the project dropdown - const excludedPaths = ["/directory", "/expenses", "/employee"]; + const isDirectoryPath = /^\/directory$/.test(pathname); - // Check if pathname starts with any of the excluded paths - return !excludedPaths.some((path) => pathname.startsWith(path)); + // const isProfilePage = /^\/employee$/.test(location.pathname); + const isProfilePage = + /^\/employee\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test( + pathname + ); + const isExpensePage = /^\/expenses$/.test(pathname); + + return !(isDirectoryPath || isProfilePage || isExpensePage); }; - const allowedProjectStatusIds = [ "603e994b-a27f-4e5d-a251-f3d69b0498ba", "cdad86aa-8a56-4ff4-b633-9c629057dfef", @@ -55,9 +61,9 @@ const Header = () => { return role ? role.name : "User"; }; + - - + const handleProfilePage = () => { navigate(`/employee/${profile?.employeeInfo?.id}`); @@ -70,8 +76,8 @@ const Header = () => { const projectsForDropdown = isDashboardPath ? projectNames : projectNames?.filter((project) => - allowedProjectStatusIds.includes(project.projectStatusId) - ); + allowedProjectStatusIds.includes(project.projectStatusId) + ); let currentProjectDisplayName; if (projectLoading) { @@ -187,63 +193,63 @@ const Header = () => { >
{showProjectDropdown(location.pathname) && ( -
- -
- {shouldShowDropdown ? ( - - ) : ( - - {currentProjectDisplayName} - - )} +
+ +
+ {shouldShowDropdown ? ( + + ) : ( + + {currentProjectDisplayName} + + )} - {shouldShowDropdown && - projectsForDropdown && - projectsForDropdown.length > 0 && ( -
    - {isDashboardPath && ( -
  • + {shouldShowDropdown && + projectsForDropdown && + projectsForDropdown.length > 0 && ( +
      + {(isDashboardPath|| isCollectionPath) &&( +
    • + +
    • + )} + {[...projectsForDropdown] + .sort((a, b) => a?.name?.localeCompare(b.name)) + .map((project) => ( +
    • - )} - {[...projectsForDropdown] - .sort((a, b) => a?.name?.localeCompare(b.name)) - .map((project) => ( -
    • - -
    • - ))} -
    - )} -
+ ))} + + )}
- )} +
+ )}
diff --git a/src/components/collections/AddPayment.jsx b/src/components/collections/AddPayment.jsx new file mode 100644 index 00000000..a47200ee --- /dev/null +++ b/src/components/collections/AddPayment.jsx @@ -0,0 +1,266 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import React, { useState } from "react"; +import { FormProvider, useForm } from "react-hook-form"; +import { defaultPayment, paymentSchema } from "./collectionSchema"; +import Label from "../common/Label"; +import DatePicker from "../common/DatePicker"; +import { formatDate } from "date-fns"; +import { useCollectionContext } from "../../pages/collections/CollectionPage"; +import { useAddPayment, useCollection } from "../../hooks/useCollections"; +import { formatFigure, localToUtc } from "../../utils/appUtils"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import Avatar from "../common/Avatar"; +import { PaymentHistorySkeleton } from "./CollectionSkeleton"; +import { usePaymentAjustmentHead } from "../../hooks/masterHook/useMaster"; + +const AddPayment = ({ onClose }) => { + const { addPayment } = useCollectionContext(); + const { data, isLoading, isError, error } = useCollection( + addPayment?.invoiceId + ); + const { + data: paymentTypes, + isLoading: isPaymentTypeLoading, + isError: isPaymentTypeError, + error: paymentError, + } = usePaymentAjustmentHead(true); + const methods = useForm({ + resolver: zodResolver(paymentSchema), + defaultValues: defaultPayment, + }); + const { + control, + register, + handleSubmit, + reset, + formState: { errors }, + } = methods; + const { mutate: AddPayment, isPending } = useAddPayment(() => { + handleClose(); + }); + const onSubmit = (formData) => { + const payload = { + ...formData, + paymentReceivedDate: localToUtc(formData.paymentReceivedDate), + invoiceId: addPayment.invoiceId, + }; + AddPayment(payload); + }; + const handleClose = (formData) => { + reset(defaultPayment); + }; + + return ( +
+
Add Payment
+ +
+
+
+ + + {errors.transactionId && ( + + {errors.transactionId.message} + + )} +
+ +
+ + + {errors.paymentReceivedDate && ( + + {errors.paymentReceivedDate.message} + + )} +
+ +
+ + + {errors.paymentAdjustmentHeadId && ( + + {errors.paymentAdjustmentHeadId.message} + + )} +
+ +
+ + + {errors.amount && ( + {errors.amount.message} + )} +
+
+ + + {errors.description && ( + + {errors.description.message} + + )} +
+
+ +
+ + +
document.getElementById("attachments").click()} + > + + + Click to select or click here to browse + + + (PDF, JPG, PNG,Doc,docx,xls,xlsx max 5MB) + + + { + onFileChange(e); + e.target.value = ""; + }} + /> +
+ {errors.attachments && ( + + {errors.attachments.message} + + )} + {files.length > 0 && ( +
+ {files + .filter((file) => { + if (collectionId) { + return file.isActive; + } + return true; + }) + .map((file, idx) => ( + +
+ + {file.fileName} + + + {file.fileSize ? formatFileSize(file.fileSize) : ""} + +
+ { + e.preventDefault(); + removeFile(collectionId ? file.documentId : idx); + }} + > +
+ ))} +
+ )} + + {Array.isArray(errors.attachments) && + errors.attachments.map((fileError, index) => ( +
+ { + (fileError?.fileSize?.message || + fileError?.contentType?.message || + fileError?.base64Data?.message, + fileError?.documentId?.message) + } +
+ ))} +
+ +
+ {" "} + + +
+
+ + +
+ ); +}; + +export default ManageCollection; diff --git a/src/components/collections/PaymentHistoryTable.jsx b/src/components/collections/PaymentHistoryTable.jsx new file mode 100644 index 00000000..e7b31fc9 --- /dev/null +++ b/src/components/collections/PaymentHistoryTable.jsx @@ -0,0 +1,53 @@ +import React from 'react' +import { formatUTCToLocalTime } from '../../utils/dateUtils' +import { formatFigure } from '../../utils/appUtils' +import Avatar from '../common/Avatar' + +const PaymentHistoryTable = ({data}) => { + return ( +
+ + {data?.receivedInvoicePayments?.length > 0 ? ( +
+ + + + + + + + + + + + + {data.receivedInvoicePayments.map((payment, index) => ( + + + + + + + + + ))} + +
Sr.NoTransaction ID Received Date Payment Adjustment-HeadAmountUpdated By
{index + 1}{payment.transactionId}{formatUTCToLocalTime(payment.paymentReceivedDate)}{payment?.paymentAdjustmentHead?.name ?? "--"} + {formatFigure(payment.amount, { + type: "currency", + currency: "INR", + })} + +
+ + {payment.createdBy?.firstName}{" "} + {payment.createdBy?.lastName} +
+
+
+ ):(

No History

)} +
+ ) +} + +export default PaymentHistoryTable diff --git a/src/components/collections/ViewCollection.jsx b/src/components/collections/ViewCollection.jsx new file mode 100644 index 00000000..799033e5 --- /dev/null +++ b/src/components/collections/ViewCollection.jsx @@ -0,0 +1,258 @@ +import React, { useState } from "react"; +import { useCollectionContext } from "../../pages/collections/CollectionPage"; +import { useCollection } from "../../hooks/useCollections"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import { formatFigure, getIconByFileType } from "../../utils/appUtils"; +import Avatar from "../common/Avatar"; +import PaymentHistoryTable from "./PaymentHistoryTable"; +import Comment from "./Comment"; +import { CollectionDetailsSkeleton } from "./CollectionSkeleton"; +import { useHasUserPermission } from "../../hooks/useHasUserPermission"; +import { ADMIN_COLLECTION, EDIT_COLLECTION } from "../../utils/constants"; + +const ViewCollection = ({ onClose }) => { + const [activeTab, setActiveTab] = useState("payments"); + const isAdmin = useHasUserPermission(ADMIN_COLLECTION); + const canEditCollection = useHasUserPermission(EDIT_COLLECTION); + const { viewCollection, setCollection, setDocumentView } = + useCollectionContext(); + const { data, isLoading, isError, error } = useCollection(viewCollection); + + const handleEdit = () => { + setCollection({ isOpen: true, invoiceId: viewCollection }); + onClose(); + }; + + if (isLoading) return ; + if (isError) return
{error.message}
; + + return ( +
+

Collection Details

+
+
+
+ +
{data?.project?.name}
+
+
+ {" "} + + {data?.isActive ? "Active" : "Inactive"} + + {(isAdmin || canEditCollection) && + !data?.receivedInvoicePayments && ( + + + + )} +
+
+
+
+
Title :
+
{data?.title}
+
+
+
+
+
Invoice Number:
+
{data?.invoiceNumber}
+
+
+ {/* Row 2: E-Invoice Number + Project */} +
+
+
E-Invoice Number:
+
{data?.eInvoiceNumber}
+
+
+ + {/* Row 3: Invoice Date + Client Submitted Date */} +
+
+
Invoice Date:
+
+ {formatUTCToLocalTime(data?.invoiceDate)} +
+
+
+
+
+
Client Submission Date:
+
+ {formatUTCToLocalTime(data?.clientSubmitedDate)} +
+
+
+ {/* Row 4: Expected Payment Date + Mark as Completed */} +
+
+
Expected Payment Date:
+
+ {formatUTCToLocalTime(data?.exceptedPaymentDate)} +
+
+
+ + {/* Row 5: Basic Amount + Tax Amount */} +
+
+
Basic Amount :
+
+ {formatFigure(data?.basicAmount, { + type: "currency", + currency: "INR", + })} +
+
+
+
+
+
Tax Amount :
+
+ {formatFigure(data?.taxAmount, { + type: "currency", + currency: "INR", + })} +
+
+
+ {/* Row 6: Balance Amount + Created At */} +
+
+
Balance Amount :
+
+ {formatFigure(data?.balanceAmount, { + type: "currency", + currency: "INR", + })} +
+
+
+
+
+
Created At :
+
{formatUTCToLocalTime(data?.createdAt)}
+
+
+ {/* Row 7: Created By */} +
+
+
Created By :
+
+ + + {data?.createdBy?.firstName} {data?.createdBy?.lastName} + +
+
+
+ {/* Description */} +
+
Description :
+
{data?.description}
+
+ +
+ + +
+ {data?.attachments?.map((doc) => { + const isImage = doc.contentType?.includes("image"); + + return ( +
{ + if (isImage) { + setDocumentView({ + IsOpen: true, + Image: doc.preSignedUrl, + }); + } + }} + > + + + {doc.fileName} + +
+ ); + }) ?? "No Attachment"} +
+
+ +
+ {/* Tabs Navigation */} +
    +
  • + +
  • +
  • + +
  • +
+ + {/* Tab Content */} +
+ {activeTab === "payments" && ( +
+ +
+ )} + + {activeTab === "details" && ( +
+ +
+ )} +
+
+
+
+ ); +}; + +export default ViewCollection; diff --git a/src/components/collections/collectionSchema.jsx b/src/components/collections/collectionSchema.jsx new file mode 100644 index 00000000..bb97fede --- /dev/null +++ b/src/components/collections/collectionSchema.jsx @@ -0,0 +1,101 @@ +import { z } from "zod"; + +const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB +const ALLOWED_TYPES = [ + "application/pdf", + "application/doc", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "image/png", + "image/jpg", + "image/jpeg", + "application/vnd.ms-excel", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", +]; + + +export const newCollection = z.object({ + title: z.string().trim().min(1, { message: "Title is required" }), + projectId: z.string().trim().min(1, { message: "Project is required" }), + invoiceDate: z.string().min(1, { message: "Date is required" }), + description: z.string().trim().optional(), + clientSubmitedDate: z.string().min(1, { message: "Date is required" }), + exceptedPaymentDate: z.string().min(1, { message: "Date is required" }), + invoiceNumber: z + .string() + .trim() + .min(1, { message: "Invoice is required" }) + .max(17, { message: "Invalid Number" }), + eInvoiceNumber: z + .string() + .trim() + .min(1, { message: "E-Invoice No is required" }), + taxAmount: z.coerce + .number({ + invalid_type_error: "Amount is required and must be a number", + }) + .min(1, "Amount must be Enter") + .refine((val) => /^\d+(\.\d{1,2})?$/.test(val.toString()), { + message: "Amount must have at most 2 decimal places", + }), + basicAmount: z.coerce + .number({ + invalid_type_error: "Amount is required and must be a number", + }) + .min(1, "Amount must be Enter") + .refine((val) => /^\d+(\.\d{1,2})?$/.test(val.toString()), { + message: "Amount must have at most 2 decimal places", + }), + attachments: z + .array( + z.object({ + fileName: z.string().min(1, { message: "Filename is required" }), + base64Data: z.string().nullable(), + contentType: z.string().refine((val) => ALLOWED_TYPES.includes(val), { + message: "Only PDF, PNG, JPG, or JPEG files are allowed", + }), + documentId: z.string().optional(), + fileSize: z.number().max(MAX_FILE_SIZE, { + message: "File size must be less than or equal to 5MB", + }), + description: z.string().optional(), + isActive: z.boolean().default(true), + }) + ) + .nonempty({ message: "At least one file attachment is required" }), +}); + +export const defaultCollection = { + projectId: "", + invoiceNumber: " ", + eInvoiceNumber: "", + title: "", + clientSubmitedDate: null, + invoiceDate: null, + exceptedPaymentDate: null, + taxAmount: "", + basicAmount: "", + description: "", + attachments: [], +}; + +export const paymentSchema = z.object({ + paymentReceivedDate: z.string().min(1, { message: "Date is required" }), + transactionId: z.string().min(1, "Transaction ID is required"), + amount: z.number().min(1, "Amount must be greater than zero"), + comment:z.string().min(1,{message:"Comment required"}), + paymentAdjustmentHeadId:z.string().min(1,{message:"Payment Type required"}) +}); + +// Default Value +export const defaultPayment = { + paymentReceivedDate: null, + transactionId: "", + amount: 0, + comment:"", + paymentAdjustmentHeadId:"" +}; + + +export const CommentSchema = z.object({ + comment:z.string().min(1,{message:"Comment required"}) +}) diff --git a/src/components/common/AccessDenied.jsx b/src/components/common/AccessDenied.jsx new file mode 100644 index 00000000..62cc5f4e --- /dev/null +++ b/src/components/common/AccessDenied.jsx @@ -0,0 +1,22 @@ +import React from "react"; +import Breadcrumb from "./Breadcrumb"; + +const AccessDenied = ({data}) => { + return ( +
+ + +
+ +

+ Access Denied: You don't have permission to perform this action ! +

+
+ +
+ ); +}; + +export default AccessDenied; diff --git a/src/components/common/Avatar.jsx b/src/components/common/Avatar.jsx index 5c3f25e6..04b36f3c 100644 --- a/src/components/common/Avatar.jsx +++ b/src/components/common/Avatar.jsx @@ -51,7 +51,7 @@ const Avatar = ({ firstName, lastName, size = "sm", classAvatar }) => { return (
-
+
{generateAvatarText(firstName, lastName)} diff --git a/src/components/common/ConfirmModal.jsx b/src/components/common/ConfirmModal.jsx index 037c6a59..dd0158eb 100644 --- a/src/components/common/ConfirmModal.jsx +++ b/src/components/common/ConfirmModal.jsx @@ -13,19 +13,18 @@ const ConfirmModal = ({ if (!isOpen) return null; const TypeofIcon = () => { - if (type === "delete") { - return ( - - ); + switch (type) { + case "delete": + return ; + case "success": + return ; + case "warning": + return ; + default: + return null; } - return null; }; - const modalSize = type === "delete" ? "sm" : "md"; - return (
-
+
{header && {header}}
-
{TypeofIcon()}
-
+
+ {TypeofIcon()} +
+
{message}
+
diff --git a/src/components/common/DateRangePicker.jsx b/src/components/common/DateRangePicker.jsx index ec8c36e9..5bc1894d 100644 --- a/src/components/common/DateRangePicker.jsx +++ b/src/components/common/DateRangePicker.jsx @@ -88,7 +88,6 @@ export default DateRangePicker; - export const DateRangePicker1 = ({ startField = "startDate", endField = "endDate", @@ -98,6 +97,7 @@ export const DateRangePicker1 = ({ resetSignal, defaultRange = true, maxDate = null, + howManyDay = 6, ...rest }) => { const inputRef = useRef(null); @@ -107,12 +107,13 @@ export const DateRangePicker1 = ({ field: { ref }, } = useController({ name: startField, control }); + // Apply default range const applyDefaultDates = () => { const today = new Date(); - const past = new Date(); - past.setDate(today.getDate() - 6); + const past = new Date(today.getTime()); + past.setDate(today.getDate() - howManyDay); - const format = (d) => flatpickr.formatDate(d, "d-m-Y"); + const format = (d) => window.flatpickr.formatDate(d, "d-m-Y"); const formattedStart = format(past); const formattedEnd = format(today); @@ -127,15 +128,19 @@ export const DateRangePicker1 = ({ useEffect(() => { if (!inputRef.current || inputRef.current._flatpickr) return; - const instance = flatpickr(inputRef.current, { + if (defaultRange && !getValues(startField) && !getValues(endField)) { + applyDefaultDates(); + } + + const instance = window.flatpickr(inputRef.current, { mode: "range", dateFormat: "d-m-Y", allowInput: allowText, - maxDate , + maxDate, onChange: (selectedDates) => { if (selectedDates.length === 2) { const [start, end] = selectedDates; - const format = (d) => flatpickr.formatDate(d, "d-m-Y"); + const format = (d) => window.flatpickr.formatDate(d, "d-m-Y"); setValue(startField, format(start), { shouldValidate: true }); setValue(endField, format(end), { shouldValidate: true }); } else { @@ -148,12 +153,10 @@ export const DateRangePicker1 = ({ const currentStart = getValues(startField); const currentEnd = getValues(endField); - if (defaultRange && !currentStart && !currentEnd) { - applyDefaultDates(); - } else if (currentStart && currentEnd) { + if (currentStart && currentEnd) { instance.setDate([ - flatpickr.parseDate(currentStart, "d-m-Y"), - flatpickr.parseDate(currentEnd, "d-m-Y"), + window.flatpickr.parseDate(currentStart, "d-m-Y"), + window.flatpickr.parseDate(currentEnd, "d-m-Y"), ]); } @@ -161,20 +164,19 @@ export const DateRangePicker1 = ({ }, []); useEffect(() => { - if (resetSignal !== undefined) { - if (defaultRange) { - applyDefaultDates(); - } else { - setValue(startField, "", { shouldValidate: true }); - setValue(endField, "", { shouldValidate: true }); + if (resetSignal !== undefined) { + if (defaultRange) { + applyDefaultDates(); + } else { + setValue(startField, "", { shouldValidate: true }); + setValue(endField, "", { shouldValidate: true }); - if (inputRef.current?._flatpickr) { - inputRef.current._flatpickr.clear(); + if (inputRef.current?._flatpickr) { + inputRef.current._flatpickr.clear(); + } } } - } -}, [resetSignal, defaultRange, setValue, startField, endField]); - + }, [resetSignal, defaultRange, setValue, startField, endField]); const start = getValues(startField); const end = getValues(endField); @@ -186,7 +188,7 @@ export const DateRangePicker1 = ({ type="text" className="form-control form-control-sm" placeholder={placeholder} - defaultValue={formattedValue} + value={formattedValue} ref={(el) => { inputRef.current = el; ref(el); diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index b26d6de4..569ea854 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -16,10 +16,10 @@ import ManageDocumentCategory from "./ManageDocumentCategory"; import ManageDocumentType from "./ManageDocumentType"; import ManageServices from "./Services/ManageServices"; import ServiceGroups from "./Services/ServicesGroups"; +import ManagePaymentHead from "./paymentAdjustmentHead/ManagePaymentHead"; const MasterModal = ({ modaldata, closeModal }) => { if (!modaldata?.modalType || modaldata.modalType === "delete") { - return null; } @@ -33,7 +33,7 @@ const MasterModal = ({ modaldata, closeModal }) => { ), "Job Role": , - "Edit-Job Role": , + "Edit-Job Role": , "Work Category": , "Edit-Work Category": , "Contact Category": , @@ -58,24 +58,20 @@ const MasterModal = ({ modaldata, closeModal }) => { "Edit-Document Type": ( ), - "Services": ( - - ), - "Edit-Services": ( - - ), - "Manage-Services": ( - - ), + Services: , + "Edit-Services": , + "Manage-Services": , + "Payment Adjustment Head": , + "Edit-Payment Adjustment Head": }; return ( -
-
-

{`${masterType, " ", modalType}`}

+
+
+

{`${(masterType, " ", modalType)}`}

+
+ {modalComponents[modalType] || null}
- { modalComponents[modalType] || null} -
); }; diff --git a/src/components/master/paymentAdjustmentHead/ManagePaymentHead.jsx b/src/components/master/paymentAdjustmentHead/ManagePaymentHead.jsx new file mode 100644 index 00000000..1afba4ab --- /dev/null +++ b/src/components/master/paymentAdjustmentHead/ManagePaymentHead.jsx @@ -0,0 +1,107 @@ +import React, { useEffect } from "react"; +import { z } from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import Label from "../../common/Label"; +import { useCreatePaymentAjustmentHead, useUpdatePaymentAjustmentHead } from "../../../hooks/masterHook/useMaster"; + +export const simpleFormSchema = z.object({ + name: z.string().min(1, "Name is required"), + description: z.string().min(1, "Description is required"), +}); + +const ManagePaymentHead = ({ data, onClose }) => { + const { + register, + handleSubmit, + reset, + formState: { errors }, + } = useForm({ + resolver: zodResolver(simpleFormSchema), + defaultValues: { + name: "", + description: "", + }, + }); + + const {mutate:CreateAjustmentHead,isPending} = useCreatePaymentAjustmentHead(()=>{ + handleClose?.() + }); + const {mutate:UpdateAjustmentHead,isPending:isUpdating} = useUpdatePaymentAjustmentHead(()=>{ + handleClose?.() + }) + const onSubmit = (formData) => { + if(data){ + let id = data?.id; + const payload = { + ...formData, + id:id, + } + UpdateAjustmentHead({id:id,payload:payload}) + }else{ + let payload={ + ...formData + } + CreateAjustmentHead(payload) + } + }; + + useEffect(() => { + if (data) { + reset({ + name: data.name, + description: data.description, + }); + } + }, [data]); + const handleClose = () => { + reset(); + onClose(); + }; + return ( +
+
+
+ + + {errors.name && ( +
{errors.name.message}
+ )} +
+ +
+ +