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 },
|
||||
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],
|
||||
|
@ -67,6 +66,42 @@ const propertySchema = mongoose.Schema({
|
|||
type: Number,
|
||||
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);
|
||||
|
|
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">
|
||||
</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