This commit is contained in:
omkieit 2024-10-21 19:51:48 +05:30
parent 3b05f20ffe
commit 51f1b4afbd
16 changed files with 650 additions and 222 deletions

View File

@ -0,0 +1,37 @@
import FundDetailsModal from "../models/fundDetails.js"
export const submitFundDetails = async (req, res) => {
const { propertyOwnerId, investorId, investmentAmount, ownershipPercentage, propertyId } = req.body;
const newFundDetail = new FundDetailsModal({
propertyOwnerId,
investorId,
investmentAmount,
ownershipPercentage,
propertyId
});
console.log("newFundDetail", newFundDetail)
try {
const savedFundDetail = await newFundDetail.save();
res.status(201).json(savedFundDetail);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
export const getFundDetailsById = async (req, res) => {
const { id } = req.params;
try {
const fundDetails = await FundDetailsModal.findOne({ propertyId: id });
if (!fundDetails) {
return res.status(404).json({ message: "Fund details not found" });
}
res.status(200).json(fundDetails);
} catch (error) {
res.status(500).json({ message: error.message });
}
};

View File

@ -7,6 +7,7 @@ import session from "express-session";
import userRouter from "./routes/user.js";
import propertyRouter from "./routes/property.js";
import mysqlRouter from "./routes/mysqlproperty.js";
import fundDetailsRouter from "./routes/fundDetails.js";
import dotenv from "dotenv";
const app = express();
@ -34,6 +35,7 @@ app.use(
app.use("/users", userRouter);
app.use("/properties", propertyRouter);
app.use("/mysql", mysqlRouter); // Use MySQL routes
app.use("/fundDetails", fundDetailsRouter);

View File

@ -0,0 +1,29 @@
import mongoose from "mongoose";
const fundDetailsSchema = new mongoose.Schema({
propertyOwnerId: {
type: String,
required: true,
},
investorId: {
type: String,
required: true,
},
investmentAmount: {
type: Number,
required: true,
},
ownershipPercentage: {
type: Number,
required: true,
},
propertyId:{
type: String,
required: true,
},
}, { timestamps: true });
const FundDetailsModal = mongoose.model("FundDetails", fundDetailsSchema);
export default FundDetailsModal;

View File

@ -1,74 +0,0 @@
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;

View File

@ -0,0 +1,10 @@
import express from 'express';
const router = express.Router();
import { submitFundDetails, getFundDetailsById } from '../controllers/fundDetails.js';
router.post('/', submitFundDetails);
router.get("/:id", getFundDetailsById);
export default router;

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

View File

@ -45,7 +45,7 @@
<script type="module" crossorigin src="/assets/index-7mv97drX.js"></script>
<script type="module" crossorigin src="/assets/index-B2o9sH6t.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-iEl-il0E.css">
</head>

215
ef-ui/package-lock.json generated
View File

@ -20,6 +20,7 @@
"primeicons": "^7.0.0",
"prop-types": "^15.8.1",
"react": "^18.3.1",
"react-bootstrap": "^2.10.5",
"react-dom": "^18.3.1",
"react-file-base64": "^1.0.3",
"react-icons": "^5.3.0",
@ -336,6 +337,18 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/runtime": {
"version": "7.25.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz",
"integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==",
"license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
"version": "7.25.0",
"dev": true,
@ -654,12 +667,26 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@react-aria/ssr": {
"version": "3.9.6",
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.6.tgz",
"integrity": "sha512-iLo82l82ilMiVGy342SELjshuWottlb5+VefO3jOQqQRNYnJBFpUSadswDPbRimSgJUZuFwIEYs6AabkP038fA==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
},
"engines": {
"node": ">= 12"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@reduxjs/toolkit": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.7.tgz",
@ -693,6 +720,48 @@
"node": ">=14.0.0"
}
},
"node_modules/@restart/hooks": {
"version": "0.4.16",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
"integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==",
"license": "MIT",
"dependencies": {
"dequal": "^2.0.3"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@restart/ui": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.8.0.tgz",
"integrity": "sha512-xJEOXUOTmT4FngTmhdjKFRrVVF0hwCLNPdatLCHkyS4dkiSK12cEu1Y0fjxktjJrdst9jJIc5J6ihMJCoWEN/g==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.21.0",
"@popperjs/core": "^2.11.6",
"@react-aria/ssr": "^3.5.0",
"@restart/hooks": "^0.4.9",
"@types/warning": "^3.0.0",
"dequal": "^2.0.3",
"dom-helpers": "^5.2.0",
"uncontrollable": "^8.0.1",
"warning": "^4.0.3"
},
"peerDependencies": {
"react": ">=16.14.0",
"react-dom": ">=16.14.0"
}
},
"node_modules/@restart/ui/node_modules/uncontrollable": {
"version": "8.0.4",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz",
"integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==",
"license": "MIT",
"peerDependencies": {
"react": ">=16.14.0"
}
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.21.0",
"cpu": [
@ -705,6 +774,15 @@
"win32"
]
},
"node_modules/@swc/helpers": {
"version": "0.5.13",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz",
"integrity": "sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
"dev": true,
@ -766,12 +844,27 @@
"@types/react": "*"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.11",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz",
"integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==",
"license": "MIT",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
"license": "MIT"
},
"node_modules/@types/warning": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
"integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==",
"license": "MIT"
},
"node_modules/@vitejs/plugin-react": {
"version": "4.3.1",
"dev": true,
@ -1135,6 +1228,12 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
"license": "MIT"
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@ -1326,6 +1425,15 @@
"node": ">=0.4.0"
}
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/doctrine": {
"version": "2.1.0",
"dev": true,
@ -1337,6 +1445,16 @@
"node": ">=0.10.0"
}
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
@ -2171,6 +2289,15 @@
"node": ">= 0.4"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/is-array-buffer": {
"version": "3.0.4",
"dev": true,
@ -3018,6 +3145,19 @@
"react-is": "^16.13.1"
}
},
"node_modules/prop-types-extra": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz",
"integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==",
"license": "MIT",
"dependencies": {
"react-is": "^16.3.2",
"warning": "^4.0.0"
},
"peerDependencies": {
"react": ">=0.14.0"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@ -3063,6 +3203,36 @@
"node": ">=0.10.0"
}
},
"node_modules/react-bootstrap": {
"version": "2.10.5",
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.5.tgz",
"integrity": "sha512-XueAOEn64RRkZ0s6yzUTdpFtdUXs5L5491QU//8ZcODKJNDLt/r01tNyriZccjgRImH1REynUc9pqjiRMpDLWQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.24.7",
"@restart/hooks": "^0.4.9",
"@restart/ui": "^1.6.9",
"@types/react-transition-group": "^4.4.6",
"classnames": "^2.3.2",
"dom-helpers": "^5.2.1",
"invariant": "^2.2.4",
"prop-types": "^15.8.1",
"prop-types-extra": "^1.1.0",
"react-transition-group": "^4.4.5",
"uncontrollable": "^7.2.1",
"warning": "^4.0.3"
},
"peerDependencies": {
"@types/react": ">=16.14.8",
"react": ">=16.14.0",
"react-dom": ">=16.14.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-dom": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
@ -3123,6 +3293,12 @@
"version": "16.13.1",
"license": "MIT"
},
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
"license": "MIT"
},
"node_modules/react-loading-icons": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-loading-icons/-/react-loading-icons-1.1.0.tgz",
@ -3223,6 +3399,22 @@
"react-dom": ">=18"
}
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"license": "BSD-3-Clause",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
@ -3258,6 +3450,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"license": "MIT"
},
"node_modules/regexp.prototype.flags": {
"version": "1.5.2",
"dev": true,
@ -3769,6 +3967,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/uncontrollable": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
"integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.6.3",
"@types/react": ">=16.9.11",
"invariant": "^2.2.4",
"react-lifecycles-compat": "^3.0.4"
},
"peerDependencies": {
"react": ">=15.0.0"
}
},
"node_modules/update-browserslist-db": {
"version": "1.1.0",
"dev": true,

View File

@ -22,6 +22,7 @@
"primeicons": "^7.0.0",
"prop-types": "^15.8.1",
"react": "^18.3.1",
"react-bootstrap": "^2.10.5",
"react-dom": "^18.3.1",
"react-file-base64": "^1.0.3",
"react-icons": "^5.3.0",

View File

@ -2,17 +2,25 @@ 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 propertydummy from "../img/propertydummy.jpg";
import Navbar from "./Navbar";
import Footer from "./Footer";
import { Modal, Button, Form } from "react-bootstrap"; // Importing Modal components
import { submitFundDetails } from "../redux/features/fundDetailsSlice";
import { useNavigate } from "react-router-dom";
const PropertyView = () => {
const { id } = useParams(); // Extract the property ID from the route
const dispatch = useDispatch();
const { selectedProperty, status, error } = useSelector((state) => state.property);
const navigate = useNavigate();
const { selectedProperty, status, error } = useSelector(
(state) => state.property
);
const { user } = useSelector((state) => ({ ...state.auth }));
const [loading, setLoading] = useState(true); // Loader state
const [showModal, setShowModal] = useState(false); // Modal state
const [fundValue, setFundValue] = useState(""); // Fund value state
const [fundPercentage, setFundPercentage] = useState(""); // Fund percentage state
useEffect(() => {
// Fetch the property by ID when the component loads
@ -30,32 +38,47 @@ const PropertyView = () => {
return <p>Error loading property: {error}</p>;
}
// console.log("selectedProperty", selectedProperty);
// 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
};
// Inside your PropertyView component
const handleSubmit = (e) => {
e.preventDefault();
const fundDetails = {
propertyOwnerId: selectedProperty?.userId,
investorId: user?.result?.userId,
investmentAmount: fundValue,
ownershipPercentage: fundPercentage,
propertyId: selectedProperty?.propertyId || "defaultId", // Add a fallback
};
dispatch(submitFundDetails({ fundDetails, id, navigate }));
handleCloseModal(); // Close modal after submission
};
return (
<>
<Navbar />
<Navbar />
<br /> <br /> <br /> <br />
<div className="container col-12">
{loading ? (
<div className="loader">Loading...</div> // Loader
) : selectedProperty ? (
<div className="py-3 py-md-5 bg-light">
{/* <div className="container"> */}
<div className="card-header bg-white">
<div className="row">
<div className="col-md-5 mt-3">
<div className="bg-white border">
{/* {selectedProperty.images && selectedProperty.images[0] && (
<img
src={selectedProperty.images[0].file || propertydummy}
alt="Property Thumbnail"
style={{ width: "100px", height: "auto" }}
/>
)} */}
<div>
{selectedProperty.images && selectedProperty.images[0] ? (
<img
src={selectedProperty.images[0].file || propertydummy}
@ -66,7 +89,7 @@ const PropertyView = () => {
maxHeight: "500px",
}}
/>
) : (
) : (
<img
src={propertydummy}
alt="Default Property Thumbnail"
@ -76,11 +99,75 @@ const PropertyView = () => {
maxHeight: "500px",
}}
/>
)}
)}
</div>
<br />
<div className="label-stock border">
<p
style={{
color: "#fda417",
fontSize: "30px",
padding: "10px",
fontWeight: "normal",
}}
>
Total Funding Amount:{" "}
<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 on your property.
</span>
)}
{!isLoggedIn && (
<span style={{ color: "red", marginTop: "10px" }}>
Please login to submit.
</span>
)}
</div>
<div className="col-md-7 mt-3">
<div className="product-view">
<h4
@ -95,21 +182,27 @@ const PropertyView = () => {
<hr />
<p className="product-path">
<span style={{ color: "#fda417", fontSize: "15px" }}>
city:{" "}
City:{" "}
</span>{" "}
{selectedProperty.city} /
{selectedProperty.city}
{" "} /{" "}
{" "}
{" "}
<span style={{ color: "#fda417", fontSize: "15px" }}>
County:{" "}
</span>{" "}
{selectedProperty.county} /
{selectedProperty.county} {" "}/{" "}
{" "}
<span style={{ color: "#fda417", fontSize: "15px" }}>
State:{" "}
</span>{" "}
{selectedProperty.state} /
{selectedProperty.state} {" "}/ {" "}
{" "}
<span style={{ color: "#fda417", fontSize: "15px" }}>
Zipcode:
Zipcode:{" "}
</span>{" "}
{selectedProperty.zip}
{" "}
</p>
<div>
<span
@ -137,8 +230,11 @@ const PropertyView = () => {
{selectedProperty.yearBuild}
</div>
<div className="mt-3 card bg-white">
<h5 className="mb-0" style={{ color: "#fda417", fontSize: "15px" }}>
<div className="mt-3 card bg-white label-stock border">
<h5
className="mb-0"
style={{ color: "#fda417", fontSize: "15px" }}
>
Legal Description
</h5>
<span>
@ -146,11 +242,11 @@ const PropertyView = () => {
? selectedProperty.legal
: "No data available"}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-md-12 mt-3">
<div className="card">
@ -183,11 +279,56 @@ const PropertyView = () => {
</div>
</div>
) : (
<p>No property details found.</p>
<p>No property found.</p>
)}
</div>
<Footer />
{/* 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 controlId="fundValue">
<Form.Label>Investment Amount ($)</Form.Label>
<Form.Control
type="number"
placeholder="Enter amount"
value={fundValue}
onChange={(e) => setFundValue(e.target.value)}
required
/>
</Form.Group>
<Form.Group controlId="fundPercentage">
<Form.Label>Ownership Percentage (%)</Form.Label>
<Form.Control
type="number"
placeholder="Enter percentage"
value={fundPercentage}
onChange={(e) => setFundPercentage(e.target.value)}
required
/>
</Form.Group>
<Button
variant="primary"
type="submit"
style={{ backgroundColor: "#fda417", border: "#fda417" }}
>
Submit
</Button>
{" "}
<Button
variant="primary"
onClick={handleCloseModal}
style={{ backgroundColor: "#d80b0b", border: "#d80b0b" }}
>
close
</Button>
</Form>
</Modal.Body>
</Modal>
</>
);
};

View File

@ -140,7 +140,6 @@ useEffect(() => {
<NavLink
to={`/property/${property.propertyId}`}
className="link-primary text-decoration-none"
target="_blank"
>
{property.address}
</NavLink>

View File

@ -91,7 +91,6 @@ const UserProperties = () => {
<div className="d-flex flex-column mt-4">
<NavLink
to={`/property/${property.propertyId}`}
target="_blank"
>
<button
className="btn btn-outline-primary btn-sm mt-2"

View File

@ -26,6 +26,7 @@ export const fetchUserProperties = (userId, page, limit) => API.get( `/propertie
export const fetchPropertyById = (id) => API.get(`/properties/${id}`, id);
export const updateProperty = (id, propertyData) => API.put(`/properties/${id}`, propertyData);
export const showUser = (userId) => API.get(`/users/${userId}`);
export const submitFundDetails = (fundDetails) => API.post(`/fundDetails`, fundDetails);

View File

@ -0,0 +1,65 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import * as api from "../api";
import { toast } from "react-toastify"; // Import toast here
import axios from "axios";
export const submitFundDetails = createAsyncThunk(
"fundDetails/submit",
async ({ fundDetails,id,navigate }, { rejectWithValue }) => {
try {
const response = await api.submitFundDetails(fundDetails, id);
toast.success("Details submitted successfully"); // Show toast on success
navigate(`/property/${id}`); // Navigate after successful submission
return response.data; // Assuming you return JSON data
} catch (error) {
toast.error("Failed to submit details"); // Show error toast
return rejectWithValue(error.response.data);
}
}
);
export const getFundDetailsById = createAsyncThunk(
"fundDetails/getFundDetailsById",
async (id, { rejectWithValue }) => {
try {
const response = await axios.get(`http://localhost:3002/fundDetails/${id}`);
return response.data;
} catch (error) {
return rejectWithValue(error.response.data);
}
}
);
const fundDetailsSlice = createSlice({
name: "fundDetails",
initialState: {
fundDetails: null,
status: null,
error: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(getFundDetailsById.pending, (state) => {
state.status = "loading";
})
.addCase(getFundDetailsById.fulfilled, (state, action) => {
state.status = "succeeded";
state.fundDetails = action.payload;
})
.addCase(getFundDetailsById.rejected, (state, action) => {
state.status = "failed";
state.error = action.payload;
})
.addCase(submitFundDetails.fulfilled, (state, action) => {
state.fundDetails = action.payload;
})
.addCase(submitFundDetails.rejected, (state, action) => {
state.error = action.payload;
});
},
});
export default fundDetailsSlice.reducer;

View File

@ -2,12 +2,13 @@ import { configureStore } from "@reduxjs/toolkit";
import AuthReducer from "./features/authSlice";
import userReducer from "./features/userSlice";
import propertyReducer from "./features/propertySlice";
import fundReducer from "./features/fundDetailsSlice"
export default configureStore({
reducer: {
auth: AuthReducer,
user: userReducer,
property: propertyReducer,
fundDetails:fundReducer,
},
});