initially setup purchaseform

This commit is contained in:
pramod.mahajan 2025-11-25 20:01:11 +05:30
parent 5d773b0680
commit b8df8a2bde
8 changed files with 363 additions and 2 deletions

View File

@ -0,0 +1,117 @@
import React, { useState } from "react";
import { AppFormProvider, useAppForm } from "../../hooks/appHooks/useAppForm";
import { zodResolver } from "@hookform/resolvers/zod";
import {
defaultPurchaseValue,
PurchaseSchema,
getStepFields,
} from "./PurchaseSchema";
import { defaultJobValue } from "../ServiceProject/ServiceProjectSchema";
import PurchasePartyDetails from "./PurchasePartyDetails";
const ManagePurchase = () => {
const [activeTab, setActiveTab] = useState(0);
const [completedTabs, setCompletedTabs] = useState([]);
const newTenantConfig = [
{
name: "Contact Info",
icon: "bx bx-user bx-md",
subtitle: "Provide Contact Details",
component: <PurchasePartyDetails />,
},
{
name: "Organization",
icon: "bx bx-buildings bx-md",
subtitle: "Organization Details",
component: <div>Invoice & Transport Details</div>,
},
{
name: "SubScription",
icon: "bx bx-star bx-md",
component: <div>Payment & Financials</div>,
},
];
const purchaseOrder = useAppForm({
resolver: zodResolver(PurchaseSchema),
defaultJobValue: defaultPurchaseValue,
});
const getCurrentTrigger = () =>
activeTab === 2 ? subscriptionForm.trigger : tenantForm.trigger;
const handleNext = async () => {
const currentStepFields = getStepFields(activeTab);
const trigger = getCurrentTrigger();
const valid = await trigger(currentStepFields);
if (valid) {
setCompletedTabs((prev) => [...new Set([...prev, activeTab])]);
setActiveTab((prev) => Math.min(prev + 1, newTenantConfig.length - 1));
}
};
const handlePrev = () => {
setActiveTab((prev) => Math.max(prev - 1, 0));
};
const onsubmit = (formData) => {};
return (
<div
id="wizard-property-listing"
className="bs-stepper horizontically mt-2 b-secondry px-1 shadow-none border-0"
>
{/* <span className="fs-5">New Parchase</span> */}
<div className="bs-stepper-header text-start px-0">
{newTenantConfig
.filter((step) => step.name.toLowerCase() !== "congratulation")
.map((step, index) => {
const isActive = activeTab === index;
const isCompleted = completedTabs.includes(index);
return (
<React.Fragment key={step.name}>
<div
className={`step ${isActive ? "active" : ""} ${
isCompleted ? "crossed" : ""
}`}
data-target={`#step-${index}`}
>
<button
type="button"
className={`step-trigger ${isActive ? "active" : ""}`}
// onClick={() => setActiveTab(index)} // optional
>
<span className="bs-stepper-circle">
{isCompleted ? (
<i className="bx bx-check"></i>
) : (
<i className={step.icon}></i>
)}
</span>
<span className="bs-stepper-label">
<span className="bs-stepper-title">{step.name}</span>
<span className="bs-stepper-subtitle">
{step.subtitle}
</span>
</span>
</button>
</div>
{index < newTenantConfig.length - 1 && (
<div className="line text-primary"></div>
)}
</React.Fragment>
);
})}
</div>
<div className="bs-stepper-content py-2">
<AppFormProvider>
<form onSubmit={purchaseOrder.handleSubmit(onsubmit)}>
{newTenantConfig[activeTab].component}
</form>
</AppFormProvider>
</div>
</div>
);
};
export default ManagePurchase;

View File

@ -0,0 +1,31 @@
import React from "react";
import { useAppFormContext } from "../../hooks/appHooks/useAppForm";
const PurchasePartyDetails = ({ onNext }) => {
const {
register,
control,
trigger,
formState: { errors },
} = useAppFormContext();
const handleNext = async () => {
const valid = await trigger([
"title",
"projectId",
"organizationId",
"supplier",
"billingAddress",
"shippingAddress",
"purchaseOrderNumber",
"purchaseOrderDate",
"porformaInvoiceNo",
]);
if (valid) {
onNext();
}
};
return <div className="row"></div>;
};
export default PurchasePartyDetails;

View File

