fixed attachment remove and update operation
This commit is contained in:
parent
cb9d263730
commit
6fa2cc4ef0
@ -41,7 +41,7 @@ const Filelist = ({ files, removeFile, expenseToEdit, sm = 6, md = 4 }) => {
|
||||
role="button"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
removeFile(expenseToEdit ? file.documentId ?? idx : idx);
|
||||
removeFile(expenseToEdit ? file.documentId ?? file.tempId ?? idx : file.tempId ?? idx);
|
||||
}}
|
||||
></i>
|
||||
</Tooltip>
|
||||
|
||||
@ -1,25 +1,32 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useMemo, useCallback, useState } from "react";
|
||||
import { AppFormProvider, useAppForm } from "../../hooks/appHooks/useAppForm";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
|
||||
import {
|
||||
defaultPurchaseValue,
|
||||
PurchaseSchema,
|
||||
getStepFields,
|
||||
} from "./PurchaseSchema";
|
||||
|
||||
import PurchasePartyDetails from "./PurchasePartyDetails";
|
||||
import PurchaseTransportDetails from "./PurchaseTransportDetails";
|
||||
import PurchasePaymentDetails from "./PurchasePaymentDetails";
|
||||
|
||||
import {
|
||||
useCreatePurchaseInvoice,
|
||||
usePurchase,
|
||||
useUpdatePurchaseInvoice,
|
||||
} from "../../hooks/usePurchase";
|
||||
import { error } from "pdf-lib";
|
||||
|
||||
const ManagePurchase = ({ onClose, purchaseId }) => {
|
||||
const { data } = usePurchase(purchaseId);
|
||||
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
const [completedTabs, setCompletedTabs] = useState([]);
|
||||
|
||||
const stepsConfig = [
|
||||
const stepsConfig = useMemo(
|
||||
() => [
|
||||
{
|
||||
name: "Party Details",
|
||||
icon: "bx bx-user bx-md",
|
||||
@ -38,7 +45,9 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
|
||||
subtitle: "Amount, tax & due date",
|
||||
component: <PurchasePaymentDetails purchaseId={purchaseId} />,
|
||||
},
|
||||
];
|
||||
],
|
||||
[data, purchaseId]
|
||||
);
|
||||
|
||||
const purchaseOrder = useAppForm({
|
||||
resolver: zodResolver(PurchaseSchema),
|
||||
@ -47,55 +56,63 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
|
||||
shouldUnregister: false,
|
||||
});
|
||||
|
||||
const {
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = purchaseOrder;
|
||||
const { reset, formState } = purchaseOrder;
|
||||
|
||||
useEffect(() => {
|
||||
if (purchaseId && data) {
|
||||
if (!purchaseId || !data) return;
|
||||
|
||||
reset({
|
||||
...data,
|
||||
title: data.title,
|
||||
projectId: data.project.id,
|
||||
organizationId: data.organization.id,
|
||||
supplierId: data.supplier.id,
|
||||
attachments: data.attachments
|
||||
? data?.attachments?.map((doc) => ({
|
||||
projectId: data?.project?.id,
|
||||
organizationId: data?.organization?.id,
|
||||
supplierId: data?.supplier?.id,
|
||||
invoiceAttachmentTypeId: null,
|
||||
attachments:
|
||||
data?.attachments?.map((doc) => ({
|
||||
fileName: doc.fileName,
|
||||
base64Data: null,
|
||||
contentType: doc.contentType,
|
||||
documentId: doc.documentId,
|
||||
invoiceAttachmentTypeId:doc.invoiceAttachmentTypeId ?? null,
|
||||
invoiceAttachmentTypeId: doc.invoiceAttachmentType?.id ?? null,
|
||||
fileSize: 0,
|
||||
description: "",
|
||||
preSignedUrl: doc.preSignedUrl,
|
||||
isActive: doc.isActive ?? true,
|
||||
}))
|
||||
: [],
|
||||
})) || [],
|
||||
});
|
||||
|
||||
setCompletedTabs([0, 1, 2]);
|
||||
}
|
||||
}, []);
|
||||
}, [purchaseId, data, reset]);
|
||||
|
||||
const handleNext = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const handleNext = useCallback(async () => {
|
||||
const fields = getStepFields(activeTab);
|
||||
const valid = await purchaseOrder.trigger(fields);
|
||||
|
||||
const currentStepFields = getStepFields(activeTab);
|
||||
const valid = await purchaseOrder.trigger(currentStepFields);
|
||||
if (!valid) return;
|
||||
|
||||
if (valid) {
|
||||
setCompletedTabs((prev) => [...new Set([...prev, activeTab])]);
|
||||
setActiveTab((prev) => Math.min(prev + 1, stepsConfig.length - 1));
|
||||
}
|
||||
};
|
||||
}, [activeTab, purchaseOrder, stepsConfig.length]);
|
||||
|
||||
const handlePrev = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const handlePrev = useCallback(() => {
|
||||
setActiveTab((prev) => Math.max(prev - 1, 0));
|
||||
};
|
||||
}, []);
|
||||
|
||||
const generatePatchOps = useCallback(
|
||||
(formData) => {
|
||||
const { dirtyFields } = formState;
|
||||
|
||||
return Object.keys(dirtyFields)
|
||||
.filter((key) => key !== "invoiceAttachmentTypeId")
|
||||
.map((key) => ({
|
||||
operationType: 0,
|
||||
path: `/${key}`,
|
||||
op: "replace",
|
||||
value: formData[key],
|
||||
}));
|
||||
},
|
||||
[formState]
|
||||
);
|
||||
|
||||
const { mutate: CreateInvoice, isPending } = useCreatePurchaseInvoice(() => {
|
||||
reset();
|
||||
@ -108,44 +125,22 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
|
||||
onClose();
|
||||
});
|
||||
|
||||
const onSubmit = (formData) => {
|
||||
if (activeTab !== 2) {
|
||||
console.warn("Submit blocked - not on last step");
|
||||
return;
|
||||
}
|
||||
|
||||
const dirtyFields = purchaseOrder.formState.dirtyFields;
|
||||
const onSubmit = useCallback(
|
||||
(formData) => {
|
||||
if (activeTab !== 2) return;
|
||||
|
||||
if (purchaseId) {
|
||||
const changedData = Object.keys(dirtyFields).reduce((acc, key) => {
|
||||
debugger;
|
||||
if (dirtyFields[key] && key !== "invoiceAttachmentTypeId") {
|
||||
acc.push({
|
||||
operationType: 0,
|
||||
path: `/${key}`,
|
||||
op: "replace",
|
||||
from: null,
|
||||
value: formData[key],
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
console.log(changedData)
|
||||
// updatePurchase({
|
||||
// purchaseId,
|
||||
// payload: changedData,
|
||||
// });
|
||||
const payload = generatePatchOps(formData);
|
||||
updatePurchase({ purchaseId, payload });
|
||||
} else {
|
||||
CreateInvoice(formData);
|
||||
}
|
||||
};
|
||||
console.log(errors)
|
||||
},
|
||||
[activeTab, purchaseId, generatePatchOps, updatePurchase, CreateInvoice]
|
||||
);
|
||||
return (
|
||||
<div
|
||||
id="wizard-property-listing"
|
||||
className="bs-stepper horizontically mt-2 b-secondry shadow-none border-0"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="bs-stepper horizontically mt-2 b-secondry shadow-none border-0">
|
||||
{/* --- Steps Header --- */}
|
||||
<div className="bs-stepper-header text-start px-0 py-1">
|
||||
{stepsConfig.map((step, index) => {
|
||||
const isActive = activeTab === index;
|
||||
@ -158,7 +153,11 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
|
||||
isCompleted ? "crossed" : ""
|
||||
}`}
|
||||
>
|
||||
<button type="button" className="step-trigger">
|
||||
<button
|
||||
type="button"
|
||||
className="step-trigger"
|
||||
onClick={() => purchaseId && setActiveTab(index)}
|
||||
>
|
||||
<span className="bs-stepper-circle">
|
||||
{isCompleted ? (
|
||||
<i className="bx bx-check"></i>
|
||||
@ -182,28 +181,18 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
{/* --- Form Content --- */}
|
||||
<div className="bs-stepper-content py-2 px-3">
|
||||
<AppFormProvider {...purchaseOrder}>
|
||||
<form
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" && activeTab !== 2) {
|
||||
e.preventDefault();
|
||||
onKeyDown={(e) =>
|
||||
e.key === "Enter" && activeTab !== 2 && e.preventDefault()
|
||||
}
|
||||
}}
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (activeTab !== 2) {
|
||||
console.warn("BLOCKED SUBMIT on step:", activeTab);
|
||||
return;
|
||||
}
|
||||
|
||||
purchaseOrder.handleSubmit(onSubmit)(e);
|
||||
}}
|
||||
onSubmit={purchaseOrder.handleSubmit(onSubmit)}
|
||||
>
|
||||
{stepsConfig[activeTab].component}
|
||||
|
||||
{/* Buttons */}
|
||||
<div className="d-flex justify-content-between mt-4">
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@ -37,6 +37,13 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
|
||||
}, [baseAmount, taxAmount, setValue]);
|
||||
const invoiceAttachmentType = watch("invoiceAttachmentTypeId");
|
||||
const files = watch("attachments");
|
||||
const toBase64 = (file) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result.split(",")[1]);
|
||||
reader.onerror = (error) => reject(error);
|
||||
});
|
||||
const onFileChange = async (e) => {
|
||||
const newFiles = Array.from(e.target.files);
|
||||
if (newFiles.length === 0) return;
|
||||
@ -54,6 +61,8 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
|
||||
description: "",
|
||||
isActive: true,
|
||||
invoiceAttachmentTypeId: invoiceAttachmentType,
|
||||
isNew: true, // <-- temp
|
||||
tempId: crypto.randomUUID(), // temp - id
|
||||
};
|
||||
})
|
||||
);
|
||||
@ -75,36 +84,25 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const toBase64 = (file) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result.split(",")[1]);
|
||||
reader.onerror = (error) => reject(error);
|
||||
});
|
||||
const removeFile = (index) => {
|
||||
const updated = files.flatMap((file, i) => {
|
||||
// NEW FILE → remove completely
|
||||
if (file.isNew && file.tempId === index) {
|
||||
return []; // remove from array
|
||||
}
|
||||
|
||||
// EXISTING FILE → mark inactive
|
||||
if (!file.isNew && file.documentId === index) {
|
||||
return [{ ...file, isActive: false }];
|
||||
}
|
||||
|
||||
if (purchaseId) {
|
||||
const newFiles = files.map((file, i) => {
|
||||
if (i !== index) return file;
|
||||
|
||||
return {
|
||||
...file,
|
||||
isActive: false,
|
||||
};
|
||||
return [file];
|
||||
});
|
||||
|
||||
console.log("OLD:", files);
|
||||
console.log("NEW:", newFiles);
|
||||
|
||||
// setValue("attachments", newFiles, { shouldValidate: true });
|
||||
const new_Files = files.filter((_, i) => i !== index);
|
||||
setValue("attachments", new_Files, { shouldValidate: true });
|
||||
} else {
|
||||
const newFiles = files.filter((_, i) => i !== index);
|
||||
setValue("attachments", newFiles, { shouldValidate: true });
|
||||
}
|
||||
setValue("attachments", updated, {
|
||||
shouldDirty: true,
|
||||
shouldValidate: true,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -63,11 +63,6 @@ export const PurchaseSchema = z.object({
|
||||
|
||||
});
|
||||
|
||||
// deliveryChallanNo: z
|
||||
// .string()
|
||||
// .min(1, { message: "Delivery Challan No is required" }),
|
||||
// deliveryDate: z.string().min(1, { message: "Delevery Date is required" }),
|
||||
// shippingAddress: z.string().min(1, { message: "Delevery Date is required" }),
|
||||
|
||||
export const defaultPurchaseValue = {
|
||||
title: "",
|
||||
|
||||
@ -26,13 +26,13 @@ export const PurchaseColumn = [
|
||||
{
|
||||
key: "project",
|
||||
label: "Project",
|
||||
className: "text-start d-none d-sm-table-cell",
|
||||
className: "text-start ",
|
||||
render: (item) => <span>{item?.project?.name || "NA"}</span>,
|
||||
},
|
||||
{
|
||||
key: "supplier",
|
||||
label: "Supplier",
|
||||
className: "text-start d-none d-sm-table-cell",
|
||||
className: "text-start ",
|
||||
render: (item) => <span>{item?.supplier?.name || "NA"}</span>,
|
||||
},
|
||||
{
|
||||
|
||||
@ -49,7 +49,7 @@ const PurchasePage = () => {
|
||||
/>
|
||||
<div className="card px-sm-4 my-3">
|
||||
<div className="row p-2">
|
||||
<div className="col-12 col-md-6 text-start">
|
||||
<div className="col-12 col-sm-6 text-start">
|
||||
{" "}
|
||||
<label className="mb-0">
|
||||
<input
|
||||
@ -62,7 +62,7 @@ const PurchasePage = () => {
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<di className="col-6 text-end">
|
||||
<di className="col-sm-6 text-end">
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={() =>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user