From b8d3e584288a572b2331a7039cc46906a3bc1ee0 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Thu, 27 Nov 2025 21:25:23 +0530 Subject: [PATCH] added challan --- public/assets/css/core-extend.css | 4 +- src/components/Expenses/Filelist.jsx | 132 +++++++---- src/components/common/SigleFileUploader.jsx | 111 ++++++++++ .../purchase/DeliverChallanList.jsx | 68 ++++++ src/components/purchase/DeliveryChallane.jsx | 205 ++++++++++++++++++ src/components/purchase/PurchaseList.jsx | 13 +- src/components/purchase/PurchaseSchema.jsx | 46 ++++ src/hooks/usePurchase.jsx | 37 +++- src/pages/purchase/PurchasePage.jsx | 18 ++ 9 files changed, 582 insertions(+), 52 deletions(-) create mode 100644 src/components/common/SigleFileUploader.jsx create mode 100644 src/components/purchase/DeliverChallanList.jsx create mode 100644 src/components/purchase/DeliveryChallane.jsx diff --git a/public/assets/css/core-extend.css b/public/assets/css/core-extend.css index 127de638..3a8406cb 100644 --- a/public/assets/css/core-extend.css +++ b/public/assets/css/core-extend.css @@ -234,7 +234,9 @@ font-weight: normal; .h-min { height: min-content; } .h-max { height: max-content; } - +.vh-50 { + height: 50vh !important; +} diff --git a/src/components/Expenses/Filelist.jsx b/src/components/Expenses/Filelist.jsx index 35a4a986..ba270877 100644 --- a/src/components/Expenses/Filelist.jsx +++ b/src/components/Expenses/Filelist.jsx @@ -2,54 +2,51 @@ import React from "react"; import { formatFileSize, getIconByFileType } from "../../utils/appUtils"; import Tooltip from "../common/Tooltip"; -const Filelist = ({ files, removeFile, expenseToEdit,sm=6,md=4 }) => { +const Filelist = ({ files, removeFile, expenseToEdit, sm = 6, md = 4 }) => { return (
- {files - .filter((file) => { - if (expenseToEdit) { - return file.isActive; - } - return true; - }) - .map((file, idx) => ( -
-
- {/* File icon and info */} -
- + {files + .filter((file) => { + if (expenseToEdit) { + return file.isActive; + } + return true; + }) + .map((file, idx) => ( +
+
+ {/* File icon and info */} +
+ -
- - {file.fileName} - - - {file.fileSize ? formatFileSize(file.fileSize) : ""} - +
+ + {file.fileName} + + + {file.fileSize ? formatFileSize(file.fileSize) : ""} + +
+
+ + {/* Delete icon */} + + { + e.preventDefault(); + removeFile(expenseToEdit ? file.documentId : idx); + }} + > +
- - {/* Delete icon */} - - { - e.preventDefault(); - removeFile(expenseToEdit ? file.documentId : idx); - }} - > - -
-
- ))} -
- + ))} +
); }; @@ -58,13 +55,11 @@ export const FilelistView = ({ files, viewFile }) => { return (
{files?.map((file, idx) => ( -
+
{/* File icon and info */}
- +
{ ))}
); -}; \ No newline at end of file +}; +export const FileView = ({ file, viewFile }) => { + if (!file) { + return ( +
+

No file uploaded

+
+ ); + } + + return ( +
+
+ {/* File icon and info */} +
+ + +
{ + e.preventDefault(); + viewFile({ + IsOpen: true, + Image: file.preSignedUrl, + }); + }} + > + + {file.fileName} + + + + {" "} + {file.fileSize ? formatFileSize(file.fileSize) : ""} + + +
+
+
+
+ ); +}; diff --git a/src/components/common/SigleFileUploader.jsx b/src/components/common/SigleFileUploader.jsx new file mode 100644 index 00000000..eb32b304 --- /dev/null +++ b/src/components/common/SigleFileUploader.jsx @@ -0,0 +1,111 @@ +import { useRef } from "react"; +import Label from "./Label"; +import Tooltip from "./Tooltip"; +import { formatFileSize, getIconByFileType } from "../../utils/appUtils"; + +const SingleFileUploader = ({ + label = "Upload Document", + required = false, + value, + onChange, + onRemove, + disabled, + error, + accept = ".pdf,.jpg,.jpeg,.png", + maxSizeMB = 25, + hint = "(PDF, JPG, PNG, max 5MB)", +}) => { + const inputRef = useRef(null); + + const handleFileSelect = async (e) => { + const file = e.target.files?.[0]; + if (!file) return; + + // Validate size + if (file.size > maxSizeMB * 1024 * 1024) { + alert(`File size cannot exceed ${maxSizeMB}MB`); + e.target.value = ""; + return; + } + + // Convert to base64 + const base64Data = await new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => resolve(reader.result.split(",")[1]); + reader.onerror = (err) => reject(err); + }); + + const attachmentObj = { + fileName: file.name, + base64Data, + invoiceAttachmentTypeId: "", // set dynamically if needed + contentType: file.type, + fileSize: file.size, + description: "", + isActive: true, + }; + + onChange(attachmentObj); + e.target.value = ""; + }; + + return ( +
+ + +
inputRef.current.click()} + > + + + Click to select or click here to browse + + {hint} + + +
+ + {error && {error}} + + {value && ( +
+
+
+ + +
+ + {value.fileName} + + + {value.fileSize ? formatFileSize(value.fileSize) : ""} + +
+
+ + +
+
+ )} +
+ ); +}; + +export default SingleFileUploader; diff --git a/src/components/purchase/DeliverChallanList.jsx b/src/components/purchase/DeliverChallanList.jsx new file mode 100644 index 00000000..45ecf033 --- /dev/null +++ b/src/components/purchase/DeliverChallanList.jsx @@ -0,0 +1,68 @@ +import React from "react"; +import { useDeliverChallane } from "../../hooks/usePurchase"; +import { SpinnerLoader } from "../common/Loader"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import { FileView } from "../Expenses/Filelist"; + +const DeliverChallanList = ({ purchaseId }) => { + const { data, isLoading, isError, error } = useDeliverChallane(purchaseId); + if (isLoading) { + return ( +
+ +
+ ); + } + + if (isError) { + return ( +
+ +
+ ); + } + if (!isLoading && data.length === 0) + return ( +
+

Not Yet

+
+ ); + + return ( +
+ {data.map((item) => ( +
+ {/* LEFT SIDE */} +
+
+

{item.deliveryChallanNumber}

+ + {formatUTCToLocalTime(item.deliveryChallanDate, true)} + +
+ +
+ Invoice: + {item.purchaseInvoice?.title} ( + {item.purchaseInvoice?.purchaseInvoiceUId}) +
+ +

+ Description:{" "} + {item.description || "-"} +

+ + {item.attachment?.preSignedUrl && ( + + )} +
+
+ ))} +
+ ); +}; + +export default DeliverChallanList; diff --git a/src/components/purchase/DeliveryChallane.jsx b/src/components/purchase/DeliveryChallane.jsx new file mode 100644 index 00000000..d8da640c --- /dev/null +++ b/src/components/purchase/DeliveryChallane.jsx @@ -0,0 +1,205 @@ +import React, { useState } from "react"; +import { + useAddDeliverChallan, + useDeliverChallane, +} from "../../hooks/usePurchase"; +import { SpinnerLoader } from "../common/Loader"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import DeliverChallanList from "./DeliverChallanList"; +import { AppFormController, useAppForm } from "../../hooks/appHooks/useAppForm"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { + DeliveryChallanDefaultValue, + DeliveryChallanSchema, +} from "./PurchaseSchema"; +import Label from "../common/Label"; +import DatePicker from "../common/DatePicker"; +import { useInvoiceAttachmentTypes } from "../../hooks/masterHook/useMaster"; +import SelectField from "../common/Forms/SelectField"; +import Filelist from "../Expenses/Filelist"; +import SingleFileUploader from "../common/SigleFileUploader"; +import { localToUtc } from "../../utils/appUtils"; + +const DeliveryChallane = ({ purchaseId }) => { + const [file, setFile] = useState(null); + + const { + register, + control, + handleSubmit, + setValue, + trigger, + watch, + reset, + formState: { errors }, + } = useAppForm({ + resolver: zodResolver(DeliveryChallanSchema), + defaultValues: DeliveryChallanDefaultValue, + }); + + const { data, isLoading } = useInvoiceAttachmentTypes(); + const { mutate: AddChallan, isPending } = useAddDeliverChallan(() => { + setFile(null); + setValue("attachment", null, { shouldValidate: false }); + reset({ + ...DeliveryChallanDefaultValue, + attachment: null, + }); + }); + const onSubmit = async (formData) => { + const valid = await trigger(); + if (!valid) return; + const { invoiceAttachmentTypeId, ...rest } = formData; + + const payload = { + ...rest, + attachment: formData.attachment + ? { + ...formData.attachment, + invoiceAttachmentTypeId, + } + : null, + purchaseInvoiceId: purchaseId, + deliveryChallanDate: formData.deliveryChallanDate + ? localToUtc(formData.deliveryChallanDate) + : null, + }; + + AddChallan(payload); + }; + const isUploaded = watch("attachment"); + const isDocumentType = watch("invoiceAttachmentTypeId"); + return ( +
+
Delivery Challans
+ +
+
+ {/* Challan Number */} +
+ + + + + {errors?.deliveryChallanNumber && ( + + {errors.deliveryChallanNumber.message} + + )} +
+ +
+ + + + + {errors?.deliveryChallanDate && ( + + {errors.deliveryChallanDate.message} + + )} +
+ +
+ + +