This commit is contained in:
omkieit 2024-09-26 19:56:23 +05:30
parent 252e91fc77
commit d54bcdb9c5
6 changed files with 222 additions and 64 deletions

View File

@ -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

View File

@ -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>

View File

@ -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">

View File

@ -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 />
</> </>

View File

@ -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;