done
This commit is contained in:
parent
1549c32eb6
commit
3750a089f4
|
@ -29,7 +29,7 @@ db.connect((err) => {
|
|||
console.log("Connected to the MYSQL database.");
|
||||
});
|
||||
|
||||
// Controller function
|
||||
// Controller function to get the data from the table from MYSQL with no pagination and huge data cant be getting
|
||||
// export const searchMySQL = (req, res) => {
|
||||
// const q = "SELECT * FROM client_information";
|
||||
// // const q = "SELECT * FROM home_information";
|
||||
|
@ -42,21 +42,60 @@ db.connect((err) => {
|
|||
// });
|
||||
// };
|
||||
|
||||
export const searchMySQL = (req, res) => {
|
||||
const limit = parseInt(req.query.limit) || 10; // Default to 10 items per page
|
||||
const offset = parseInt(req.query.offset) || 0; // Default to the first page
|
||||
const q = `SELECT * FROM home_information LIMIT ${limit} OFFSET ${offset}`;
|
||||
// Controller function to get all data from MYSQL table with pagination
|
||||
// export const searchMySQL = (req, res) => {
|
||||
// const limit = parseInt(req.query.limit) || 10; // Default to 10 items per page
|
||||
// const offset = parseInt(req.query.offset) || 0; // Default to the first page
|
||||
// const q = `SELECT * FROM home_information LIMIT ${limit} OFFSET ${offset}`;
|
||||
|
||||
db.query(q, (err, data) => {
|
||||
// db.query(q, (err, data) => {
|
||||
// if (err) {
|
||||
// console.log(err);
|
||||
// return res.status(500).json({ error: "Database query failed" });
|
||||
// }
|
||||
// // Set cache-control headers to prevent caching
|
||||
// res.set('Cache-Control', 'no-store');
|
||||
// return res.json(data);
|
||||
// });
|
||||
// };
|
||||
|
||||
// Controller function to get all data from MYSQL table with pagination and total records
|
||||
export const searchMySQL = (req, res) => {
|
||||
const limit = parseInt(req.query.limit) || 10; // Default to 10 items per page
|
||||
const offset = parseInt(req.query.offset) || 0; // Default to the first page
|
||||
const searchQuery = req.query.search || ''; // Get search query if provided
|
||||
|
||||
// Query to get total count of records
|
||||
const countQuery = `SELECT COUNT(*) as total FROM home_information WHERE address LIKE ?`;
|
||||
|
||||
// Query to fetch paginated data
|
||||
const dataQuery = `SELECT * FROM home_information WHERE address LIKE ? LIMIT ? OFFSET ?`;
|
||||
|
||||
// Perform count query
|
||||
db.query(countQuery, [`%${searchQuery}%`], (err, countResult) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return res.status(500).json({ error: "Database query failed" });
|
||||
}
|
||||
// Set cache-control headers to prevent caching
|
||||
res.set('Cache-Control', 'no-store');
|
||||
return res.json(data);
|
||||
|
||||
// Get the total count from the query result
|
||||
const totalRecords = countResult[0].total;
|
||||
|
||||
// Perform data query
|
||||
db.query(dataQuery, [`%${searchQuery}%`, limit, offset], (err, data) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return res.status(500).json({ error: "Database query failed" });
|
||||
}
|
||||
|
||||
// Set cache-control headers to prevent caching
|
||||
res.set('Cache-Control', 'no-store');
|
||||
|
||||
// Return both count and paginated data in the response
|
||||
return res.json({
|
||||
data,
|
||||
totalRecords
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -42,8 +42,8 @@
|
|||
<!-- <script src="https://stackpath.bootstrapcdn.com/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script> -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<script type="module" crossorigin src="/assets/index-vceAjfBL.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DPr78kNB.css">
|
||||
<script type="module" crossorigin src="/assets/index-Br5i4FR3.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DlbKQED5.css">
|
||||
</head>
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import axios from "axios";
|
||||
import Navbar from "./Navbar";
|
||||
import Footer from "./Footer";
|
||||
|
@ -9,26 +10,82 @@ const SearchMysql = () => {
|
|||
const [totalRecords, setTotalRecords] = useState(0);
|
||||
const [page, setPage] = useState(0); // Page number
|
||||
const [limit] = useState(10); // Items per page
|
||||
const [search, setSearch] = useState(""); // Search query state
|
||||
const [loading, setLoading] = useState(false); // Loader state
|
||||
|
||||
// Fetch properties from API
|
||||
const fetchProperties = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`${import.meta.env.VITE_REACT_APP_SECRET}/mysql/searchmysql`,
|
||||
{
|
||||
params: { limit, offset: page * limit },
|
||||
params: { limit, offset: page * limit, search },
|
||||
headers: { "Cache-Control": "no-cache" }, // Disable caching
|
||||
}
|
||||
);
|
||||
setProperties(res.data);
|
||||
setTotalRecords(res.data.total); // Get total records from backend
|
||||
setProperties(res.data.data); // Set properties
|
||||
setTotalRecords(res.data.totalRecords); // Set total records
|
||||
} catch (err) {
|
||||
console.log("Error fetching data:", err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Debounce the search input
|
||||
const debounce = (func, delay) => {
|
||||
let timer;
|
||||
return (...args) => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => func(...args), delay);
|
||||
};
|
||||
};
|
||||
|
||||
// Debounced fetch properties
|
||||
const debouncedFetchProperties = useCallback(
|
||||
debounce(() => {
|
||||
if (search.trim() === "") {
|
||||
fetchProperties(); // Fetch default properties when search is empty
|
||||
} else {
|
||||
fetchProperties(); // Fetch properties with new search term
|
||||
}
|
||||
}, 3000), // 3 seconds delay
|
||||
[search, page]
|
||||
);
|
||||
|
||||
// Handle search input change
|
||||
const handleSearchChange = (e) => {
|
||||
setSearch(e.target.value);
|
||||
// Trigger search only if input is non-empty
|
||||
if (e.target.value.trim() === "") {
|
||||
setPage(0); // Reset to first page
|
||||
fetchProperties(); // Fetch default properties immediately
|
||||
}
|
||||
};
|
||||
|
||||
// Handle key press events (Enter)
|
||||
const handleKeyPress = (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
if (search.trim()) {
|
||||
debouncedFetchProperties(); // Fetch properties on Enter key
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Handle input blur to fetch results after typing
|
||||
const handleBlur = () => {
|
||||
if (search.trim() !== "") {
|
||||
debouncedFetchProperties(); // Fetch properties after typing is complete
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch data when page changes
|
||||
useEffect(() => {
|
||||
fetchProperties();
|
||||
// Fetch default properties when the component mounts or page changes
|
||||
if (search.trim() === "") {
|
||||
fetchProperties(); // Fetch default properties
|
||||
}
|
||||
}, [page]);
|
||||
|
||||
const totalPages = Math.ceil(totalRecords / limit);
|
||||
|
@ -38,38 +95,44 @@ const SearchMysql = () => {
|
|||
<Navbar />
|
||||
<br /> <br /> <br /> <br /> <br /> <br />
|
||||
{/* Display properties */}
|
||||
{properties.length > 0 ? (
|
||||
<div className="container col-12">
|
||||
<div className="row">
|
||||
<div className="col-lg-12 card-margin col-12">
|
||||
<div className="card search-form col-12">
|
||||
<div className="card-body p-0">
|
||||
<form id="search-form">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="row no-gutters">
|
||||
<div className="col-lg-8 col-md-6 col-sm-12 p-0">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
className="form-control"
|
||||
id="search"
|
||||
name="search"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-lg-1 col-md-3 col-sm-12 p-0"></div>
|
||||
<div className="container col-12">
|
||||
<div className="row">
|
||||
<div className="col-lg-12 card-margin col-12">
|
||||
<div className="card search-form col-12">
|
||||
<div className="card-body p-0">
|
||||
<form id="search-form">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="row no-gutters">
|
||||
<div className="col-lg-8 col-md-6 col-sm-12 p-0">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
className="form-control"
|
||||
id="search"
|
||||
name="search"
|
||||
value={search}
|
||||
onChange={handleSearchChange}
|
||||
onKeyDown={handleKeyPress}
|
||||
onBlur={handleBlur} // Trigger search on blur
|
||||
/>
|
||||
</div>
|
||||
<div className="col-lg-1 col-md-3 col-sm-12 p-0"></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="card card-margin">
|
||||
<div className="card-body">
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="card card-margin">
|
||||
<div className="card-body">
|
||||
{loading ? (
|
||||
<div className="loader">Loading...</div> // Loader component
|
||||
) : (
|
||||
<div className="row search-body">
|
||||
<div className="col-lg-12">
|
||||
<div className="search-result col-12">
|
||||
|
@ -77,11 +140,15 @@ const SearchMysql = () => {
|
|||
<div className="row">
|
||||
<div className="col-lg-6">
|
||||
<div className="records">
|
||||
{/* Pagination Controls */}
|
||||
<div>
|
||||
{/* Pagination Controls */}
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setPage(page - 1)}
|
||||
disabled={page === 0}
|
||||
style={{
|
||||
backgroundColor: "#fda417",
|
||||
border: "#fda417",
|
||||
}}
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
|
@ -92,137 +159,123 @@ const SearchMysql = () => {
|
|||
<button
|
||||
onClick={() => setPage(page + 1)}
|
||||
disabled={page + 1 >= totalPages}
|
||||
style={{
|
||||
backgroundColor: "#fda417",
|
||||
border: "#fda417",
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className="result-body">
|
||||
<div className="table-responsive">
|
||||
<table className="table widget-26">
|
||||
{/* Add table headers */}
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Details</th>
|
||||
<th>Total Living Square Foot</th>
|
||||
<th>Year Built</th>
|
||||
<th>Cost per Square Foot</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{properties.map((property, index) => (
|
||||
<div key={index} className="property">
|
||||
<tr>
|
||||
<td>
|
||||
<div className="widget-26-job-emp-img">
|
||||
<img
|
||||
src="https://bootdey.com/img/Content/avatar/avatar2.png"
|
||||
alt="Company"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
{properties.length > 0 ? (
|
||||
properties.map((property, index) => (
|
||||
<tr key={index}>
|
||||
<td>
|
||||
<div className="widget-26-job-title">
|
||||
<a href="#">{property.address}</a>
|
||||
<NavLink
|
||||
to={`/property/${property.house_id}`}
|
||||
className="link-primary text-decoration-none"
|
||||
>
|
||||
{property.address}
|
||||
</NavLink>
|
||||
<p className="m-0">
|
||||
<span
|
||||
className="employer-name"
|
||||
>
|
||||
{property.city}, {property.county}, {property.state}
|
||||
<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.house_id}
|
||||
</p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="widget-26-job-info">
|
||||
<p className="type m-0">Total Living Square foot</p>
|
||||
<p className="text-muted m-0">
|
||||
is{" "}
|
||||
<span className="location">
|
||||
{property.total_living_sqft}
|
||||
</span>
|
||||
<p className="m-0">
|
||||
{property.total_living_sqft}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="widget-26-job-info">
|
||||
<p className="m-0">
|
||||
{property.year_built}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="widget-26-job-salary">
|
||||
$ {property.cost_per_sqft}/sqft
|
||||
$ {property.cost_per_sqft}/sqft
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="widget-26-job-info">
|
||||
<p className="type m-0">Year built</p>
|
||||
<p className="text-muted m-0">
|
||||
|
||||
<span className="location">
|
||||
{property.year_built}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
{/* <td>
|
||||
<div className="widget-26-job-starred">
|
||||
<a href="#">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={24}
|
||||
height={24}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="feather feather-star"
|
||||
>
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</td> */}
|
||||
|
||||
</tr>
|
||||
<hr style={{color: "#fda417"}} />
|
||||
</div>
|
||||
|
||||
))}
|
||||
{/* Pagination Controls */}
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setPage(page - 1)}
|
||||
disabled={page === 0}
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
<span>
|
||||
{" "}
|
||||
Page {page + 1} of {totalPages}{" "}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setPage(page + 1)}
|
||||
disabled={page + 1 >= totalPages}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="4">No results found</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/* Pagination Controls */}
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setPage(page - 1)}
|
||||
disabled={page === 0}
|
||||
style={{
|
||||
backgroundColor: "#fda417",
|
||||
border: "#fda417",
|
||||
}}
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
<span>
|
||||
{" "}
|
||||
Page {page + 1} of {totalPages}{" "}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setPage(page + 1)}
|
||||
disabled={page + 1 >= totalPages}
|
||||
style={{
|
||||
backgroundColor: "#fda417",
|
||||
border: "#fda417",
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p>No properties found.</p>
|
||||
)}
|
||||
{/* Pagination Controls */}
|
||||
</div>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -37,7 +37,7 @@ body{
|
|||
margin: 0;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
color: #3c4142;
|
||||
color: #000000;
|
||||
font-size: 0.9125rem;
|
||||
color: #3c4142;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ body{
|
|||
|
||||
.widget-26 .widget-26-job-info p {
|
||||
line-height: 1.5;
|
||||
color: #3c4142;
|
||||
color: #000000;
|
||||
font-size: 0.9125rem;
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ body{
|
|||
.widget-26 .widget-26-job-salary {
|
||||
min-width: 70px;
|
||||
font-weight: 400;
|
||||
color: #3c4142;
|
||||
color: #000000;
|
||||
font-size: 0.9125rem;
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ body{
|
|||
|
||||
.widget-26 .widget-26-job-category span {
|
||||
font-size: 0.8125rem;
|
||||
color: #3c4142;
|
||||
color: #000000;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
@ -170,7 +170,7 @@ body{
|
|||
}
|
||||
|
||||
.search-body .search-filters .filter-list .title {
|
||||
color: #3c4142;
|
||||
color: #000000;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
@ -183,7 +183,7 @@ body{
|
|||
}
|
||||
|
||||
.search-body .search-result .result-header .records {
|
||||
color: #3c4142;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.search-body .search-result .result-header .result-actions {
|
||||
|
@ -208,7 +208,7 @@ body{
|
|||
}
|
||||
|
||||
.search-body .search-result .result-header .result-actions .result-sorting select option {
|
||||
color: #3c4142;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 991.98px) {
|
||||
|
@ -253,5 +253,32 @@ body{
|
|||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.loader {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
border: 8px solid #f3f3f3;
|
||||
border-top: 8px solid #3498db;
|
||||
border-radius: 50%;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue