done
This commit is contained in:
parent
98707b20fc
commit
14fd066fe0
|
@ -0,0 +1,74 @@
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
// Schema for property tax information
|
||||||
|
const propertyTaxInfoSchema = mongoose.Schema({
|
||||||
|
propertytaxowned: { type: String },
|
||||||
|
ownedyear: { type: String },
|
||||||
|
taxassessed: { type: String },
|
||||||
|
taxyear: { type: String },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Schema for images
|
||||||
|
const imageSchema = mongoose.Schema({
|
||||||
|
title: { type: String }, // Title of the image
|
||||||
|
file: { type: String }, // Uploaded image URL
|
||||||
|
});
|
||||||
|
|
||||||
|
const propertySchema = mongoose.Schema({
|
||||||
|
address: { type: String, required: true },
|
||||||
|
city: { type: String, required: true },
|
||||||
|
state: { type: String, required: true },
|
||||||
|
county: { type: String, required: true },
|
||||||
|
zip: { type: String, required: true },
|
||||||
|
parcel: { type: String, required: true },
|
||||||
|
subdivision: { type: String, required: true },
|
||||||
|
legal: { type: String, required: true },
|
||||||
|
costpersqft: { type: String, required: true },
|
||||||
|
propertyType: { type: String, required: true },
|
||||||
|
lotacres: { type: String, required: true },
|
||||||
|
yearBuild: { type: String, required: true },
|
||||||
|
totallivingsqft: { type: String, required: true },
|
||||||
|
beds: { type: String, required: true },
|
||||||
|
baths: { type: String, required: true },
|
||||||
|
stories: { type: String, required: true },
|
||||||
|
garage: { type: String, required: true },
|
||||||
|
garagesqft: { type: String, required: true },
|
||||||
|
poolspa: { type: String, required: true },
|
||||||
|
fireplaces: { type: String, required: true },
|
||||||
|
ac: { type: String, required: true },
|
||||||
|
heating: { type: String, required: true },
|
||||||
|
buildingstyle: { type: String, required: true },
|
||||||
|
sitevacant: { type: String, required: true },
|
||||||
|
extwall: { type: String, required: true },
|
||||||
|
roofing: { type: String, required: true },
|
||||||
|
totalSqft: { type: String, required: true },
|
||||||
|
|
||||||
|
// Remove individual property tax fields and replace with array
|
||||||
|
propertyTaxInfo: [propertyTaxInfoSchema], // Array of tax info objects
|
||||||
|
images: [imageSchema],
|
||||||
|
googleMapLink: { type: String },
|
||||||
|
userfirstname: String,
|
||||||
|
usermiddlename: String,
|
||||||
|
userlastname: String,
|
||||||
|
usertitle: String,
|
||||||
|
creator: String,
|
||||||
|
useremail: String,
|
||||||
|
propertyId: String,
|
||||||
|
userId: String,
|
||||||
|
createdAt: {
|
||||||
|
type: Date,
|
||||||
|
default: new Date(),
|
||||||
|
},
|
||||||
|
publishedAt: {
|
||||||
|
type: Date,
|
||||||
|
default: new Date(),
|
||||||
|
},
|
||||||
|
currentYear: {
|
||||||
|
type: Number,
|
||||||
|
default: new Date().getFullYear(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const PropertyModal = mongoose.model("property", propertySchema);
|
||||||
|
|
||||||
|
export default PropertyModal;
|
|
@ -42,7 +42,6 @@ const propertySchema = mongoose.Schema({
|
||||||
extwall: { type: String, required: true },
|
extwall: { type: String, required: true },
|
||||||
roofing: { type: String, required: true },
|
roofing: { type: String, required: true },
|
||||||
totalSqft: { type: String, required: true },
|
totalSqft: { type: String, required: true },
|
||||||
|
|
||||||
// Remove individual property tax fields and replace with array
|
// Remove individual property tax fields and replace with array
|
||||||
propertyTaxInfo: [propertyTaxInfoSchema], // Array of tax info objects
|
propertyTaxInfo: [propertyTaxInfoSchema], // Array of tax info objects
|
||||||
images: [imageSchema],
|
images: [imageSchema],
|
||||||
|
@ -67,6 +66,42 @@ const propertySchema = mongoose.Schema({
|
||||||
type: Number,
|
type: Number,
|
||||||
default: new Date().getFullYear(),
|
default: new Date().getFullYear(),
|
||||||
},
|
},
|
||||||
|
purchaseCost: {
|
||||||
|
type: Number,
|
||||||
|
required: true, // Set to true if this field is mandatory
|
||||||
|
},
|
||||||
|
costPaidAtoB: [
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true, // Set to true if this field is mandatory
|
||||||
|
},
|
||||||
|
price: {
|
||||||
|
type: Number,
|
||||||
|
required: true, // Set to true if this field is mandatory
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
totalCostsAtoB: {
|
||||||
|
type: Number,
|
||||||
|
required: true, // Set to true if this field is mandatory
|
||||||
|
},
|
||||||
|
credits: [
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true, // Set to true if this field is mandatory
|
||||||
|
},
|
||||||
|
price: {
|
||||||
|
type: Number,
|
||||||
|
required: true, // Set to true if this field is mandatory
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
totalCostsAtoBaftercredits: {
|
||||||
|
type: Number,
|
||||||
|
required: true, // Set to true if this field is mandatory
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const PropertyModal = mongoose.model("property", propertySchema);
|
const PropertyModal = mongoose.model("property", propertySchema);
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -45,7 +45,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="module" crossorigin src="/assets/index-CMK2z1zZ.js"></script>
|
<script type="module" crossorigin src="/assets/index-DfwOztBd.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-iEl-il0E.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-iEl-il0E.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,880 @@
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { submitProperty } from "../redux/features/propertySlice";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import Navbar from "./Navbar";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import "react-toastify/dist/ReactToastify.css";
|
||||||
|
import FileBase64 from "react-file-base64";
|
||||||
|
import "../addproperty.css";
|
||||||
|
|
||||||
|
const Addproperty = () => {
|
||||||
|
const [activeTab, setActiveTab] = useState("propertydetails");
|
||||||
|
const handleContinue = () => {
|
||||||
|
if (activeTab === "propertydetails") setActiveTab("Images");
|
||||||
|
if (activeTab === "Images") setActiveTab("Accounting");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBack = () => {
|
||||||
|
if (activeTab === "Images") setActiveTab("propertydetails");
|
||||||
|
if (activeTab === "Accounting") setActiveTab("Images");
|
||||||
|
};
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { status, error } = useSelector((state) => state.property);
|
||||||
|
const { user } = useSelector((state) => ({ ...state.auth }));
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
poolspa: "",
|
||||||
|
propertyTaxInfo: [
|
||||||
|
{ propertytaxowned: "", ownedyear: "", taxassessed: "", taxyear: "" },
|
||||||
|
],
|
||||||
|
images: [{ title: "", file: "" }], // Array to hold image objects
|
||||||
|
googleMapLink: "", // Field for Google Maps link
|
||||||
|
purchaseCost: "",
|
||||||
|
costPaidAtoB: [
|
||||||
|
{ title: "Closing Fees - Settlement Fee", price: "" },
|
||||||
|
{ title: "Closing Fees - Owner's Title Insurance", price: "" },
|
||||||
|
{ title: "Courier Fees", price: "" },
|
||||||
|
{ title: "Wire Fee", price: "" },
|
||||||
|
{ title: "E recording Fee", price: "" },
|
||||||
|
{ title: "Recording Fee", price: "" },
|
||||||
|
{ title: "Property Tax", price: "" },
|
||||||
|
],
|
||||||
|
credits: [{ title: "Credits received on settlement", price: "" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const [submitted, setSubmitted] = useState(false);
|
||||||
|
|
||||||
|
const handleImageChange = (file, index) => {
|
||||||
|
const updatedImages = [...formData.images];
|
||||||
|
updatedImages[index].file = file.base64; // store base64 format of image
|
||||||
|
setFormData({ ...formData, images: updatedImages });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddImage = () => {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
images: [...formData.images, { title: "", file: "" }],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteImage = (index) => {
|
||||||
|
const updatedImages = formData.images.filter((_, i) => i !== index);
|
||||||
|
setFormData({ ...formData, images: updatedImages });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImageTitleChange = (index, e) => {
|
||||||
|
const updatedImages = [...formData.images];
|
||||||
|
updatedImages[index].title = e.target.value;
|
||||||
|
setFormData({ ...formData, images: updatedImages });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (e, index, fieldName) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
if (fieldName === "propertyTaxInfo") {
|
||||||
|
const updatedPropertyTaxInfo = [...formData.propertyTaxInfo];
|
||||||
|
updatedPropertyTaxInfo[index][name] = value;
|
||||||
|
setFormData({ ...formData, propertyTaxInfo: updatedPropertyTaxInfo });
|
||||||
|
} else {
|
||||||
|
setFormData({ ...formData, [name]: value });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addPropertyTaxField = () => {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
propertyTaxInfo: [
|
||||||
|
...formData.propertyTaxInfo,
|
||||||
|
{ propertytaxowned: "", ownedyear: "", taxassessed: "", taxyear: "" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deletePropertyTaxField = (index) => {
|
||||||
|
const updatedPropertyTaxInfo = formData.propertyTaxInfo.filter(
|
||||||
|
(_, i) => i !== index
|
||||||
|
);
|
||||||
|
setFormData({ ...formData, propertyTaxInfo: updatedPropertyTaxInfo });
|
||||||
|
};
|
||||||
|
|
||||||
|
const addCost = () => {
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
costPaidAtoB: [...prevData.costPaidAtoB, { title: "", price: "" }],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteCost = (index) => {
|
||||||
|
const updatedCosts = formData.costPaidAtoB.filter((_, i) => i !== index);
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
costPaidAtoB: updatedCosts,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to handle changes to title and price
|
||||||
|
const handleCostChange = (index, field, value) => {
|
||||||
|
const updatedCosts = [...formData.costPaidAtoB];
|
||||||
|
updatedCosts[index][field] = value;
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
costPaidAtoB: updatedCosts,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePriceChange = (e, index) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
|
||||||
|
// Use a regular expression to allow only numbers and decimals
|
||||||
|
const isNumber = /^\d*\.?\d*$/.test(value);
|
||||||
|
|
||||||
|
// If valid number, update state, otherwise show the alert
|
||||||
|
if (isNumber || value === "") {
|
||||||
|
const updatedCosts = formData.costPaidAtoB.map((cost, i) =>
|
||||||
|
i === index
|
||||||
|
? { ...cost, price: value, isInvalid: false } // Reset isInvalid if valid number
|
||||||
|
: cost
|
||||||
|
);
|
||||||
|
setFormData({ ...formData, costPaidAtoB: updatedCosts });
|
||||||
|
} else {
|
||||||
|
const updatedCosts = formData.costPaidAtoB.map((cost, i) =>
|
||||||
|
i === index
|
||||||
|
? { ...cost, isInvalid: true } // Keep isInvalid true for invalid input
|
||||||
|
: cost
|
||||||
|
);
|
||||||
|
setFormData({ ...formData, costPaidAtoB: updatedCosts });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateTotalCosts = () => {
|
||||||
|
const totalCostsFromArray = formData.costPaidAtoB.reduce((total, cost) => {
|
||||||
|
const price = parseFloat(cost.price) || 0; // Convert price to number
|
||||||
|
return total + price;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
const purchaseCost = parseFloat(formData.purchaseCost) || 0; // Convert purchase cost to number
|
||||||
|
|
||||||
|
return totalCostsFromArray + purchaseCost; // Return the total
|
||||||
|
};
|
||||||
|
|
||||||
|
const addCredits = () => {
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
credits: [...prevData.credits, { title: "", price: "" }],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteCredits = (index) => {
|
||||||
|
const updatedCredits = formData.credits.filter((_, i) => i !== index);
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
credits: updatedCredits,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to handle changes to credits title and price
|
||||||
|
const handleCreditChange = (index, field, value) => {
|
||||||
|
const updatedCredits = [...formData.credits];
|
||||||
|
updatedCredits[index][field] = value;
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
credits: updatedCredits,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreditPriceChange = (e, index) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
|
||||||
|
// Regular expression to allow only numbers and optional decimals
|
||||||
|
const isNumber = /^\d*\.?\d*$/.test(value);
|
||||||
|
|
||||||
|
// If valid number, update state, otherwise show the alert
|
||||||
|
if (isNumber || value === "") {
|
||||||
|
const updatedCredits = formData.credits.map((credit, i) =>
|
||||||
|
i === index
|
||||||
|
? { ...credit, price: value, isInvalid: false } // Reset isInvalid if valid number
|
||||||
|
: credit
|
||||||
|
);
|
||||||
|
setFormData({ ...formData, credits: updatedCredits });
|
||||||
|
} else {
|
||||||
|
const updatedCredits = formData.credits.map((credit, i) =>
|
||||||
|
i === index
|
||||||
|
? { ...credit, isInvalid: true } // Set isInvalid true for invalid input
|
||||||
|
: credit
|
||||||
|
);
|
||||||
|
setFormData({ ...formData, credits: updatedCredits });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateTotalCostsWithCredits = () => {
|
||||||
|
// Calculate total from costPaidAtoB array
|
||||||
|
const totalCostsFromArray = formData.costPaidAtoB.reduce((total, cost) => {
|
||||||
|
const price = parseFloat(cost.price) || 0; // Convert price to number
|
||||||
|
return total + price;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
// Calculate total from credits array
|
||||||
|
const totalCreditsFromArray = formData.credits.reduce((total, credit) => {
|
||||||
|
const price = parseFloat(credit.price) || 0; // Convert price to number
|
||||||
|
return total + price;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
// Convert purchase cost to number
|
||||||
|
const purchaseCost = parseFloat(formData.purchaseCost) || 0;
|
||||||
|
|
||||||
|
// Return the sum of total costs from both arrays and purchase cost
|
||||||
|
return totalCostsFromArray + totalCreditsFromArray + purchaseCost;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
if (formData.poolspa) {
|
||||||
|
const totalCostsAtoB = calculateTotalCosts();
|
||||||
|
const totalCostsAtoBaftercredits = calculateTotalCostsWithCredits();
|
||||||
|
// Add user info to formData, including the propertyTaxInfo array
|
||||||
|
const formDataWithUserInfo = {
|
||||||
|
...formData,
|
||||||
|
userfirstname: user?.result?.firstName,
|
||||||
|
usermiddlename: user?.result?.middleName,
|
||||||
|
userlastname: user?.result?.lastName,
|
||||||
|
usertitle: user?.result?.title,
|
||||||
|
useremail: user?.result?.email,
|
||||||
|
userId: user?.result?.userId,
|
||||||
|
totalCostsAtoB: totalCostsAtoB,
|
||||||
|
totalCostsAtoBaftercredits: totalCostsAtoBaftercredits,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if propertyTaxInfo is an array and has values
|
||||||
|
if (
|
||||||
|
Array.isArray(formData.propertyTaxInfo) &&
|
||||||
|
formData.propertyTaxInfo.length > 0
|
||||||
|
) {
|
||||||
|
formDataWithUserInfo.propertyTaxInfo = formData.propertyTaxInfo; // Include propertyTaxInfo
|
||||||
|
} else {
|
||||||
|
formDataWithUserInfo.propertyTaxInfo = []; // Ensure it's an empty array if not filled
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch the form data with user info
|
||||||
|
dispatch(submitProperty(formDataWithUserInfo));
|
||||||
|
setSubmitted(true);
|
||||||
|
} else {
|
||||||
|
toast.error("Please fill all fields before submitting", {
|
||||||
|
position: "top-right",
|
||||||
|
autoClose: 3000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (submitted) {
|
||||||
|
if (status === "succeeded") {
|
||||||
|
toast.success("Property submitted successfully!", {
|
||||||
|
position: "top-right",
|
||||||
|
autoClose: 3000,
|
||||||
|
});
|
||||||
|
setSubmitted(false);
|
||||||
|
} else if (status === "failed") {
|
||||||
|
toast.error(`Failed to submit: ${error}`, {
|
||||||
|
position: "top-right",
|
||||||
|
autoClose: 3000,
|
||||||
|
});
|
||||||
|
setSubmitted(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [status, error, submitted]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="container tabs-wrap">
|
||||||
|
<Navbar />
|
||||||
|
|
||||||
|
<ul className="nav nav-tabs" role="tablist">
|
||||||
|
<li
|
||||||
|
role="presentation"
|
||||||
|
className={activeTab === "propertydetails" ? "active tab" : "tab"}
|
||||||
|
>
|
||||||
|
<a onClick={() => setActiveTab("propertydetails")} role="tab">
|
||||||
|
Property Details
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
role="presentation"
|
||||||
|
className={activeTab === "Images" ? "active tab" : "tab"}
|
||||||
|
>
|
||||||
|
<a onClick={() => setActiveTab("Images")} role="tab">
|
||||||
|
Images, Maps
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li
|
||||||
|
role="presentation"
|
||||||
|
className={activeTab === "Accounting" ? "active tab" : "tab"}
|
||||||
|
>
|
||||||
|
<a onClick={() => setActiveTab("Accounting")} role="tab">
|
||||||
|
Accounting
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div className="tab-content">
|
||||||
|
{activeTab === "propertydetails" && (
|
||||||
|
<div role="tabpanel" className="card tab-pane active">
|
||||||
|
<form>
|
||||||
|
<h3
|
||||||
|
style={{
|
||||||
|
color: "#fda417",
|
||||||
|
border: "#fda417",
|
||||||
|
fontSize: "20px",
|
||||||
|
fontWeight: "normal",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Property Location
|
||||||
|
</h3>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div className="row gy-3">
|
||||||
|
<div className="col-md-4">
|
||||||
|
Please Select Pool/SPA
|
||||||
|
<select
|
||||||
|
className="form-floating mb-3 form-control"
|
||||||
|
name="poolspa"
|
||||||
|
value={formData.poolspa}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="">Please Select</option>
|
||||||
|
<option value="N/A">N/A</option>
|
||||||
|
<option value="Yes- Pool Only">Yes- Pool Only</option>
|
||||||
|
<option value="Yes- SPA only">Yes- SPA only</option>
|
||||||
|
<option value="Yes - Both Pool and SPA">
|
||||||
|
Yes - Both Pool and SPA
|
||||||
|
</option>
|
||||||
|
<option value="4">None</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row gy-3"></div>
|
||||||
|
<h3
|
||||||
|
style={{
|
||||||
|
color: "#fda417",
|
||||||
|
border: "#fda417",
|
||||||
|
fontSize: "20px",
|
||||||
|
fontWeight: "normal",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<br />
|
||||||
|
Property Tax Information
|
||||||
|
</h3>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
{formData.propertyTaxInfo.map((taxInfo, index) => (
|
||||||
|
<div className="row gy-4" key={index}>
|
||||||
|
<div className="col-md-3">
|
||||||
|
<div className="form-floating mb-3">
|
||||||
|
Property Tax Owned
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
name="propertytaxowned"
|
||||||
|
value={taxInfo.propertytaxowned}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleInputChange(e, index, "propertyTaxInfo")
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<div className="form-floating mb-2">
|
||||||
|
Owed Year
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
name="ownedyear"
|
||||||
|
value={taxInfo.ownedyear}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleInputChange(e, index, "propertyTaxInfo")
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<div className="form-floating mb-2">
|
||||||
|
Tax Assessed
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
name="taxassessed"
|
||||||
|
value={taxInfo.taxassessed}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleInputChange(e, index, "propertyTaxInfo")
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<div className="form-floating mb-2">
|
||||||
|
Tax Year
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
name="taxyear"
|
||||||
|
value={taxInfo.taxyear}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleInputChange(e, index, "propertyTaxInfo")
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-3">
|
||||||
|
<div className="form-floating mb-2">
|
||||||
|
<br />
|
||||||
|
<button
|
||||||
|
className="btn btn-danger"
|
||||||
|
onClick={() => deletePropertyTaxField(index)}
|
||||||
|
style={{ height: "25px", width: "35px" }}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={addPropertyTaxField}
|
||||||
|
>
|
||||||
|
+ Add Another Property Tax Info
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="btn btn-primary continue"
|
||||||
|
onClick={handleContinue}
|
||||||
|
style={{ backgroundColor: "#fda417", border: "#fda417" }}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{activeTab === "Images" && (
|
||||||
|
<div role="tabpanel" className="card tab-pane active">
|
||||||
|
<h3
|
||||||
|
style={{
|
||||||
|
color: "#fda417",
|
||||||
|
border: "#fda417",
|
||||||
|
fontSize: "20px",
|
||||||
|
fontWeight: "normal",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Upload Images{" "}
|
||||||
|
</h3>
|
||||||
|
<form>
|
||||||
|
{formData.images.map((image, index) => (
|
||||||
|
<div key={index} className="row gy-3 align-items-center">
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
value={image.title}
|
||||||
|
placeholder="Image Title"
|
||||||
|
onChange={(e) => handleImageTitleChange(index, e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-4 d-flex align-items-center">
|
||||||
|
<FileBase64
|
||||||
|
multiple={false}
|
||||||
|
onDone={(file) => handleImageChange(file, index)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-danger"
|
||||||
|
onClick={() => handleDeleteImage(index)}
|
||||||
|
style={{
|
||||||
|
marginLeft: "5px", // Adjust this to minimize space between the buttons
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{image.file && (
|
||||||
|
<div className="col-md-12">
|
||||||
|
<img
|
||||||
|
src={image.file}
|
||||||
|
alt="uploaded"
|
||||||
|
style={{ width: "150px", height: "150px" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={handleAddImage}
|
||||||
|
style={{ backgroundColor: "#fda417", border: "#fda417" }}
|
||||||
|
>
|
||||||
|
+ Add Image
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div className="mb-3">
|
||||||
|
<label htmlFor="googleMapLink" className="form-label">
|
||||||
|
<h3
|
||||||
|
style={{
|
||||||
|
color: "#fda417",
|
||||||
|
border: "#fda417",
|
||||||
|
fontSize: "20px",
|
||||||
|
fontWeight: "normal",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Google Maps Link{" "}
|
||||||
|
</h3>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
name="googleMapLink"
|
||||||
|
value={formData.googleMapLink}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Enter Google Maps link"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{formData.googleMapLink && (
|
||||||
|
<iframe
|
||||||
|
title="Google Map"
|
||||||
|
width="100%"
|
||||||
|
height="300"
|
||||||
|
src={`https://www.google.com/maps/embed/v1/view?key=YOUR_API_KEY¢er=${formData.googleMapLink}&zoom=10`}
|
||||||
|
frameBorder="0"
|
||||||
|
allowFullScreen
|
||||||
|
></iframe>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
<button
|
||||||
|
className="btn btn-primary back"
|
||||||
|
onClick={handleBack}
|
||||||
|
style={{ backgroundColor: "#fda417", border: "#fda417" }}
|
||||||
|
>
|
||||||
|
Go Back
|
||||||
|
</button>{" "}
|
||||||
|
<button
|
||||||
|
className="btn btn-primary continue"
|
||||||
|
onClick={handleContinue}
|
||||||
|
style={{ backgroundColor: "#fda417", border: "#fda417" }}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{activeTab === "Accounting" && (
|
||||||
|
<div
|
||||||
|
className="card"
|
||||||
|
style={{
|
||||||
|
color: "#fda417",
|
||||||
|
border: "1px solid #fda417",
|
||||||
|
padding: "10px",
|
||||||
|
borderRadius: "8px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
style={{
|
||||||
|
color: "#fda417",
|
||||||
|
border: "#fda417",
|
||||||
|
fontSize: "20px",
|
||||||
|
fontWeight: "normal",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Property Acquisition A to B:
|
||||||
|
</h3>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{/* <div className="form-floating mb-3">
|
||||||
|
<span style={{
|
||||||
|
color: "#fda417",
|
||||||
|
fontSize: "14px",
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}>Purchase Cost</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
value={formData.purchaseCost}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
purchaseCost: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="Enter Purchase Cost"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
|
<div className="row gy-3">
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder="Purchase Cost"
|
||||||
|
required
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
value={formData.purchaseCost}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
purchaseCost: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="Enter Purchase Cost"
|
||||||
|
style={{ textAlign: "right" }}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: "#fda417",
|
||||||
|
fontSize: "14px",
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Costs paid out of Closing Hud A to B:
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{formData.costPaidAtoB.map((cost, index) => (
|
||||||
|
<div key={index} className="row gy-3 align-items-center">
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
value={cost.title}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleCostChange(index, "title", e.target.value)
|
||||||
|
}
|
||||||
|
placeholder="Title"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className={`form-control ${
|
||||||
|
cost.isInvalid ? "is-invalid" : ""
|
||||||
|
}`} // Apply 'is-invalid' class when invalid
|
||||||
|
value={cost.price}
|
||||||
|
onChange={(e) => handlePriceChange(e, index)} // Use a new handler for price validation
|
||||||
|
placeholder="Price"
|
||||||
|
style={{ textAlign: "right" }}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{cost.isInvalid && (
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Please enter a valid number.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="col-md-2 d-flex justify-content-start">
|
||||||
|
<button
|
||||||
|
className="btn btn-danger"
|
||||||
|
onClick={() => deleteCost(index)}
|
||||||
|
style={{ marginLeft: "5px" }}
|
||||||
|
>
|
||||||
|
x
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div className="col-md-4">
|
||||||
|
<button
|
||||||
|
onClick={addCost}
|
||||||
|
className="btn btn-primary back"
|
||||||
|
style={{ backgroundColor: "#fda417", border: "#fda417" }}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: "20px",
|
||||||
|
fontWeight: "normal",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</span>{" "}
|
||||||
|
Add Extra Cost
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row gy-3 align-items-center">
|
||||||
|
<span
|
||||||
|
className="col-md-4"
|
||||||
|
style={{
|
||||||
|
color: "#fda417",
|
||||||
|
fontSize: "14px",
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Total Costs to Buy A to B
|
||||||
|
</span>
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
name="totalCostsAtoB"
|
||||||
|
value={calculateTotalCosts()}
|
||||||
|
readOnly
|
||||||
|
style={{
|
||||||
|
borderColor: "#fda417", // Custom border color for the input field
|
||||||
|
color: "#fda417", // Optionally apply text color to the input text
|
||||||
|
textAlign: "right",
|
||||||
|
}}
|
||||||
|
placeholder="Total Costs to Buy A to B"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: "#fda417",
|
||||||
|
fontSize: "14px",
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Credits
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{formData.credits.map((credits, index) => (
|
||||||
|
<div key={index} className="row gy-3 align-items-center">
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
value={credits.title}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleCreditChange(index, "title", e.target.value)
|
||||||
|
}
|
||||||
|
placeholder="credits"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className={`form-control ${
|
||||||
|
credits.isInvalid ? "is-invalid" : ""
|
||||||
|
}`} // Apply 'is-invalid' class when invalid
|
||||||
|
value={credits.price}
|
||||||
|
onChange={(e) => handleCreditPriceChange(e, index)} // New handler for price validation
|
||||||
|
placeholder="Price"
|
||||||
|
style={{ textAlign: "right" }}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{credits.isInvalid && (
|
||||||
|
<div className="invalid-feedback">
|
||||||
|
Please enter a valid number.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="col-md-2 d-flex justify-content-start">
|
||||||
|
<button
|
||||||
|
className="btn btn-danger"
|
||||||
|
onClick={() => deleteCredits(index)}
|
||||||
|
style={{ marginLeft: "5px" }}
|
||||||
|
>
|
||||||
|
x
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div className="col-md-4">
|
||||||
|
<button
|
||||||
|
className="btn btn-primary back"
|
||||||
|
onClick={addCredits}
|
||||||
|
style={{ backgroundColor: "#fda417", border: "#fda417" }}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: "20px",
|
||||||
|
fontWeight: "normal",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</span>{" "}
|
||||||
|
Add credits
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row gy-3 align-items-center">
|
||||||
|
<span
|
||||||
|
className="col-md-4"
|
||||||
|
style={{
|
||||||
|
color: "#fda417",
|
||||||
|
fontSize: "14px",
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Total Costs to Buy A to B After credits
|
||||||
|
</span>
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
name="totalCostsAtoBaftercredits"
|
||||||
|
value={calculateTotalCostsWithCredits()}
|
||||||
|
readOnly
|
||||||
|
style={{
|
||||||
|
borderColor: "#fda417", // Custom border color for the input field
|
||||||
|
color: "#fda417", // Optionally apply text color to the input text
|
||||||
|
textAlign: "right",
|
||||||
|
}}
|
||||||
|
placeholder="Total Costs to Buy A to B After credits"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div className="col-md-4">
|
||||||
|
<button
|
||||||
|
className="btn btn-primary back"
|
||||||
|
onClick={handleBack}
|
||||||
|
style={{ backgroundColor: "#fda417", border: "#fda417" }}
|
||||||
|
>
|
||||||
|
Go Back
|
||||||
|
</button>{" "}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary continue"
|
||||||
|
style={{ backgroundColor: "#fda417", border: "#fda417" }}
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>{" "}
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Addproperty;
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,61 +0,0 @@
|
||||||
import { useState } from 'react';
|
|
||||||
import "../addproperty.css";
|
|
||||||
|
|
||||||
|
|
||||||
const Addproperty = () => {
|
|
||||||
const [activeTab, setActiveTab] = useState('billing');
|
|
||||||
|
|
||||||
const handleContinue = () => {
|
|
||||||
if (activeTab === 'billing') setActiveTab('shipping');
|
|
||||||
else if (activeTab === 'shipping') setActiveTab('review');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBack = () => {
|
|
||||||
if (activeTab === 'review') setActiveTab('shipping');
|
|
||||||
else if (activeTab === 'shipping') setActiveTab('billing');
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="container tabs-wrap">
|
|
||||||
<ul className="nav nav-tabs" role="tablist">
|
|
||||||
<li role="presentation" className={activeTab === 'billing' ? 'active tab' : 'tab'}>
|
|
||||||
<a onClick={() => setActiveTab('billing')} role="tab">Billing Address</a>
|
|
||||||
</li>
|
|
||||||
<li role="presentation" className={activeTab === 'shipping' ? 'active tab' : 'tab'}>
|
|
||||||
<a onClick={() => setActiveTab('shipping')} role="tab">Delivery Address</a>
|
|
||||||
</li>
|
|
||||||
<li role="presentation" className={activeTab === 'review' ? 'active tab' : 'tab'}>
|
|
||||||
<a onClick={() => setActiveTab('review')} role="tab">Review & Payment</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div className="tab-content">
|
|
||||||
{activeTab === 'billing' && (
|
|
||||||
<div role="tabpanel" className="tab-pane active">
|
|
||||||
<h3>Billing Address</h3>
|
|
||||||
<p>Billing Address Form</p>
|
|
||||||
<button className="btn btn-primary continue" onClick={handleContinue}>Continue</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{activeTab === 'shipping' && (
|
|
||||||
<div role="tabpanel" className="tab-pane active">
|
|
||||||
<h3>Shipping Address</h3>
|
|
||||||
<p>Shipping Address Form</p>
|
|
||||||
<button className="btn btn-primary back" onClick={handleBack}>Go Back </button>{" "}
|
|
||||||
<button className="btn btn-primary continue" onClick={handleContinue}>Continue</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{activeTab === 'review' && (
|
|
||||||
<div role="tabpanel" className="tab-pane active">
|
|
||||||
<h3>Review & Place Order</h3>
|
|
||||||
<p>Review & Payment Tab</p>
|
|
||||||
<button className="btn btn-primary back" onClick={handleBack}>Go Back</button>{" "}
|
|
||||||
<button className="btn btn-primary continue">Place Order</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Addproperty;
|
|
Loading…
Reference in New Issue