done
This commit is contained in:
parent
9ab07ddfc0
commit
e66b5fc26d
|
@ -53,7 +53,6 @@ export const getUserProperties = async (req, res) => {
|
|||
.limit(Number(limit));
|
||||
const total = await PropertyModal.countDocuments({ userId });
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
res.json({
|
||||
properties,
|
||||
totalPages,
|
||||
|
@ -65,6 +64,16 @@ 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;
|
||||
|
|
|
@ -14,6 +14,9 @@ import {
|
|||
|
||||
router.post("/", auth, createProperty);
|
||||
router.get("/user/:userId", getUserProperties);
|
||||
|
||||
|
||||
|
||||
router.get("/:propertyId", getPropertyById);
|
||||
router.put("/:id", updatePropertyById);
|
||||
router.get("/", getProperties);
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -45,7 +45,7 @@
|
|||
|
||||
|
||||
|
||||
<script type="module" crossorigin src="/assets/index-E_rWKug7.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-CFxhPaJf.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DUngj0jf.css">
|
||||
</head>
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ import "../dashboard.css";
|
|||
import { useSelector } from "react-redux";
|
||||
import UserProfile from "./UserProfile";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import FundingsReceived from "./FundingsReceived";
|
||||
import OffersSubmitted from "./OffersSubmitted";
|
||||
|
||||
const Dashboard = () => {
|
||||
const [activeTab, setActiveTab] = useState("dashboard");
|
||||
|
@ -26,6 +28,23 @@ const Dashboard = () => {
|
|||
<UserProperties />
|
||||
</div>
|
||||
);
|
||||
|
||||
case "Fundings Received":
|
||||
return (
|
||||
<div>
|
||||
<h3>Fundings Received</h3>
|
||||
<FundingsReceived />
|
||||
</div>
|
||||
);
|
||||
|
||||
case "Offers Submitted":
|
||||
return (
|
||||
<div>
|
||||
<h3>Offers Submitted</h3>
|
||||
<OffersSubmitted />
|
||||
</div>
|
||||
);
|
||||
|
||||
case "closedProperties":
|
||||
return <p>These are your closed properties.</p>;
|
||||
default:
|
||||
|
@ -88,6 +107,29 @@ const Dashboard = () => {
|
|||
<span className="fa fa-home" style={{ color: "#F74B02" }} />
|
||||
<span>Active Properties</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`btn mt-3 ${
|
||||
activeTab === "Fundings Received" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("Fundings Received")}
|
||||
>
|
||||
<span className="fa fa-home" style={{ color: "#F74B02" }} />
|
||||
<span>Fundings Received</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`btn mt-3 ${
|
||||
activeTab === "Offers Submitted" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("Offers Submitted")}
|
||||
>
|
||||
<span className="fa fa-home" style={{ color: "#F74B02" }} />
|
||||
<span>Offers Submitted</span>
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
<button
|
||||
className={`btn mt-3 ${
|
||||
activeTab === "closedProperties" ? "active" : ""
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { fetchUserProperties } from "../redux/features/propertySlice";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import propertydummy from "../img/propertydummy.jpg";
|
||||
|
||||
const UserProperties = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { user } = useSelector((state) => ({ ...state.auth }));
|
||||
const { userProperties, totalPages } = useSelector((state) => state.property);
|
||||
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const limit = 5; // Number of properties per page
|
||||
|
||||
// Fetch user properties when "Active Properties" tab is selected
|
||||
useEffect(() => {
|
||||
dispatch(fetchUserProperties({ userId: user.result.userId, page, limit }));
|
||||
}, [dispatch, user?.result?.userId, page]);
|
||||
|
||||
// Pagination handler
|
||||
const handlePageChange = (newPage) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const fundedProperties = userProperties.filter(
|
||||
(property) =>
|
||||
property.fundDetails &&
|
||||
property.fundDetails.some(
|
||||
(fund) =>
|
||||
fund.fundValue !== undefined &&
|
||||
fund.fundPercentage !== undefined &&
|
||||
fund.userId
|
||||
)
|
||||
);
|
||||
|
||||
// console.log("fundedProperties", fundedProperties)
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{fundedProperties.length > 0 ? (
|
||||
<>
|
||||
<ul>
|
||||
{fundedProperties.map((property) => (
|
||||
<li key={property._id}>
|
||||
<div className="container">
|
||||
<div className="col-md-12">
|
||||
<div className="row p-2 bg-white border rounded mt-2">
|
||||
<div className="col-md-3 mt-1">
|
||||
<img
|
||||
src={property.images[0].file || propertydummy}
|
||||
className="w-70"
|
||||
alt="Img"
|
||||
style={{
|
||||
marginTop: "0px",
|
||||
maxWidth: "200px",
|
||||
maxHeight: "200px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6 mt-1">
|
||||
<h5>
|
||||
{" "}
|
||||
<NavLink
|
||||
to={`/property/${property.propertyId}`}
|
||||
>
|
||||
{property.address}
|
||||
{"....."}
|
||||
</NavLink>
|
||||
</h5>
|
||||
<div className="d-flex flex-row"></div>
|
||||
<div className="mt-1 mb-1 spec-1">
|
||||
<span>100% cotton</span>
|
||||
<span className="dot" />
|
||||
<span>Light weight</span>
|
||||
<span className="dot" />
|
||||
<span>
|
||||
Best finish
|
||||
<br />
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 mb-1 spec-1">
|
||||
<span>Unique design</span>
|
||||
<span className="dot" />
|
||||
<span>For men</span>
|
||||
<span className="dot" />
|
||||
<span>
|
||||
Casual
|
||||
<br />
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-justify text-truncate para mb-0">
|
||||
There are many variations of passages of
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div className="align-items-center align-content-center col-md-3 border-left mt-1">
|
||||
{/* <div className="d-flex flex-row align-items-center">
|
||||
<h4 className="mr-1">$14.99</h4>
|
||||
<span className="strike-text">$20.99</span>
|
||||
</div>
|
||||
<h6 className="text-success">Free shipping</h6> */}
|
||||
<div className="d-flex flex-column mt-4">
|
||||
<NavLink
|
||||
to={`/property/${property.propertyId}`}
|
||||
>
|
||||
<button
|
||||
className="btn btn-outline-primary btn-sm mt-2"
|
||||
type="button"
|
||||
style={{
|
||||
backgroundColor: "#fda417",
|
||||
border: "#fda417",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="fa fa-eye"
|
||||
style={{ color: "#F74B02" }}
|
||||
/>{" "}
|
||||
{" "}
|
||||
View Details
|
||||
</button>
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
to={`/editproperty/${property.propertyId}`}
|
||||
>
|
||||
<button
|
||||
className="btn btn-outline-primary btn-sm mt-2"
|
||||
type="button"
|
||||
style={{
|
||||
backgroundColor: "#fda417",
|
||||
border: "#fda417",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="fa fa-edit"
|
||||
style={{ color: "#F74B02" }}
|
||||
/>{" "}
|
||||
{" "}
|
||||
Edit Details ..
|
||||
</button>
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* Pagination Controls */}
|
||||
<div className="pagination">
|
||||
<button
|
||||
onClick={() => handlePageChange(page - 1)}
|
||||
disabled={page === 1}
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
|
||||
{/* Show page numbers */}
|
||||
{Array.from({ length: totalPages }, (_, index) => index + 1).map(
|
||||
(pageNumber) =>
|
||||
pageNumber === page || // Current page
|
||||
pageNumber === 1 || // First page
|
||||
pageNumber === totalPages || // Last page
|
||||
(pageNumber >= page - 1 && pageNumber <= page + 1) ? ( // Pages near current page
|
||||
<button
|
||||
key={pageNumber}
|
||||
onClick={() => handlePageChange(pageNumber)}
|
||||
disabled={page === pageNumber}
|
||||
>
|
||||
{pageNumber}
|
||||
</button>
|
||||
) : pageNumber === 2 || pageNumber === totalPages - 1 ? ( // Add "..." between non-adjacent pages
|
||||
<span key={pageNumber}>...</span>
|
||||
) : null
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => handlePageChange(page + 1)}
|
||||
disabled={page === totalPages}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p>No active properties found.</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserProperties;
|
|
@ -0,0 +1,191 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { getProperties } from "../redux/features/propertySlice"; // Redux action to fetch properties
|
||||
import { NavLink } from "react-router-dom";
|
||||
import propertydummy from "../img/propertydummy.jpg";
|
||||
|
||||
const OffersSubmitted = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { user } = useSelector((state) => ({ ...state.auth }));
|
||||
// const { userProperties, totalPages } = useSelector((state) => state.property);
|
||||
const { properties, totalPages } = useSelector((state) => state.property);
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const limit = 5; // Number of properties per page
|
||||
|
||||
// Fetch user properties when "Active Properties" tab is selected
|
||||
useEffect(() => {
|
||||
dispatch(getProperties({ page, limit }));
|
||||
}, [dispatch, user?.result?.userId, page]);
|
||||
|
||||
// Pagination handler
|
||||
const handlePageChange = (newPage) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const OffersSubmitted = properties.filter(
|
||||
(property) =>
|
||||
property.fundDetails &&
|
||||
property.fundDetails.some(
|
||||
(fund) =>
|
||||
fund.fundValue !== undefined &&
|
||||
fund.fundPercentage !== undefined &&
|
||||
fund.userId === user.result._id
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{OffersSubmitted.length > 0 ? (
|
||||
<>
|
||||
<ul>
|
||||
{OffersSubmitted.map((property) => (
|
||||
<li key={property._id}>
|
||||
<div className="container">
|
||||
<div className="col-md-12">
|
||||
<div className="row p-2 bg-white border rounded mt-2">
|
||||
<div className="col-md-3 mt-1">
|
||||
<img
|
||||
src={property.images[0].file || propertydummy}
|
||||
className="w-70"
|
||||
alt="Img"
|
||||
style={{
|
||||
marginTop: "0px",
|
||||
maxWidth: "200px",
|
||||
maxHeight: "200px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6 mt-1">
|
||||
<h5>
|
||||
{" "}
|
||||
<NavLink
|
||||
to={`/property/${property.propertyId}`}
|
||||
target="_blank"
|
||||
>
|
||||
{property.address}
|
||||
{"....."}
|
||||
</NavLink>
|
||||
</h5>
|
||||
<div className="d-flex flex-row"></div>
|
||||
<div className="mt-1 mb-1 spec-1">
|
||||
<span>100% cotton</span>
|
||||
<span className="dot" />
|
||||
<span>Light weight</span>
|
||||
<span className="dot" />
|
||||
<span>
|
||||
Best finish
|
||||
<br />
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 mb-1 spec-1">
|
||||
<span>Unique design</span>
|
||||
<span className="dot" />
|
||||
<span>For men</span>
|
||||
<span className="dot" />
|
||||
<span>
|
||||
Casual
|
||||
<br />
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-justify text-truncate para mb-0">
|
||||
There are many variations of passages of
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div className="align-items-center align-content-center col-md-3 border-left mt-1">
|
||||
{/* <div className="d-flex flex-row align-items-center">
|
||||
<h4 className="mr-1">$14.99</h4>
|
||||
<span className="strike-text">$20.99</span>
|
||||
</div>
|
||||
<h6 className="text-success">Free shipping</h6> */}
|
||||
<div className="d-flex flex-column mt-4">
|
||||
<NavLink to={`/property/${property.propertyId}`}>
|
||||
<button
|
||||
className="btn btn-outline-primary btn-sm mt-2"
|
||||
type="button"
|
||||
style={{
|
||||
backgroundColor: "#fda417",
|
||||
border: "#fda417",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="fa fa-eye"
|
||||
style={{ color: "#F74B02" }}
|
||||
/>{" "}
|
||||
{" "}
|
||||
View Details
|
||||
</button>
|
||||
</NavLink>
|
||||
|
||||
<NavLink to={`/editproperty/${property.propertyId}`}>
|
||||
<button
|
||||
className="btn btn-outline-primary btn-sm mt-2"
|
||||
type="button"
|
||||
style={{
|
||||
backgroundColor: "#fda417",
|
||||
border: "#fda417",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="fa fa-edit"
|
||||
style={{ color: "#F74B02" }}
|
||||
/>{" "}
|
||||
{" "}
|
||||
Edit Details ..
|
||||
</button>
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* Pagination Controls */}
|
||||
<div className="pagination">
|
||||
<button
|
||||
onClick={() => handlePageChange(page - 1)}
|
||||
disabled={page === 1}
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
|
||||
{/* Show page numbers */}
|
||||
{Array.from({ length: totalPages }, (_, index) => index + 1).map(
|
||||
(pageNumber) =>
|
||||
pageNumber === page || // Current page
|
||||
pageNumber === 1 || // First page
|
||||
pageNumber === totalPages || // Last page
|
||||
(pageNumber >= page - 1 && pageNumber <= page + 1) ? ( // Pages near current page
|
||||
<button
|
||||
key={pageNumber}
|
||||
onClick={() => handlePageChange(pageNumber)}
|
||||
disabled={page === pageNumber}
|
||||
>
|
||||
{pageNumber}
|
||||
</button>
|
||||
) : pageNumber === 2 || pageNumber === totalPages - 1 ? ( // Add "..." between non-adjacent pages
|
||||
<span key={pageNumber}>...</span>
|
||||
) : null
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => handlePageChange(page + 1)}
|
||||
disabled={page === totalPages}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p>No active properties found.</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default OffersSubmitted;
|
|
@ -297,7 +297,8 @@ const PropertyView = () => {
|
|||
</label>
|
||||
</h4>
|
||||
<hr />
|
||||
<p className="product-path">
|
||||
|
||||
<span className="product-path">
|
||||
<span
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
|
@ -328,7 +329,9 @@ const PropertyView = () => {
|
|||
</span>{" "}
|
||||
{selectedProperty.zip}
|
||||
{" "}
|
||||
</p>
|
||||
</span>
|
||||
|
||||
<br /> <br />
|
||||
<div>
|
||||
<span
|
||||
className="selling-price"
|
||||
|
@ -339,7 +342,7 @@ const PropertyView = () => {
|
|||
{selectedProperty.totallivingsqft}
|
||||
<p></p>
|
||||
<span
|
||||
className=""
|
||||
className="selling-price"
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
Cost per Square Foot:{" "}
|
||||
|
@ -347,7 +350,7 @@ const PropertyView = () => {
|
|||
${selectedProperty.costpersqft}/sqft
|
||||
<p></p>
|
||||
<span
|
||||
className=""
|
||||
className="selling-price"
|
||||
style={{ color: "#fda417", fontSize: "15px" }}
|
||||
>
|
||||
Year Built:{" "}
|
||||
|
@ -425,7 +428,7 @@ const PropertyView = () => {
|
|||
<li key={index} className="d-flex align-items-center">
|
||||
<div className="d-flex align-items-center">
|
||||
<span>
|
||||
Total Fund Value: {detail.fundValue}, Interest Percentage: {detail.fundPercentage}%
|
||||
Total Fund Value: $ {detail.fundValue}, Interest Percentage: {detail.fundPercentage}%
|
||||
|
||||
</span>
|
||||
<br /> <br />
|
||||
|
|
|
@ -23,6 +23,8 @@ export const submitProperty = (propertyData) => API.post("/properties", property
|
|||
// export const fetchUserProperties = (userId) => API.get(`/properties/user/${userId}`, userId);
|
||||
|
||||
export const fetchUserProperties = (userId, page, limit) => API.get( `/properties/user/${userId}?page=${page}&limit=${limit}`, userId);
|
||||
|
||||
|
||||
export const fetchPropertyById = (id) => API.get(`/properties/${id}`, id);
|
||||
export const updateProperty = (id, propertyData) => API.put(`/properties/${id}`, propertyData);
|
||||
|
||||
|
|
|
@ -40,6 +40,9 @@ export const fetchUserProperties = createAsyncThunk(
|
|||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
export const fetchPropertyById = createAsyncThunk(
|
||||
"property/fetchPropertyById",
|
||||
async (id, { rejectWithValue }) => {
|
||||
|
@ -145,6 +148,7 @@ const propertySlice = createSlice({
|
|||
loading: false,
|
||||
properties: [],
|
||||
fundDetails: [],
|
||||
|
||||
},
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
|
@ -236,7 +240,13 @@ const propertySlice = createSlice({
|
|||
.addCase(deleteFundDetail.rejected, (state, action) => {
|
||||
state.loading = false;
|
||||
state.error = action.payload;
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue