This commit is contained in:
omkieit 2024-12-23 12:14:12 +05:30
parent ccb1dcbb9e
commit 0593f510af
8 changed files with 489 additions and 178 deletions

View File

@ -4,41 +4,41 @@ import mongoose from "mongoose";
// import { v4 as uuidv4 } from "uuid";
export const createProperty = async (req, res) => {
const propertyData = req.body;
console.log('Property received:', propertyData);
function generateRandomNumber() {
return Math.floor(Math.random() * 90000) + 10000;
const propertyData = req.body;
console.log("Property received:", propertyData);
function generateRandomNumber() {
return Math.floor(Math.random() * 90000) + 10000;
}
const randomNumber = generateRandomNumber().toString();
const propertyId = `ELPI${randomNumber}`;
const newProperty = new PropertyModal({
...propertyData,
_id: new mongoose.Types.ObjectId(), // Explicitly generating _id
creator: req.userId,
createdAt: new Date().toISOString(),
publishedAt: new Date().toISOString(),
currentYear: new Date().getFullYear(),
propertyId: propertyId,
});
console.log("newProperty received:", newProperty);
try {
await newProperty.save();
res.status(201).json(newProperty);
} catch (error) {
// Log the full error object to see what's inside
console.log("Error fetching properties:", error);
// Capture and return a meaningful error message
if (error.response && error.response.data) {
return rejectWithValue(error.response.data);
} else {
return rejectWithValue({ message: "Unknown error occurred" });
}
const randomNumber = generateRandomNumber().toString();
const propertyId = `ELPI${randomNumber}`;
const newProperty = new PropertyModal({
...propertyData,
_id: new mongoose.Types.ObjectId(), // Explicitly generating _id
creator: req.userId,
createdAt: new Date().toISOString(),
publishedAt: new Date().toISOString(),
currentYear: new Date().getFullYear(),
propertyId: propertyId,
});
console.log('newProperty received:', newProperty);
try {
await newProperty.save();
res.status(201).json(newProperty);
} catch (error) {
// Log the full error object to see what's inside
console.log("Error fetching properties:", error);
// Capture and return a meaningful error message
if (error.response && error.response.data) {
return rejectWithValue(error.response.data);
} else {
return rejectWithValue({ message: "Unknown error occurred" });
}
}
}
};
// Fetch property by userId.. Gets the specific user properties
@ -48,12 +48,12 @@ export const getUserProperties = async (req, res) => {
const skip = (page - 1) * limit;
try {
const properties = await PropertyModal.find({ userId: userId })
.skip(skip)
.limit(Number(limit));
const properties = await PropertyModal.find({ userId: userId })
.skip(skip)
.limit(Number(limit));
const total = await PropertyModal.countDocuments({ userId });
const totalPages = Math.ceil(total / limit);
res.json({
res.json({
properties,
totalPages,
currentPage: Number(page),
@ -63,17 +63,6 @@ export const getUserProperties = async (req, res) => {
}
};
// Fetch property by ID.. which is property view page
export const getPropertyById = async (req, res) => {
const { propertyId } = req.params;
@ -85,7 +74,7 @@ export const getPropertyById = async (req, res) => {
}
};
// Update property by ID..
// Update property by ID..
export const updatePropertyById = async (req, res) => {
const { id } = req.params; // This should be the propertyId
const updateData = req.body;
@ -100,8 +89,8 @@ export const updatePropertyById = async (req, res) => {
{ new: true }
);
// Push the new fund details into the existing fundDetails array
updatedProperty.fundDetails.push({
// Push the new fund details into the existing fundDetails array
updatedProperty.fundDetails.push({
fundValue,
fundPercentage,
});
@ -118,13 +107,12 @@ export const updatePropertyById = async (req, res) => {
res.status(200).json(updatedProperty);
} catch (error) {
console.error("Error updating property:", error.message);
res.status(500).json({ message: "Failed to update property", error: error.message });
res
.status(500)
.json({ message: "Failed to update property", error: error.message });
}
};
// Function to update fundDetails
export const addFundDetails = async (req, res) => {
const { id } = req.params; // This should be the propertyId
@ -137,14 +125,14 @@ export const addFundDetails = async (req, res) => {
const property = await PropertyModal.findOne({ propertyId: id });
if (!property) {
return res.status(404).json({ message: 'Property not found' });
return res.status(404).json({ message: "Property not found" });
}
// Create new fund detail
const newFundDetail = {
fundValue,
fundPercentage,
userId
userId,
};
// Add new fund detail to fundDetails array
@ -154,15 +142,13 @@ export const addFundDetails = async (req, res) => {
await property.save();
// Return success response with updated property
res.status(200).json({ message: 'Fund details added', property });
res.status(200).json({ message: "Fund details added", property });
} catch (error) {
console.error("Error adding fund details:", error); // Log error for debugging
res.status(500).json({ message: error.message });
}
};
export const deleteFundDetail = async (req, res) => {
const { id, fundDetailId } = req.params;
const userId = req.userId;
@ -175,44 +161,46 @@ export const deleteFundDetail = async (req, res) => {
try {
const property = await PropertyModal.findOne({ propertyId: id });
if (!property) {
return res.status(404).json({ message: 'Property not found' });
return res.status(404).json({ message: "Property not found" });
}
console.log("Fund Details:", property.fundDetails);
const fundDetailIndex = property.fundDetails.findIndex(
(detail) => detail._id.toString() === fundDetailId && detail.userId.toString() === userId
(detail) =>
detail._id.toString() === fundDetailId &&
detail.userId.toString() === userId
);
console.log("Fund Detail Index:", fundDetailIndex);
if (fundDetailIndex === -1) {
return res.status(403).json({ message: 'Not authorized to delete this fund detail' });
return res
.status(403)
.json({ message: "Not authorized to delete this fund detail" });
}
property.fundDetails.splice(fundDetailIndex, 1);
await property.save();
res.status(200).json({ message: 'Fund detail deleted', property });
res.status(200).json({ message: "Fund detail deleted", property });
} catch (error) {
console.error("Error deleting fund detail:", error);
res.status(500).json({ message: error.message });
}
};
// Getting funds from the DB
// Getting funds from the DB
export const getFundDetails = async (req, res) => {
const { id } = req.params; // Property ID
try {
const property = await PropertyModal.findOne({ propertyId: id }, 'fundDetails');
const property = await PropertyModal.findOne(
{ propertyId: id },
"fundDetails"
);
if (!property) {
return res.status(404).json({ message: 'Property not found' });
return res.status(404).json({ message: "Property not found" });
}
// Return fund details
@ -223,8 +211,6 @@ export const getFundDetails = async (req, res) => {
}
};
// export const getProperties = async (req, res) => {
// try {
// const properties = await PropertyModal.find(); // Fetch all properties from MongoDB
@ -234,7 +220,6 @@ export const getFundDetails = async (req, res) => {
// }
// };
// export const getProperties = async (req, res) => {
// try {
// const { page = 1, limit = 10 } = req.query;
@ -256,15 +241,14 @@ export const getFundDetails = async (req, res) => {
// }
// };
export const getProperties = async (req, res) => {
try {
const { page = 1, limit = 10, keyword = "" } = req.query;
const skip = (page - 1) * limit;
// Use a filter to match the keyword if provided
const keywordFilter = keyword
? { address: { $regex: keyword, $options: "i" } }
const keywordFilter = keyword
? { address: { $regex: keyword, $options: "i" } }
: {};
const totalProperties = await PropertyModal.countDocuments(keywordFilter);
@ -280,10 +264,6 @@ export const getProperties = async (req, res) => {
currentPage: parseInt(page),
});
} catch (err) {
res.status(500).json({ success: false, message: 'Server error' });
res.status(500).json({ success: false, message: "Server error" });
}
};

View File

@ -318,7 +318,18 @@ const propertySchema = mongoose.Schema({
},
},
],
TradesinRenovations:[
{
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
},
},
],
IAB:[
{
title: {

File diff suppressed because one or more lines are too long

89
ef-ui/dist/assets/index-w0FWAEFq.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -54,7 +54,7 @@
<!-- Custom styles for this template-->
<link href="css/sb-admin-2.min.css" rel="stylesheet">
<script type="module" crossorigin src="/assets/index-Ta_ndD8p.js"></script>
<script type="module" crossorigin src="/assets/index-w0FWAEFq.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CN6EriED.css">
</head>

View File

@ -127,10 +127,28 @@ const Addproperty = () => {
InsuranceClaim: "0",
LongTermRental: "0",
FinancingCostClosingCost: "0",
TradesinRenovations: [
{ title: "Planning & Design", price: "0" },
{ title: "Demolition & Cleanup", price: "0" },
{ title: "Framing", price: "0" },
{ title: "Rough-in Services", price: "0" },
{ title: "Structural Repairs", price: "0" },
{ title: "Insulation & Drywall", price: "0" },
{ title: "Cabinetry & Countertops", price: "0" },
{ title: "Fixtures & Appliances", price: "0" },
{ title: "Flooring and Tiling", price: "0" },
{ title: "Plumbing", price: "0" },
{ title: "Electrical", price: "0" },
{ title: "HVAC", price: "0" },
{ title: "Painting", price: "0" },
{ title: "Interior Finishes", price: "0" },
{ title: "Exterior Work", price: "0" },
{ title: "Final Inspections", price: "0" },
{ title: "Punch List", price: "0" },
{ title: "Staging", price: "0" },
],
IAB: [{ title: "IAB Title", URL: "Please enter URL" }],
files: [{ title: "", file: "" }],
});
const [submitted, setSubmitted] = useState(false);
@ -894,7 +912,8 @@ const Addproperty = () => {
formData.OtherIncome &&
formData.InsuranceClaim &&
formData.LongTermRental &&
formData.FinancingCostClosingCost
formData.FinancingCostClosingCost &&
formData.TradesinRenovations
) {
const rateofreturn = calculaterateofreturn();
const totalPurchaseCosts = calculateTotalPurchaseCosts();
@ -1014,6 +1033,59 @@ const Addproperty = () => {
};
const addTrade = () => {
setFormData((prevData) => ({
...prevData,
TradesinRenovations: [...prevData.TradesinRenovations, { title: "", price: "" }],
}));
};
const deleteTrade = (index) => {
const updatedCosts = formData.TradesinRenovations.filter((_, i) => i !== index);
setFormData((prevData) => ({
...prevData,
TradesinRenovations: updatedCosts,
}));
};
// Function to handle changes to title and price
const handleTrade = (index, field, value) => {
const updatedCosts = [...formData.TradesinRenovations];
updatedCosts[index][field] = value;
setFormData((prevData) => ({
...prevData,
TradesinRenovations: updatedCosts,
}));
};
const handleTradePrice = (e, index) => {
let value = e.target.value;
// Remove the dollar sign before validating
value = value.replace(/^\$/, "").trim(); // Remove '$' if it exists
// 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.TradesinRenovations.map((cost, i) =>
i === index
? { ...cost, price: value, isInvalid: false } // Reset isInvalid if valid number
: cost
);
setFormData({ ...formData, TradesinRenovations: updatedCosts });
} else {
const updatedCosts = formData.TradesinRenovations.map((cost, i) =>
i === index
? { ...cost, isInvalid: true } // Keep isInvalid true for invalid input
: cost
);
setFormData({ ...formData, TradesinRenovations: updatedCosts });
}
};
@ -4098,6 +4170,94 @@ const Addproperty = () => {
<div role="tabpanel" className="card tab-pane active">
<span
style={{
color: "#fda417",
fontSize: "14px",
fontWeight: "bold",
}}
>
Trades in Renovations
</span>
{formData.TradesinRenovations.map((title, index) => (
<div key={index} className="row gy-3 align-items-center">
<div className="col-md-4">
<input
type="text"
className="form-control"
value={title.title}
onChange={(e) =>
handleTrade(index, "title", e.target.value)
}
placeholder="Title"
required
/>
</div>
<div className="col-md-4">
<input
type="text"
className={`form-control ${
title.isInvalid ? "is-invalid" : ""
}`}
value={`$ ${title.price}`}
onChange={(e) => handleTradePrice(e, index)}
placeholder="Price"
style={{ textAlign: "right" }}
required
/>
{title.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={() => deleteTrade(index)}
style={{ marginLeft: "5px" }}
>
x
</button>
</div>
</div>
))}
<div className="col-md-4">
<button
onClick={addTrade}
className="btn btn-primary back"
style={{ backgroundColor: "#fda417", border: "#fda417" }}
>
<span
style={{
fontSize: "20px",
fontWeight: "normal",
}}
>
+
</span>{" "}
Add Trades in Renovations
</button>
</div>
<hr
style={{
borderColor: "#fda417", // Set the color of the line
borderWidth: "1px", // Optional: Adjust the thickness of the line
backgroundColor: "#fda417", // Optional: Apply color if using for background
height: "1px", // Optional: Set height to match the border width
}}
/>
<span
style={{
color: "#fda417",

View File

@ -132,6 +132,26 @@ const EditProperty = () => {
InsuranceClaim: "0",
LongTermRental: "0",
FinancingCostClosingCost: "0",
TradesinRenovations: [
{ title: "Planning & Design", price: "0" },
{ title: "Demolition & Cleanup", price: "0" },
{ title: "Framing", price: "0" },
{ title: "Rough-in Services", price: "0" },
{ title: "Structural Repairs", price: "0" },
{ title: "Insulation & Drywall", price: "0" },
{ title: "Cabinetry & Countertops", price: "0" },
{ title: "Fixtures & Appliances", price: "0" },
{ title: "Flooring and Tiling", price: "0" },
{ title: "Plumbing", price: "0" },
{ title: "Electrical", price: "0" },
{ title: "HVAC", price: "0" },
{ title: "Painting", price: "0" },
{ title: "Interior Finishes", price: "0" },
{ title: "Exterior Work", price: "0" },
{ title: "Final Inspections", price: "0" },
{ title: "Punch List", price: "0" },
{ title: "Staging", price: "0" },
],
IAB: [{ title: "IAB Title", URL: "Please enter URL" }],
});
@ -213,6 +233,7 @@ const EditProperty = () => {
images: selectedProperty.images || [{ title: "", file: "" }],
googleMapLink: selectedProperty.googleMapLink,
rateofreturn: selectedProperty.rateofreturn,
TradesinRenovations:selectedProperty.TradesinRenovations,
IAB:selectedProperty.IAB,
});
}
@ -1046,6 +1067,59 @@ const EditProperty = () => {
};
const addTrade = () => {
setFormData((prevData) => ({
...prevData,
TradesinRenovations: [...prevData.TradesinRenovations, { title: "", price: "" }],
}));
};
const deleteTrade = (index) => {
const updatedCosts = formData.TradesinRenovations.filter((_, i) => i !== index);
setFormData((prevData) => ({
...prevData,
TradesinRenovations: updatedCosts,
}));
};
// Function to handle changes to title and price
const handleTrade = (index, field, value) => {
const updatedCosts = [...formData.TradesinRenovations];
updatedCosts[index][field] = value;
setFormData((prevData) => ({
...prevData,
TradesinRenovations: updatedCosts,
}));
};
const handleTradePrice = (e, index) => {
let value = e.target.value;
// Remove the dollar sign before validating
value = value.replace(/^\$/, "").trim(); // Remove '$' if it exists
// 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.TradesinRenovations.map((cost, i) =>
i === index
? { ...cost, price: value, isInvalid: false } // Reset isInvalid if valid number
: cost
);
setFormData({ ...formData, TradesinRenovations: updatedCosts });
} else {
const updatedCosts = formData.TradesinRenovations.map((cost, i) =>
i === index
? { ...cost, isInvalid: true } // Keep isInvalid true for invalid input
: cost
);
setFormData({ ...formData, TradesinRenovations: updatedCosts });
}
};
const addIAB = () => {
setFormData((prevData) => ({
@ -4179,6 +4253,91 @@ const EditProperty = () => {
<div role="tabpanel" className="card tab-pane active">
<span
style={{
color: "#fda417",
fontSize: "14px",
fontWeight: "bold",
}}
>
Trades in Renovations
</span>
{formData.TradesinRenovations.map((title, index) => (
<div key={index} className="row gy-3 align-items-center">
<div className="col-md-4">
<input
type="text"
className="form-control"
value={title.title}
onChange={(e) =>
handleTrade(index, "title", e.target.value)
}
placeholder="Title"
required
/>
</div>
<div className="col-md-4">
<input
type="text"
className={`form-control ${
title.isInvalid ? "is-invalid" : ""
}`}
value={`$ ${title.price}`}
onChange={(e) => handleTradePrice(e, index)}
placeholder="Price"
style={{ textAlign: "right" }}
required
/>
{title.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={() => deleteTrade(index)}
style={{ marginLeft: "5px" }}
>
x
</button>
</div>
</div>
))}
<div className="col-md-4">
<button
onClick={addTrade}
className="btn btn-primary back"
style={{ backgroundColor: "#fda417", border: "#fda417" }}
>
<span
style={{
fontSize: "20px",
fontWeight: "normal",
}}
>
+
</span>{" "}
Add Trades in Renovations
</button>
</div>
<hr
style={{
borderColor: "#fda417", // Set the color of the line
borderWidth: "1px", // Optional: Adjust the thickness of the line
backgroundColor: "#fda417", // Optional: Apply color if using for background
height: "1px", // Optional: Set height to match the border width
}}
/>
<span
style={{
color: "#fda417",

View File

@ -8,6 +8,7 @@ export const submitProperty = createAsyncThunk(
try {
const response = await api.submitProperty(propertyData);
return response.data;
} catch (error) {
return rejectWithValue(error.response.data);
}