done
This commit is contained in:
parent
02c8197678
commit
cf9c4d44cf
|
@ -119,9 +119,9 @@ export const updatePropertyById = async (req, res) => {
|
|||
// Function to update fundDetails
|
||||
export const addFundDetails = async (req, res) => {
|
||||
const { id } = req.params; // This should be the propertyId
|
||||
const { fundValue, fundPercentage } = req.body;
|
||||
const { fundValue, fundPercentage, userId } = req.body;
|
||||
|
||||
console.log("d", id, { fundValue, fundPercentage });
|
||||
console.log("d", id, { fundValue, fundPercentage, userId });
|
||||
|
||||
try {
|
||||
// Change findById to findOne with the correct field name
|
||||
|
@ -135,6 +135,7 @@ export const addFundDetails = async (req, res) => {
|
|||
const newFundDetail = {
|
||||
fundValue,
|
||||
fundPercentage,
|
||||
userId
|
||||
};
|
||||
|
||||
// Add new fund detail to fundDetails array
|
||||
|
@ -151,6 +152,49 @@ export const addFundDetails = async (req, res) => {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const deleteFundDetail = async (req, res) => {
|
||||
const { id, fundDetailId } = req.params;
|
||||
const userId = req.userId;
|
||||
|
||||
console.log("Attempting to delete Fund Detail...");
|
||||
console.log("Property ID:", id);
|
||||
console.log("Fund Detail ID:", fundDetailId);
|
||||
console.log("User ID from token:", userId);
|
||||
|
||||
try {
|
||||
const property = await PropertyModal.findOne({ propertyId: id });
|
||||
if (!property) {
|
||||
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
|
||||
);
|
||||
|
||||
console.log("Fund Detail Index:", fundDetailIndex);
|
||||
if (fundDetailIndex === -1) {
|
||||
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 });
|
||||
} catch (error) {
|
||||
console.error("Error deleting fund detail:", error);
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Getting funds from the DB
|
||||
export const getFundDetails = async (req, res) => {
|
||||
const { id } = req.params; // Property ID
|
||||
|
@ -172,13 +216,6 @@ export const getFundDetails = async (req, res) => {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// export const getProperties = async (req, res) => {
|
||||
// try {
|
||||
// const properties = await PropertyModal.find(); // Fetch all properties from MongoDB
|
||||
|
|
|
@ -1,35 +1,34 @@
|
|||
import jwt from "jsonwebtoken";
|
||||
import UserModel from "../models/user.js"
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const secret = process.env.SECRET_KEY
|
||||
const secret = process.env.SECRET_KEY;
|
||||
|
||||
const auth = async (req, res, next) => {
|
||||
try {
|
||||
|
||||
// Check if the authorization header exists
|
||||
if (!req.headers.authorization) {
|
||||
// Check if the authorization header exists
|
||||
if (!req.headers.authorization) {
|
||||
return res.status(401).json({ message: "Authorization header missing" });
|
||||
}
|
||||
|
||||
const token = req.headers.authorization.split(" ")[1];
|
||||
const isCustomAuth = token.length < 500;
|
||||
|
||||
const token = req.headers.authorization.split(" ")[1]; // Extract the token from 'Bearer <token>'
|
||||
const isCustomAuth = token.length < 500; // Check if it's a custom JWT or a third-party auth
|
||||
|
||||
let decodedData;
|
||||
|
||||
if (token && isCustomAuth) {
|
||||
decodedData = jwt.verify(token, secret);
|
||||
req.userId = decodedData?.id;
|
||||
decodedData = jwt.verify(token, secret); // Verify the custom JWT
|
||||
req.userId = decodedData?.id; // Set the user ID to request object
|
||||
} else {
|
||||
decodedData = jwt.decode(token);
|
||||
// const googleId = decodedData?.sub.toString();
|
||||
// const user = await UserModel.findOne({ googleId });
|
||||
// req.userId = user?._id;
|
||||
req.userId = decodedData?.id;
|
||||
decodedData = jwt.decode(token); // Decode third-party tokens (e.g., Google OAuth)
|
||||
req.userId = decodedData?.sub; // Usually for third-party tokens, user ID is in `sub`
|
||||
}
|
||||
next();
|
||||
|
||||
next(); // Continue to the next middleware or route handler
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.status(403).json({ message: "Authentication failed" });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -306,13 +306,16 @@ const propertySchema = mongoose.Schema({
|
|||
fundDetails: [
|
||||
{
|
||||
fundValue: {
|
||||
type: Number, // Adjust type if needed (String or Number)
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
fundPercentage: {
|
||||
type: Number, // Adjust type if needed
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
userId:{
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
getProperties,
|
||||
addFundDetails,
|
||||
getFundDetails,
|
||||
deleteFundDetail,
|
||||
} from "../controllers/property.js";
|
||||
|
||||
router.post("/", auth, createProperty);
|
||||
|
@ -18,5 +19,7 @@ router.put("/:id", updatePropertyById);
|
|||
router.get("/", getProperties);
|
||||
router.put("/:id/fund-details", addFundDetails);
|
||||
router.get("/:id/fund-details", getFundDetails);
|
||||
router.delete('/:id/fund-details/:fundDetailId', auth, deleteFundDetail);
|
||||
|
||||
|
||||
export default router;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -45,7 +45,7 @@
|
|||
|
||||
|
||||
|
||||
<script type="module" crossorigin src="/assets/index-DjwKvwco.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-CVyH-t6Z.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-iEl-il0E.css">
|
||||
</head>
|
||||
|
||||
|
|
|
@ -123,19 +123,43 @@ const Dashboard = () => {
|
|||
<span>
|
||||
Welcome to{" "}
|
||||
<span style={{ color: "#067ADC" }}>
|
||||
|
||||
|
||||
<NavLink
|
||||
to={`/profile/${user.result.userId}`}
|
||||
className="link-primary text-decoration-none"
|
||||
target="_blank"
|
||||
>
|
||||
{user.result.title}. {user.result.firstName}{" "}
|
||||
{user.result.middleName} {user.result.lastName}
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
to={`/profile/${user.result.userId}`}
|
||||
className="link-primary text-decoration-none"
|
||||
target="_blank"
|
||||
>
|
||||
{user.result.title}. {user.result.firstName}{" "}
|
||||
{user.result.middleName} {user.result.lastName}
|
||||
</NavLink>
|
||||
</span>
|
||||
</span>
|
||||
<br /> <br /> <br /> <br />
|
||||
|
||||
<div className="banner_taital">
|
||||
<h1 style={{ color: "#fda417", fontSize: "30px",
|
||||
padding: "10px",
|
||||
fontWeight: "normal" }} className="services_taital">
|
||||
Now you are accessing the world's only portal which has Streamlined the
|
||||
<h1 style={{ fontSize: "30px",
|
||||
padding: "10px",
|
||||
fontWeight: "normal" }}> investor-borrower interactions, </h1>
|
||||
|
||||
</h1>
|
||||
<h1 className="services_taital" style={{color: "#fda417",fontSize: "30px",
|
||||
padding: "10px",
|
||||
fontWeight: "normal" }} >
|
||||
gaining complete visibility
|
||||
into your data, and using smart filters to
|
||||
<h1 className="services_taital" style={{fontSize: "30px",
|
||||
padding: "10px",
|
||||
fontWeight: "normal" }} >create automatic
|
||||
workflows{" "}</h1>
|
||||
|
||||
</h1>
|
||||
<br /> <br /> <br /> <br /> <br />
|
||||
</div>
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
{/* Dynamic content based on the selected tab */}
|
||||
|
|
|
@ -8,7 +8,7 @@ import Footer from "./Footer";
|
|||
import { Modal, Button, Form } from "react-bootstrap"; // Importing Modal components
|
||||
import { addFundDetails } from "../redux/features/propertySlice";
|
||||
import { toast } from "react-toastify";
|
||||
import { fetchFundDetails} from "../redux/features/fundSlice";
|
||||
import { deleteFundDetail } from "../redux/features/propertySlice";
|
||||
|
||||
const PropertyView = () => {
|
||||
const { id } = useParams(); // Extract the property ID from the route
|
||||
|
@ -17,20 +17,7 @@ const PropertyView = () => {
|
|||
(state) => state.property
|
||||
);
|
||||
const { user } = useSelector((state) => ({ ...state.auth }));
|
||||
|
||||
// This also works !!
|
||||
// const fundDetails = useSelector(selectFundDetails);
|
||||
// console.log("fundDetailsa", fundDetails)
|
||||
|
||||
const {fundDetails} = useSelector((state) => state.fundDetails);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchFundDetails(id));
|
||||
}, [dispatch, id]);
|
||||
|
||||
|
||||
|
||||
|
||||
// console.log("usr", user.result.userId);
|
||||
|
||||
const [activeTab, setActiveTab] = useState("propertydetails");
|
||||
|
||||
|
@ -48,10 +35,6 @@ const PropertyView = () => {
|
|||
}
|
||||
}, [id, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchFundDetails(id));
|
||||
}, [dispatch, id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === "succeeded") {
|
||||
setLoading(false);
|
||||
|
@ -89,15 +72,30 @@ const PropertyView = () => {
|
|||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (formData.fundValue <= 0 || formData.fundPercentage <= 0) {
|
||||
return toast.error("Please enter valid funding details");
|
||||
}
|
||||
|
||||
const fundDetails = {
|
||||
fundValue: Number(formData.fundValue), // Ensure number conversion if needed
|
||||
fundPercentage: Number(formData.fundPercentage),
|
||||
userId: user.result._id,
|
||||
};
|
||||
|
||||
dispatch(addFundDetails({ id, fundDetails, toast }));
|
||||
handleCloseModal();
|
||||
};
|
||||
|
||||
// const handleDelete = (fundDetailId) => {
|
||||
// dispatch(deleteFundDetail({ id, fundDetailId }));
|
||||
// };
|
||||
|
||||
const handleDelete = (fundDetailId) => {
|
||||
const token = JSON.parse(localStorage.getItem("profile")).token;
|
||||
dispatch(deleteFundDetail({ id, fundDetailId, token }));
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
|
@ -200,7 +198,7 @@ const PropertyView = () => {
|
|||
className="fa fa-dollar"
|
||||
style={{ color: "#F74B02" }}
|
||||
/>{" "}
|
||||
{selectedProperty.totalCoststoBuyAtoB}
|
||||
{selectedProperty?.totalCoststoBuyAtoB || "N/A"}
|
||||
</span>
|
||||
</p>
|
||||
<p
|
||||
|
@ -219,7 +217,8 @@ const PropertyView = () => {
|
|||
className="fa fa-dollar"
|
||||
style={{ color: "#F74B02" }}
|
||||
/>{" "}
|
||||
{selectedProperty.NetProfit}
|
||||
{/* {selectedProperty.NetProfit} */}
|
||||
{selectedProperty?.NetProfit || "N/A"}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -389,44 +388,35 @@ const PropertyView = () => {
|
|||
<div>
|
||||
{activeTab === "Funding Details" && (
|
||||
<div className="tab-pane active">
|
||||
|
||||
<div>
|
||||
{selectedProperty.fundDetails.length === 0 ? (
|
||||
<p>No fund details available.</p>
|
||||
) : (
|
||||
<ul>
|
||||
{selectedProperty.fundDetails.map((detail, index) => (
|
||||
<li key={index}>
|
||||
Fund Value: {detail.fundValue}, Fund Percentage: {detail.fundPercentage}%
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
|
||||
{fundDetails.length === 0 ? (
|
||||
<p>No fund details available.</p>
|
||||
) : (
|
||||
<ul>
|
||||
{fundDetails.map((detail, index) => (
|
||||
<li key={index}>
|
||||
Fund Value: {detail.fundValue}, Fund Percentage: {detail.fundPercentage}%
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div>
|
||||
{selectedProperty.fundDetails.length === 0 ? (
|
||||
<p>No fund details available.</p>
|
||||
) : (
|
||||
<ul>
|
||||
{selectedProperty.fundDetails.map((detail, index) => (
|
||||
<li key={index}>
|
||||
Fund Value: {detail.fundValue}, Fund Percentage:{" "}
|
||||
{detail.fundPercentage}%
|
||||
{user?.result?.userId ? (
|
||||
<div>
|
||||
{user?.result?._id === detail.userId && (
|
||||
<button
|
||||
onClick={() => handleDelete(detail._id)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@ -435,7 +425,6 @@ const PropertyView = () => {
|
|||
</div>
|
||||
<Footer />
|
||||
{/* Modal for Investment/Funding */}
|
||||
{/* Modal for Investment/Funding */}
|
||||
<Modal show={showModal} onHide={handleCloseModal}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>Investment/Funding Details</Modal.Title>
|
||||
|
|
|
@ -0,0 +1,458 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { fetchPropertyById } from "../redux/features/propertySlice";
|
||||
import propertydummy from "../img/propertydummy.jpg";
|
||||
import Navbar from "./Navbar";
|
||||
import Footer from "./Footer";
|
||||
import { Modal, Button, Form } from "react-bootstrap"; // Importing Modal components
|
||||
import { addFundDetails } from "../redux/features/propertySlice";
|
||||
import { toast } from "react-toastify";
|
||||
import { fetchFundDetails } from "../redux/features/fundSlice";
|
||||
|
||||
const PropertyView = () => {
|
||||
const { id } = useParams(); // Extract the property ID from the route
|
||||
const dispatch = useDispatch();
|
||||
const { selectedProperty, status, error } = useSelector(
|
||||
(state) => state.property
|
||||
);
|
||||
const { user } = useSelector((state) => ({ ...state.auth }));
|
||||
|
||||
// This also works !!
|
||||
// const fundDetails = useSelector(selectFundDetails);
|
||||
// console.log("fundDetailsa", fundDetails)
|
||||
|
||||
const { fundDetails } = useSelector((state) => state.fundDetails);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchFundDetails(id));
|
||||
}, [dispatch, id]);
|
||||
|
||||
const [activeTab, setActiveTab] = useState("propertydetails");
|
||||
|
||||
const [loading, setLoading] = useState(true); // Loader state
|
||||
const [showModal, setShowModal] = useState(false); // Modal state
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
fundValue: "0",
|
||||
fundPercentage: "0",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
dispatch(fetchPropertyById(id));
|
||||
}
|
||||
}, [id, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === "succeeded") {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
// if (status === "loading") {
|
||||
// return <p>Loading property details...</p>;
|
||||
// }
|
||||
|
||||
if (status === "failed") {
|
||||
return <p>Error loading property: {error}</p>;
|
||||
}
|
||||
|
||||
// Handle conditions for the "Willing to Invest/Fund" button and messages
|
||||
const isOwner = user?.result?.userId === selectedProperty?.userId;
|
||||
const isLoggedIn = !!user;
|
||||
|
||||
const handleShowModal = () => {
|
||||
setShowModal(true); // Show the modal
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setShowModal(false); // Close the modal
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData({
|
||||
...formData,
|
||||
[name]: value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const fundDetails = {
|
||||
fundValue: Number(formData.fundValue), // Ensure number conversion if needed
|
||||
fundPercentage: Number(formData.fundPercentage),
|
||||
};
|
||||
|
||||
dispatch(addFundDetails({ id, fundDetails, toast }));
|
||||
handleCloseModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<br /> <br /> <br /> <br />
|
||||
<br /> <br />
|
||||
<div className="container tabs-wrap col-12">
|
||||
<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 === "Funding Details" ? "active tab" : "tab"}
|
||||
>
|
||||
<a onClick={() => setActiveTab("Funding Details")} role="tab">
|
||||
Funding Details
|
||||
</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 container tab-pane active col-12"
|
||||
>
|
||||
<div className="container col-12">
|
||||
{loading ? (
|
||||
<div className="loader">Loading...</div> // Loader
|
||||
) : selectedProperty ? (
|
||||
<div className="py-3 py-md-5 bg-light">
|
||||
<div className="card-header bg-white">
|
||||
<div className="row">
|
||||
<div className="col-md-5 mt-3">
|
||||
<div>
|
||||
{selectedProperty.images &&
|
||||
selectedProperty.images[0] ? (
|
||||
<img
|
||||
src={
|
||||
selectedProperty.images[0].file ||
|
||||
propertydummy
|
||||
}
|
||||
alt="Property Thumbnail"
|
||||
style={{
|
||||
marginTop: "0px",
|
||||
maxWidth: "400px",
|
||||
maxHeight: "400px",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
src={propertydummy}
|
||||
alt="Default Property Thumbnail"
|
||||
style={{
|
||||
marginTop: "0px",
|
||||
maxWidth: "400px",
|
||||
maxHeight: "400px",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* <img
|
||||
src={selectedProperty.images?.[0]?.file || propertydummy}
|
||||
alt="Property Thumbnail"
|
||||
style={{ marginTop: "0px", maxWidth: "400px", maxHeight: "400px" }}
|
||||
loading="lazy"
|
||||
/> */}
|
||||
</div>
|
||||
<br />
|
||||
<div className="label-stock border">
|
||||
<p
|
||||
style={{
|
||||
color: "#fda417",
|
||||
fontSize: "30px",
|
||||
padding: "10px",
|
||||
fontWeight: "normal",
|
||||
}}
|
||||
>
|
||||
Funding Required:{" "}
|
||||
<span
|
||||
style={{ color: "#000000", fontSize: "25px" }}
|
||||
>
|
||||
<span
|
||||
className="fa fa-dollar"
|
||||
style={{ color: "#F74B02" }}
|
||||
/>{" "}
|
||||
{selectedProperty.totalCoststoBuyAtoB}
|
||||
</span>
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
color: "#fda417",
|
||||
fontSize: "30px",
|
||||
padding: "10px",
|
||||
fontWeight: "normal",
|
||||
}}
|
||||
>
|
||||
Net profit:{" "}
|
||||
<span
|
||||
style={{ color: "#000000", fontSize: "25px" }}
|
||||
>
|
||||
<span
|
||||
className="fa fa-dollar"
|
||||
style={{ color: "#F74B02" }}
|
||||
/>{" "}
|
||||
{selectedProperty.NetProfit}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
{/* "Willing to Invest/Fund" Button and Conditional Messages */}
|
||||
<button
|
||||
className="btn btn-primary back"
|
||||
style={{
|
||||
backgroundColor: "#fda417",
|
||||
border: "#fda417",
|
||||
}}
|
||||
disabled={isOwner || !isLoggedIn}
|
||||
onClick={
|
||||
isOwner || !isLoggedIn ? null : handleShowModal
|
||||
} // Show modal only if not owner or logged in
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "25px",
|
||||
fontWeight: "normal",
|
||||
}}
|
||||
>
|
||||
Willing to Invest/Fund
|
||||
</span>{" "}
|
||||
</button>
|
||||
{isOwner && (
|
||||
<span style={{ color: "red", marginTop: "10px" }}>
|
||||
You cannot invest in your own property.
|
||||
</span>
|
||||
)}
|
||||
{!isLoggedIn && (
|
||||
<span style={{ color: "red", marginTop: "10px" }}>
|
||||
Please login to invest.
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-md-7 mt-3">
|
||||
<div className="product-view">
|
||||
<h4
|
||||
className="product-name"
|
||||
style={{ color: "#fda417", fontSize: "25px" }}
|
||||
>
|
||||
{selectedProperty.address}
|
||||
<label className="label-stock bg-success">
|
||||
Verified Property
|
||||
</label>
|
||||
</h4>
|
||||
<hr />
|
||||
<p className="product-path">
|
||||
<span
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
City:{" "}
|
||||
</span>{" "}
|
||||
{selectedProperty.city}
|
||||
{" "} /{" "}
|
||||
{" "}
|
||||
{" "}
|
||||
<span
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
County:{" "}
|
||||
</span>{" "}
|
||||
{selectedProperty.county} {" "}/{" "}
|
||||
{" "}
|
||||
<span
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
State:{" "}
|
||||
</span>{" "}
|
||||
{selectedProperty.state} {" "}/ {" "}
|
||||
{" "}
|
||||
<span
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
Zipcode:{" "}
|
||||
</span>{" "}
|
||||
{selectedProperty.zip}
|
||||
{" "}
|
||||
</p>
|
||||
<div>
|
||||
<span
|
||||
className="selling-price"
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
Total Living Square Foot:{" "}
|
||||
</span>
|
||||
{selectedProperty.totallivingsqft}
|
||||
<p></p>
|
||||
<span
|
||||
className=""
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
Cost per Square Foot:{" "}
|
||||
</span>
|
||||
${selectedProperty.costpersqft}/sqft
|
||||
<p></p>
|
||||
<span
|
||||
className=""
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
Year Built:{" "}
|
||||
</span>
|
||||
{selectedProperty.yearBuild}
|
||||
</div>
|
||||
|
||||
<div className="mt-3 card bg-white label-stock border">
|
||||
<h5
|
||||
className="mb-0"
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
Legal Description
|
||||
</h5>
|
||||
<span>
|
||||
{selectedProperty.legal
|
||||
? selectedProperty.legal
|
||||
: "No data available"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row">
|
||||
<div className="col-md-12 mt-3">
|
||||
<div className="card">
|
||||
<div className="card-header bg-white">
|
||||
<h4
|
||||
className="product-name"
|
||||
style={{ color: "#fda417", fontSize: "25px" }}
|
||||
>
|
||||
Description
|
||||
</h4>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<p>
|
||||
Lorem Ipsum is simply dummy text of the printing
|
||||
and typesetting industry. Lorem Ipsum has been
|
||||
the industry's standard dummy text ever since
|
||||
the 1500s, when an unknown printer took a galley
|
||||
of type and scrambled it to make a type specimen
|
||||
book. It has survived not only five centuries,
|
||||
but also the leap into electronic typesetting,
|
||||
remaining essentially unchanged. It was
|
||||
popularised in the 1960s with the release of
|
||||
Letraset sheets containing Lorem Ipsum passages,
|
||||
and more recently with desktop publishing
|
||||
software like Aldus PageMaker including versions
|
||||
of Lorem Ipsum.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p>No property found.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "Funding Details" && (
|
||||
<div>
|
||||
{activeTab === "Funding Details" && (
|
||||
<div className="tab-pane active">
|
||||
<div>
|
||||
{selectedProperty.fundDetails.length === 0 ? (
|
||||
<p>No fund details available.</p>
|
||||
) : (
|
||||
<ul>
|
||||
{selectedProperty.fundDetails.map((detail, index) => (
|
||||
<li key={index}>
|
||||
Fund Value: {detail.fundValue}, Fund Percentage:{" "}
|
||||
{detail.fundPercentage}%
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{fundDetails.length === 0 ? (
|
||||
<p>No fund details available.</p>
|
||||
) : (
|
||||
<ul>
|
||||
{fundDetails.map((detail, index) => (
|
||||
<li key={index}>
|
||||
Fund Value: {detail.fundValue}, Fund Percentage:{" "}
|
||||
{detail.fundPercentage}%
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "Accounting" && <div></div>}
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
{/* Modal for Investment/Funding */}
|
||||
{/* Modal for Investment/Funding */}
|
||||
<Modal show={showModal} onHide={handleCloseModal}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>Investment/Funding Details</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Form.Group>
|
||||
<Form.Label>Fund Value</Form.Label>
|
||||
<Form.Control
|
||||
type="number"
|
||||
id="fundValue"
|
||||
name="fundValue" // Added the name attribute
|
||||
value={formData.fundValue}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Label>Fund Percentage</Form.Label>
|
||||
<Form.Control
|
||||
type="number"
|
||||
id="fundPercentage"
|
||||
name="fundPercentage" // Added the name attribute
|
||||
value={formData.fundPercentage}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PropertyView;
|
|
@ -58,7 +58,6 @@ export const updateProperty = createAsyncThunk(
|
|||
try {
|
||||
const response = await api.updateProperty(id, propertyData);
|
||||
return response.data;
|
||||
|
||||
} catch (error) {
|
||||
return rejectWithValue(error.response.data);
|
||||
}
|
||||
|
@ -66,13 +65,16 @@ export const updateProperty = createAsyncThunk(
|
|||
);
|
||||
|
||||
export const addFundDetails = createAsyncThunk(
|
||||
'property/addFundDetails',
|
||||
"property/addFundDetails",
|
||||
async ({ id, fundDetails, toast }, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await axios.put(
|
||||
`${import.meta.env.VITE_REACT_APP_SECRET}/properties/${id}/fund-details`,
|
||||
fundDetails);
|
||||
toast.success("Submitted Successfully");
|
||||
`${
|
||||
import.meta.env.VITE_REACT_APP_SECRET
|
||||
}/properties/${id}/fund-details`,
|
||||
fundDetails
|
||||
);
|
||||
toast.success("Submitted Successfully");
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error.response.data);
|
||||
|
@ -80,6 +82,30 @@ export const addFundDetails = createAsyncThunk(
|
|||
}
|
||||
);
|
||||
|
||||
export const deleteFundDetail = createAsyncThunk(
|
||||
"property/deleteFundDetail",
|
||||
async ({ id, fundDetailId, token }, { rejectWithValue }) => {
|
||||
// console.log("Token received:", token, fundDetailId, id);
|
||||
try {
|
||||
const response = await axios.delete(
|
||||
`http://localhost:3002/properties/${id}/fund-details/${fundDetailId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`, // Use the token passed in as a parameter
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error.response.data);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// export const getProperties = createAsyncThunk("property/getProperties", async () => {
|
||||
// const response = await axios.get(`${import.meta.env.VITE_REACT_APP_SECRET}/properties`); // Backend endpoint
|
||||
|
@ -95,14 +121,17 @@ export const addFundDetails = createAsyncThunk(
|
|||
// );
|
||||
|
||||
export const getProperties = createAsyncThunk(
|
||||
'properties/getProperties',
|
||||
"properties/getProperties",
|
||||
async ({ page, limit, keyword = "" }) => {
|
||||
const response = await axios.get(`${import.meta.env.VITE_REACT_APP_SECRET}/properties?page=${page}&limit=${limit}&keyword=${keyword}`);
|
||||
const response = await axios.get(
|
||||
`${
|
||||
import.meta.env.VITE_REACT_APP_SECRET
|
||||
}/properties?page=${page}&limit=${limit}&keyword=${keyword}`
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
const propertySlice = createSlice({
|
||||
name: "property",
|
||||
initialState: {
|
||||
|
@ -115,6 +144,7 @@ const propertySlice = createSlice({
|
|||
currentPage: 1,
|
||||
loading: false,
|
||||
properties: [],
|
||||
fundDetails: [],
|
||||
},
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
|
@ -191,6 +221,21 @@ const propertySlice = createSlice({
|
|||
.addCase(addFundDetails.rejected, (state, action) => {
|
||||
state.loading = false;
|
||||
state.error = action.payload;
|
||||
})
|
||||
|
||||
.addCase(deleteFundDetail.pending, (state) => {
|
||||
state.loading = true;
|
||||
})
|
||||
.addCase(deleteFundDetail.fulfilled, (state, action) => {
|
||||
state.loading = false;
|
||||
// Remove the deleted fund detail from state
|
||||
state.fundDetails = state.fundDetails.filter(
|
||||
(detail) => detail._id !== action.meta.arg.id
|
||||
);
|
||||
})
|
||||
.addCase(deleteFundDetail.rejected, (state, action) => {
|
||||
state.loading = false;
|
||||
state.error = action.payload;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue