done
This commit is contained in:
parent
252e91fc77
commit
d54bcdb9c5
|
@ -103,11 +103,37 @@ export const updatePropertyById = async (req, res) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// export const getProperties = async (req, res) => {
|
||||||
|
// try {
|
||||||
|
// const properties = await PropertyModal.find(); // Fetch all properties from MongoDB
|
||||||
|
// res.status(200).json(properties);
|
||||||
|
// } catch (error) {
|
||||||
|
// res.status(500).json({ message: "Server error" });
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
// controllers/propertyController.js
|
||||||
export const getProperties = async (req, res) => {
|
export const getProperties = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const properties = await PropertyModal.find(); // Fetch all properties from MongoDB
|
const { page = 1, limit = 10 } = req.query;
|
||||||
res.status(200).json(properties);
|
const skip = (page - 1) * limit;
|
||||||
} catch (error) {
|
const totalProperties = await PropertyModal.countDocuments();
|
||||||
res.status(500).json({ message: "Server error" });
|
const properties = await PropertyModal.find()
|
||||||
|
.sort({ createdAt: -1 })
|
||||||
|
.skip(skip)
|
||||||
|
.limit(parseInt(limit));
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: properties,
|
||||||
|
totalPages: Math.ceil(totalProperties / limit),
|
||||||
|
currentPage: parseInt(page), // Include the currentPage in the response
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ success: false, message: 'Server error' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -45,7 +45,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="module" crossorigin src="/assets/index-D1RcI06_.js"></script>
|
<script type="module" crossorigin src="/assets/index-DJmr6ji6.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DepkKhoc.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DepkKhoc.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { fetchPropertyById } from "../redux/features/propertySlice";
|
import { fetchPropertyById } from "../redux/features/propertySlice";
|
||||||
import propertydummy from "../img/propertydummy.jpg";
|
import propertydummy from "../img/propertydummy.jpg"
|
||||||
import Navbar from "./Navbar";
|
import Navbar from "./Navbar";
|
||||||
import Footer from "./Footer";
|
import Footer from "./Footer";
|
||||||
|
|
||||||
|
@ -48,16 +48,37 @@ const PropertyView = () => {
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-5 mt-3">
|
<div className="col-md-5 mt-3">
|
||||||
<div className="bg-white border">
|
<div className="bg-white border">
|
||||||
|
{/* {selectedProperty.images && selectedProperty.images[0] && (
|
||||||
<img
|
<img
|
||||||
src={propertydummy}
|
src={selectedProperty.images[0].file || propertydummy}
|
||||||
className="w-70"
|
alt="Property Thumbnail"
|
||||||
alt="Img"
|
style={{ width: "100px", height: "auto" }}
|
||||||
|
/>
|
||||||
|
)} */}
|
||||||
|
|
||||||
|
{selectedProperty.images && selectedProperty.images[0] ? (
|
||||||
|
<img
|
||||||
|
src={selectedProperty.images[0].file || propertydummy}
|
||||||
|
alt="Property Thumbnail"
|
||||||
style={{
|
style={{
|
||||||
marginTop: "0px",
|
marginTop: "0px",
|
||||||
maxWidth: "450px",
|
maxWidth: "500px",
|
||||||
maxHeight: "450px",
|
maxHeight: "500px",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src={propertydummy}
|
||||||
|
alt="Default Property Thumbnail"
|
||||||
|
style={{
|
||||||
|
marginTop: "0px",
|
||||||
|
maxWidth: "500px",
|
||||||
|
maxHeight: "500px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-7 mt-3">
|
<div className="col-md-7 mt-3">
|
||||||
|
|
|
@ -1,39 +1,52 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
import { getProperties } from "../redux/features/propertySlice"; // Redux action to fetch properties
|
import { getProperties } from "../redux/features/propertySlice"; // Redux action to fetch properties
|
||||||
|
import profilepic from "../img/propertydummy.jpg";
|
||||||
|
import { NavLink } from "react-router-dom";
|
||||||
import Navbar from "./Navbar";
|
import Navbar from "./Navbar";
|
||||||
import Footer from "./Footer";
|
import Footer from "./Footer";
|
||||||
import "../searchmysqlresults.css";
|
import "../searchmysqlresults.css";
|
||||||
|
|
||||||
const SearchProperties = () => {
|
const SearchProperties = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { properties, loading } = useSelector((state) => state.property); // Get loading and properties state from Redux
|
const { properties, loading, totalPages, currentPage } = useSelector(
|
||||||
const [searchTerm, setSearchTerm] = useState(""); // For storing search keyword
|
(state) => state.property
|
||||||
|
);
|
||||||
|
const [keyword, setKeyword] = useState("");
|
||||||
const [filteredProperties, setFilteredProperties] = useState([]);
|
const [filteredProperties, setFilteredProperties] = useState([]);
|
||||||
|
|
||||||
// Dispatch getProperties to load properties on mount
|
// Set pagination state
|
||||||
useEffect(() => {
|
const [page, setPage] = useState(1);
|
||||||
dispatch(getProperties());
|
const limit = 10; // Number of results per page
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
console.log("properties", properties);
|
// Dispatch getProperties to load properties on page change
|
||||||
|
|
||||||
// Filter properties based on search term
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (searchTerm) {
|
dispatch(getProperties({ page, limit }));
|
||||||
const filtered = properties.filter((property) =>
|
}, [dispatch, page]);
|
||||||
property.details.toLowerCase().includes(searchTerm.toLowerCase())
|
|
||||||
|
// Filter properties based on the keyword entered
|
||||||
|
useEffect(() => {
|
||||||
|
if (keyword.trim()) {
|
||||||
|
setFilteredProperties(
|
||||||
|
properties.filter((property) =>
|
||||||
|
property.address.toLowerCase().includes(keyword.toLowerCase())
|
||||||
|
)
|
||||||
);
|
);
|
||||||
setFilteredProperties(filtered);
|
|
||||||
} else {
|
} else {
|
||||||
setFilteredProperties(properties); // Show all properties if no search term
|
setFilteredProperties(properties); // Show all properties when no keyword
|
||||||
}
|
}
|
||||||
}, [searchTerm, properties]);
|
}, [keyword, properties]);
|
||||||
|
|
||||||
const handleSearch = (e) => {
|
const handleSearchChange = (e) => {
|
||||||
if (e.key === "Enter") {
|
setKeyword(e.target.value);
|
||||||
setSearchTerm(e.target.value);
|
};
|
||||||
}
|
|
||||||
|
const handleSearchSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (newPage) => {
|
||||||
|
setPage(newPage); // Update the current page when user clicks pagination
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -50,16 +63,17 @@ const SearchProperties = () => {
|
||||||
<div className="col-lg-12 card-margin col-12">
|
<div className="col-lg-12 card-margin col-12">
|
||||||
<div className="card search-form col-12">
|
<div className="card search-form col-12">
|
||||||
<div className="card-body p-0">
|
<div className="card-body p-0">
|
||||||
<form id="search-form">
|
<form id="search-form" onSubmit={handleSearchSubmit}>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<div className="row no-gutters">
|
<div className="row no-gutters">
|
||||||
<div className="col-lg-8 col-md-6 col-sm-12 p-0">
|
<div className="col-lg-8 col-md-6 col-sm-12 p-0">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
value={keyword}
|
||||||
|
onChange={handleSearchChange}
|
||||||
placeholder="Enter the keyword and hit ENTER button..."
|
placeholder="Enter the keyword and hit ENTER button..."
|
||||||
className="form-control"
|
className="form-control"
|
||||||
onKeyDown={handleSearch}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,6 +97,7 @@ const SearchProperties = () => {
|
||||||
<table className="table widget-26">
|
<table className="table widget-26">
|
||||||
<thead style={{ color: "#fda417", fontSize: "15px" }}>
|
<thead style={{ color: "#fda417", fontSize: "15px" }}>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th>Image</th>
|
||||||
<th>Details</th>
|
<th>Details</th>
|
||||||
<th>Total Living Square Foot</th>
|
<th>Total Living Square Foot</th>
|
||||||
<th>Year Built</th>
|
<th>Year Built</th>
|
||||||
|
@ -93,15 +108,59 @@ const SearchProperties = () => {
|
||||||
{filteredProperties.length > 0 ? (
|
{filteredProperties.length > 0 ? (
|
||||||
filteredProperties.map((property) => (
|
filteredProperties.map((property) => (
|
||||||
<tr key={property.id}>
|
<tr key={property.id}>
|
||||||
{/* <td>{property.details}</td> */}
|
<td>
|
||||||
<td>{property.address}</td>
|
|
||||||
{/* <td>{property.yearBuilt}</td>
|
{property.images && property.images[0] ? (
|
||||||
<td>{property.costPerSqFt}</td> */}
|
<img
|
||||||
|
src={property.images[0].file || profilepic}
|
||||||
|
alt="Property Thumbnail"
|
||||||
|
style={{ width: "100px", height: "auto" }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src={profilepic}
|
||||||
|
alt="Default Property Thumbnail"
|
||||||
|
style={{ width: "100px", height: "auto" }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="widget-26-job-title">
|
||||||
|
<NavLink
|
||||||
|
to={`/property/${property.propertyId}`}
|
||||||
|
className="link-primary text-decoration-none"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{property.address}
|
||||||
|
</NavLink>
|
||||||
|
<p className="m-0">
|
||||||
|
<span className="employer-name">
|
||||||
|
<i
|
||||||
|
className="fa fa-map-marker"
|
||||||
|
style={{ color: "#F74B02" }}
|
||||||
|
/>
|
||||||
|
{property.city}, {property.county},{" "}
|
||||||
|
{property.state}
|
||||||
|
</span>
|
||||||
|
<p className="text-muted m-0">
|
||||||
|
House Id: {property.propertyId}
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>{property.totallivingsqft}</td>
|
||||||
|
<td>{property.yearBuild}</td>
|
||||||
|
<td>{property.costpersqft}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan="4">No properties found.</td>
|
<td colSpan="5">No properties found.</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -112,6 +171,48 @@ const SearchProperties = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Pagination Controls */}
|
||||||
|
{/* Pagination */}
|
||||||
|
<nav aria-label="Page navigation">
|
||||||
|
<ul className="pagination justify-content-center">
|
||||||
|
<li className={`page-item ${currentPage === 1 ? "disabled" : ""}`}>
|
||||||
|
<button
|
||||||
|
className="page-link"
|
||||||
|
onClick={() => handlePageChange(currentPage - 1)}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
{Array.from({ length: totalPages }, (_, index) => (
|
||||||
|
<li
|
||||||
|
key={index + 1}
|
||||||
|
className={`page-item ${
|
||||||
|
currentPage === index + 1 ? "active" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="page-link"
|
||||||
|
onClick={() => handlePageChange(index + 1)}
|
||||||
|
>
|
||||||
|
{index + 1}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
<li
|
||||||
|
className={`page-item ${
|
||||||
|
currentPage === totalPages ? "disabled" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="page-link"
|
||||||
|
onClick={() => handlePageChange(currentPage + 1)}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -64,10 +64,18 @@ export const updateProperty = createAsyncThunk(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getProperties = createAsyncThunk("property/getProperties", async () => {
|
// export const getProperties = createAsyncThunk("property/getProperties", async () => {
|
||||||
const response = await axios.get(`${import.meta.env.VITE_REACT_APP_SECRET}/properties`); // Backend endpoint
|
// const response = await axios.get(`${import.meta.env.VITE_REACT_APP_SECRET}/properties`); // Backend endpoint
|
||||||
|
// return response.data;
|
||||||
|
// });
|
||||||
|
|
||||||
|
export const getProperties = createAsyncThunk(
|
||||||
|
'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}`);
|
||||||
return response.data;
|
return response.data;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const propertySlice = createSlice({
|
const propertySlice = createSlice({
|
||||||
name: "property",
|
name: "property",
|
||||||
|
@ -139,7 +147,9 @@ const propertySlice = createSlice({
|
||||||
})
|
})
|
||||||
.addCase(getProperties.fulfilled, (state, action) => {
|
.addCase(getProperties.fulfilled, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.properties = action.payload;
|
state.properties = action.payload.data;
|
||||||
|
state.totalPages = action.payload.totalPages;
|
||||||
|
state.currentPage = action.payload.currentPage;
|
||||||
})
|
})
|
||||||
.addCase(getProperties.rejected, (state, action) => {
|
.addCase(getProperties.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
|
|
Loading…
Reference in New Issue