@ -0,0 +1,11 @@
import React from 'react'
const PurchasePaymentDetails = () => {
return (
<div>
</div>
)
}
export default PurchasePaymentDetails

View File

@ -0,0 +1,127 @@
import { z } from "zod";
export const AttachmentSchema = (allowedContentType, maxSizeAllowedInMB) => {
const allowedTypes = normalizeAllowedContentTypes(allowedContentType);
return z.object({
fileName: z.string().min(1, { message: "File name is required" }),
base64Data: z.string().min(1, { message: "File data is required" }),
contentType: z
.string()
.min(1, { message: "MIME type is required" })
.refine(
(val) => (allowedTypes.length ? allowedTypes.includes(val) : true),
{
message: `File type must be one of: ${allowedTypes.join(", ")}`,
}
),
fileSize: z
.number()
.int()
.nonnegative("fileSize must be ≥ 0")
.max(
(maxSizeAllowedInMB ?? 25) * 1024 * 1024,
`fileSize must be ≤ ${maxSizeAllowedInMB ?? 25}MB`
),
description: z.string().optional().default(""),
isActive: z.boolean(),
});
};
export const PurchaseSchema = z.object({
title: z.string().min(1, { message: "Title is required" }),
projectId: z.string().min(1, { message: "Project is required" }),
organizationId: z.string().min(1, { message: "Organization is required" }),
billingAddress: z.string().min(1, { message: "Address is required" }),
shippingAddress: z.string().min(1, { message: "Address is required" }),
purchaseOrderNumber: z.string().nullable(),
purchaseOrderDate: z.string().nullable(),
supplier: z.string().min(1, { message: "Supplier is required" }),
porformaInvoiceNo: z.string().nullable(),
// Supplier Details
invoiceNo: z.string().min(1, { message: "Invoice No is required" }),
invoiceDate: z.string().min(1, { message: "Date is required" }),
ewayBillNo: z.string().min(1, { message: "E-Way Bill No is required" }),
ewayBillDate: z.string().min(1, { message: "E-Way Bill Date is required" }),
irnNo: z.string().min(1, { message: "IRN is required" }),
ackDate: z.string().min(1, { message: "Date is required" }),
ackNo: z.string().min(1, { message: "acknowledgement No is required" }),
// Payment Detail
baseAmount: z.string().min(1, { message: "Base amount is required" }),
taxAmount: z.string().min(1, { message: "Tax amount is required" }),
totalAmount: z.string().min(1, { message: "Total amount is required" }),
paymentDueDate: z.string().nullable(),
TransportCharges: z.string().nullable(),
description: z.string().min(1, { message: "description is required" }),
});
// 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: null,
projectId: null,
organizationId: null,
billingAddress: null,
shippingAddress: null,
purchaseOrderNumber: null,
purchaseOrderDate: null,
supplier: null,
porformaInvoiceNo: null,
invoiceNo: null,
invoiceDate: null,
ewayBillNo: null,
ewayBillDate: null,
irnNo: null,
ackDate: null,
ackNo: null,
baseAmount: null,
taxAmount: null,
totalAmount: null,
paymentDueDate: null,
TransportCharges: null,
description: null,
};
export const getStepFields = (stepIndex) => {
const stepFieldMap = {
0: [
"title",
"projectId",
"organizationId",
"supplier",
"billingAddress",
"shippingAddress",
"purchaseOrderNumber",
"purchaseOrderDate",
"porformaInvoiceNo",
],
1: [
"invoiceNo",
"invoiceDate",
"ewayBillNo",
"ewayBillDate",
"irnNo",
"ackNo",
"taxId",
"ackDate",
],
2: [
"baseAmount",
"taxAmount",
"totalAmount",
"TransportCharges",
"paymentDueDate",
"description",
],
};
return stepFieldMap[stepIndex] || [];
};

View File

@ -0,0 +1,11 @@
import React from 'react'
const PurchaseTansportDetails = () => {
return (
<div>
</div>
)
}
export default PurchaseTansportDetails

View File

@ -1,6 +1,6 @@
import { useForm, Controller,FormProvider } from "react-hook-form"; import { useForm, Controller, FormProvider, useFormContext } from "react-hook-form";
export const useAppForm = (config) => useForm(config); export const useAppForm = (config) => useForm(config);
export const AppFormProvider = FormProvider; export const AppFormProvider = FormProvider;
export const AppFormController = Controller; export const AppFormController = Controller;
export const useAppFormContext = useFormContext;

View File

@ -0,0 +1,57 @@
import React, { createContext, useContext, useState } from "react";
import Breadcrumb from "../../components/common/Breadcrumb";
import showToast from "../../services/toastService";
import GlobalModel from "../../components/common/GlobalModel";
import ManagePurchase from "../../components/purchase/ManagePurchase";
export const PurchaseContext = createContext();
export const usePurchaseContext = () => {
let context = useContext(PurchaseContext);
if (!context) {
showToast("Please use Innne cntext", "warning");
window.location = "/dashboard";
}
};
const PurchasePage = () => {
const [addePurchase, setAddedPurchase] = useState(false);
const contextValue = {};
return (
<PurchaseContext.Provider value={contextValue}>
<div className="container-fluid">
<Breadcrumb
data={[
{ label: "Home", link: "/dashboard" },
{ label: "Procurement & Inventory", link: "/purchase-invoice" },
{ label: "Purchase" },
]}
/>
<div className="card">
<div className="row p-2">
<di className="col-12 text-end">
<button
className="btn btn-sm btn-primary"
onClick={() => setAddedPurchase(true)}
>
<i className="bx bx-plus-circle me-2"></i>Add
</button>
</di>
</div>
</div>
{/* MOdals */}
<GlobalModel
isOpen={addePurchase}
size="lg"
closeModal={() => setAddedPurchase(false)}
>
<ManagePurchase onClose={() => () => setAddedPurchase(false)} />
</GlobalModel>
</div>
</PurchaseContext.Provider>
);
};
export default PurchasePage;

View File

@ -62,6 +62,7 @@ import AdvancePaymentPage from "../pages/AdvancePayment/AdvancePaymentPage";
import ServiceProjectDetail from "../pages/ServiceProject/ServiceProjectDetail"; import ServiceProjectDetail from "../pages/ServiceProject/ServiceProjectDetail";
import ManageJob from "../components/ServiceProject/ServiceProjectJob/ManageJob"; import ManageJob from "../components/ServiceProject/ServiceProjectJob/ManageJob";
import AdvancePaymentPage1 from "../pages/AdvancePayment/AdvancePaymentPage1"; import AdvancePaymentPage1 from "../pages/AdvancePayment/AdvancePaymentPage1";
import PurchasePage from "../pages/purchase/PurchasePage";
const router = createBrowserRouter( const router = createBrowserRouter(
[ [
{ {
@ -113,6 +114,8 @@ const router = createBrowserRouter(
{ path: "/activities/task", element: <TaskPlannng /> }, { path: "/activities/task", element: <TaskPlannng /> },
{ path: "/activities/reports", element: <Reports /> }, { path: "/activities/reports", element: <Reports /> },
{ path: "/gallary", element: <ImageGalleryPage /> }, { path: "/gallary", element: <ImageGalleryPage /> },
// Finance
{ path: "/expenses/:status?/:project?", element: <ExpensePage /> }, { path: "/expenses/:status?/:project?", element: <ExpensePage /> },
{ path: "/expenses", element: <ExpensePage /> }, { path: "/expenses", element: <ExpensePage /> },
{ path: "/payment-request", element: <PaymentRequestPage /> }, { path: "/payment-request", element: <PaymentRequestPage /> },
@ -120,6 +123,10 @@ const router = createBrowserRouter(
{ path: "/advance-payment", element: <AdvancePaymentPage1 /> }, { path: "/advance-payment", element: <AdvancePaymentPage1 /> },
{ path: "/advance-payment/:employeeId", element: <AdvancePaymentPage /> }, { path: "/advance-payment/:employeeId", element: <AdvancePaymentPage /> },
{ path: "/collection", element: <CollectionPage /> }, { path: "/collection", element: <CollectionPage /> },
// Purchases and Inventory
{ path: "/purchase-invoice", element: <PurchasePage /> },
// Administration
{ path: "/masters", element: <MasterPage /> }, { path: "/masters", element: <MasterPage /> },
{ path: "/tenants", element: <TenantPage /> }, { path: "/tenants", element: <TenantPage /> },
{ path: "/tenants/new-tenant", element: <CreateTenant /> }, { path: "/tenants/new-tenant", element: <CreateTenant /> },