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"
|
role="button"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
removeFile(expenseToEdit ? file.documentId ?? idx : idx);
|
removeFile(expenseToEdit ? file.documentId ?? file.tempId ?? idx : file.tempId ?? idx);
|
||||||
}}
|
}}
|
||||||
></i>
|
></i>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@ -1,44 +1,53 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useMemo, useCallback, useState } from "react";
|
||||||
import { AppFormProvider, useAppForm } from "../../hooks/appHooks/useAppForm";
|
import { AppFormProvider, useAppForm } from "../../hooks/appHooks/useAppForm";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defaultPurchaseValue,
|
defaultPurchaseValue,
|
||||||
PurchaseSchema,
|
PurchaseSchema,
|
||||||
getStepFields,
|
getStepFields,
|
||||||
} from "./PurchaseSchema";
|
} from "./PurchaseSchema";
|
||||||
|
|
||||||
import PurchasePartyDetails from "./PurchasePartyDetails";
|
import PurchasePartyDetails from "./PurchasePartyDetails";
|
||||||
import PurchaseTransportDetails from "./PurchaseTransportDetails";
|
import PurchaseTransportDetails from "./PurchaseTransportDetails";
|
||||||
import PurchasePaymentDetails from "./PurchasePaymentDetails";
|
import PurchasePaymentDetails from "./PurchasePaymentDetails";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useCreatePurchaseInvoice,
|
useCreatePurchaseInvoice,
|
||||||
usePurchase,
|
usePurchase,
|
||||||
useUpdatePurchaseInvoice,
|
useUpdatePurchaseInvoice,
|
||||||
} from "../../hooks/usePurchase";
|
} from "../../hooks/usePurchase";
|
||||||
|
import { error } from "pdf-lib";
|
||||||
|
|
||||||
const ManagePurchase = ({ onClose, purchaseId }) => {
|
const ManagePurchase = ({ onClose, purchaseId }) => {
|
||||||
const { data } = usePurchase(purchaseId);
|
const { data } = usePurchase(purchaseId);
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
const [completedTabs, setCompletedTabs] = useState([]);
|
const [completedTabs, setCompletedTabs] = useState([]);
|
||||||
|
|
||||||
const stepsConfig = [
|
const stepsConfig = useMemo(
|
||||||
{
|
() => [
|
||||||
name: "Party Details",
|
{
|
||||||
icon: "bx bx-user bx-md",
|
name: "Party Details",
|
||||||
subtitle: "Supplier & project information",
|
icon: "bx bx-user bx-md",
|
||||||
component: <PurchasePartyDetails purchase={data} />,
|
subtitle: "Supplier & project information",
|
||||||
},
|
component: <PurchasePartyDetails purchase={data} />,
|
||||||
{
|
},
|
||||||
name: "Invoice & Transport",
|
{
|
||||||
icon: "bx bx-receipt bx-md",
|
name: "Invoice & Transport",
|
||||||
subtitle: "Invoice, eWay bill & transport info",
|
icon: "bx bx-receipt bx-md",
|
||||||
component: <PurchaseTransportDetails />,
|
subtitle: "Invoice, eWay bill & transport info",
|
||||||
},
|
component: <PurchaseTransportDetails />,
|
||||||
{
|
},
|
||||||
name: "Payment Details",
|
{
|
||||||
icon: "bx bx-credit-card bx-md",
|
name: "Payment Details",
|
||||||
subtitle: "Amount, tax & due date",
|
icon: "bx bx-credit-card bx-md",
|
||||||
component: <PurchasePaymentDetails purchaseId={purchaseId} />,
|
subtitle: "Amount, tax & due date",
|
||||||
},
|
component: <PurchasePaymentDetails purchaseId={purchaseId} />,
|
||||||
];
|
},
|
||||||
|
],
|
||||||
|
[data, purchaseId]
|
||||||
|
);
|
||||||
|
|
||||||
const purchaseOrder = useAppForm({
|
const purchaseOrder = useAppForm({
|
||||||
resolver: zodResolver(PurchaseSchema),
|
resolver: zodResolver(PurchaseSchema),
|
||||||
@ -47,55 +56,63 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
|
|||||||
shouldUnregister: false,
|
shouldUnregister: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const { reset, formState } = purchaseOrder;
|
||||||
reset,
|
|
||||||
formState: { errors },
|
|
||||||
} = purchaseOrder;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (purchaseId && data) {
|
if (!purchaseId || !data) return;
|
||||||
reset({
|
|
||||||
...data,
|
reset({
|
||||||
title: data.title,
|
...data,
|
||||||
projectId: data.project.id,
|
projectId: data?.project?.id,
|
||||||
organizationId: data.organization.id,
|
organizationId: data?.organization?.id,
|
||||||
supplierId: data.supplier.id,
|
supplierId: data?.supplier?.id,
|
||||||
attachments: data.attachments
|
invoiceAttachmentTypeId: null,
|
||||||
? data?.attachments?.map((doc) => ({
|
attachments:
|
||||||
fileName: doc.fileName,
|
data?.attachments?.map((doc) => ({
|
||||||
base64Data: null,
|
fileName: doc.fileName,
|
||||||
contentType: doc.contentType,
|
base64Data: null,
|
||||||
documentId: doc.documentId,
|
contentType: doc.contentType,
|
||||||
invoiceAttachmentTypeId:doc.invoiceAttachmentTypeId ?? null,
|
documentId: doc.documentId,
|
||||||
fileSize: 0,
|
invoiceAttachmentTypeId: doc.invoiceAttachmentType?.id ?? null,
|
||||||
description: "",
|
fileSize: 0,
|
||||||
preSignedUrl: doc.preSignedUrl,
|
description: "",
|
||||||
isActive: doc.isActive ?? true,
|
preSignedUrl: doc.preSignedUrl,
|
||||||
}))
|
isActive: doc.isActive ?? true,
|
||||||
: [],
|
})) || [],
|
||||||
});
|
});
|
||||||
setCompletedTabs([0, 1, 2]);
|
|
||||||
}
|
setCompletedTabs([0, 1, 2]);
|
||||||
|
}, [purchaseId, data, reset]);
|
||||||
|
|
||||||
|
const handleNext = useCallback(async () => {
|
||||||
|
const fields = getStepFields(activeTab);
|
||||||
|
const valid = await purchaseOrder.trigger(fields);
|
||||||
|
|
||||||
|
if (!valid) return;
|
||||||
|
|
||||||
|
setCompletedTabs((prev) => [...new Set([...prev, activeTab])]);
|
||||||
|
setActiveTab((prev) => Math.min(prev + 1, stepsConfig.length - 1));
|
||||||
|
}, [activeTab, purchaseOrder, stepsConfig.length]);
|
||||||
|
|
||||||
|
const handlePrev = useCallback(() => {
|
||||||
|
setActiveTab((prev) => Math.max(prev - 1, 0));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleNext = async (e) => {
|
const generatePatchOps = useCallback(
|
||||||
e.preventDefault();
|
(formData) => {
|
||||||
e.stopPropagation();
|
const { dirtyFields } = formState;
|
||||||
|
|
||||||
const currentStepFields = getStepFields(activeTab);
|
return Object.keys(dirtyFields)
|
||||||
const valid = await purchaseOrder.trigger(currentStepFields);
|
.filter((key) => key !== "invoiceAttachmentTypeId")
|
||||||
|
.map((key) => ({
|
||||||
if (valid) {
|
operationType: 0,
|
||||||
setCompletedTabs((prev) => [...new Set([...prev, activeTab])]);
|
path: `/${key}`,
|
||||||
setActiveTab((prev) => Math.min(prev + 1, stepsConfig.length - 1));
|
op: "replace",
|
||||||
}
|
value: formData[key],
|
||||||
};
|
}));
|
||||||
|
},
|
||||||
const handlePrev = (e) => {
|
[formState]
|
||||||
e.preventDefault();
|
);
|
||||||
e.stopPropagation();
|
|
||||||
setActiveTab((prev) => Math.max(prev - 1, 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
const { mutate: CreateInvoice, isPending } = useCreatePurchaseInvoice(() => {
|
const { mutate: CreateInvoice, isPending } = useCreatePurchaseInvoice(() => {
|
||||||
reset();
|
reset();
|
||||||
@ -108,44 +125,22 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
|
|||||||
onClose();
|
onClose();
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = (formData) => {
|
const onSubmit = useCallback(
|
||||||
if (activeTab !== 2) {
|
(formData) => {
|
||||||
console.warn("Submit blocked - not on last step");
|
if (activeTab !== 2) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dirtyFields = purchaseOrder.formState.dirtyFields;
|
if (purchaseId) {
|
||||||
|
const payload = generatePatchOps(formData);
|
||||||
if (purchaseId) {
|
updatePurchase({ purchaseId, payload });
|
||||||
const changedData = Object.keys(dirtyFields).reduce((acc, key) => {
|
} else {
|
||||||
debugger;
|
CreateInvoice(formData);
|
||||||
if (dirtyFields[key] && key !== "invoiceAttachmentTypeId") {
|
}
|
||||||
acc.push({
|
},
|
||||||
operationType: 0,
|
[activeTab, purchaseId, generatePatchOps, updatePurchase, CreateInvoice]
|
||||||
path: `/${key}`,
|
);
|
||||||
op: "replace",
|
|
||||||
from: null,
|
|
||||||
value: formData[key],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
console.log(changedData)
|
|
||||||
// updatePurchase({
|
|
||||||
// purchaseId,
|
|
||||||
// payload: changedData,
|
|
||||||
// });
|
|
||||||
} else {
|
|
||||||
CreateInvoice(formData);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
console.log(errors)
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="bs-stepper horizontically mt-2 b-secondry shadow-none border-0">
|
||||||
id="wizard-property-listing"
|
{/* --- Steps Header --- */}
|
||||||
className="bs-stepper horizontically mt-2 b-secondry shadow-none border-0"
|
|
||||||
>
|
|
||||||
{/* Header */}
|
|
||||||
<div className="bs-stepper-header text-start px-0 py-1">
|
<div className="bs-stepper-header text-start px-0 py-1">
|
||||||
{stepsConfig.map((step, index) => {
|
{stepsConfig.map((step, index) => {
|
||||||
const isActive = activeTab === index;
|
const isActive = activeTab === index;
|
||||||
@ -158,7 +153,11 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
|
|||||||
isCompleted ? "crossed" : ""
|
isCompleted ? "crossed" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<button type="button" className="step-trigger">
|
<button
|
||||||
|
type="button"
|
||||||
|
className="step-trigger"
|
||||||
|
onClick={() => purchaseId && setActiveTab(index)}
|
||||||
|
>
|
||||||
<span className="bs-stepper-circle">
|
<span className="bs-stepper-circle">
|
||||||
{isCompleted ? (
|
{isCompleted ? (
|
||||||
<i className="bx bx-check"></i>
|
<i className="bx bx-check"></i>
|
||||||
@ -182,28 +181,18 @@ const ManagePurchase = ({ onClose, purchaseId }) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* --- Form Content --- */}
|
||||||
<div className="bs-stepper-content py-2 px-3">
|
<div className="bs-stepper-content py-2 px-3">
|
||||||
<AppFormProvider {...purchaseOrder}>
|
<AppFormProvider {...purchaseOrder}>
|
||||||
<form
|
<form
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) =>
|
||||||
if (e.key === "Enter" && activeTab !== 2) {
|
e.key === "Enter" && activeTab !== 2 && e.preventDefault()
|
||||||
e.preventDefault();
|
}
|
||||||
}
|
onSubmit={purchaseOrder.handleSubmit(onSubmit)}
|
||||||
}}
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (activeTab !== 2) {
|
|
||||||
console.warn("BLOCKED SUBMIT on step:", activeTab);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
purchaseOrder.handleSubmit(onSubmit)(e);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{stepsConfig[activeTab].component}
|
{stepsConfig[activeTab].component}
|
||||||
|
|
||||||
|
{/* Buttons */}
|
||||||
<div className="d-flex justify-content-between mt-4">
|
<div className="d-flex justify-content-between mt-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -74,7 +74,7 @@ const PurchaseList = ({ searchString }) => {
|
|||||||
{col.render ? col.render(item) : item[col.key] || "NA"}
|
{col.render ? col.render(item) : item[col.key] || "NA"}
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
<td>
|
<td >
|
||||||
<div className="dropdown z-2">
|
<div className="dropdown z-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -37,6 +37,13 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
|
|||||||
}, [baseAmount, taxAmount, setValue]);
|
}, [baseAmount, taxAmount, setValue]);
|
||||||
const invoiceAttachmentType = watch("invoiceAttachmentTypeId");
|
const invoiceAttachmentType = watch("invoiceAttachmentTypeId");
|
||||||
const files = watch("attachments");
|
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 onFileChange = async (e) => {
|
||||||
const newFiles = Array.from(e.target.files);
|
const newFiles = Array.from(e.target.files);
|
||||||
if (newFiles.length === 0) return;
|
if (newFiles.length === 0) return;
|
||||||
@ -54,6 +61,8 @@ const PurchasePaymentDetails = ({ purchaseId = null }) => {
|
|||||||
description: "",
|
description: "",
|
||||||
isActive: true,
|
isActive: true,
|
||||||
invoiceAttachmentTypeId: invoiceAttachmentType,
|
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 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) {
|
return [file];
|
||||||
const newFiles = files.map((file, i) => {
|
});
|
||||||
if (i !== index) return file;
|
|
||||||
|
|
||||||
return {
|
setValue("attachments", updated, {
|
||||||
...file,
|
shouldDirty: true,
|
||||||
isActive: false,
|
shouldValidate: true,
|
||||||
};
|
});
|
||||||
});
|
|
||||||
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 = {
|
export const defaultPurchaseValue = {
|
||||||
title: "",
|
title: "",
|
||||||
|
|||||||
@ -26,13 +26,13 @@ export const PurchaseColumn = [
|
|||||||
{
|
{
|
||||||
key: "project",
|
key: "project",
|
||||||
label: "Project",
|
label: "Project",
|
||||||
className: "text-start d-none d-sm-table-cell",
|
className: "text-start ",
|
||||||
render: (item) => <span>{item?.project?.name || "NA"}</span>,
|
render: (item) => <span>{item?.project?.name || "NA"}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "supplier",
|
key: "supplier",
|
||||||
label: "Supplier",
|
label: "Supplier",
|
||||||
className: "text-start d-none d-sm-table-cell",
|
className: "text-start ",
|
||||||
render: (item) => <span>{item?.supplier?.name || "NA"}</span>,
|
render: (item) => <span>{item?.supplier?.name || "NA"}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -49,7 +49,7 @@ const PurchasePage = () => {
|
|||||||
/>
|
/>
|
||||||
<div className="card px-sm-4 my-3">
|
<div className="card px-sm-4 my-3">
|
||||||
<div className="row p-2">
|
<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">
|
<label className="mb-0">
|
||||||
<input
|
<input
|
||||||
@ -62,7 +62,7 @@ const PurchasePage = () => {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<di className="col-6 text-end">
|
<di className="col-sm-6 text-end">
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-primary"
|
className="btn btn-sm btn-primary"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user