This commit is contained in:
omkieit 2024-09-19 21:17:01 +05:30
parent 1549c32eb6
commit 3750a089f4
6 changed files with 275 additions and 156 deletions

View File

@ -29,7 +29,7 @@ db.connect((err) => {
console.log("Connected to the MYSQL database."); 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) => { // export const searchMySQL = (req, res) => {
// const q = "SELECT * FROM client_information"; // const q = "SELECT * FROM client_information";
// // const q = "SELECT * FROM home_information"; // // const q = "SELECT * FROM home_information";
@ -42,21 +42,60 @@ db.connect((err) => {
// }); // });
// }; // };
// 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) => {
// 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) => { export const searchMySQL = (req, res) => {
const limit = parseInt(req.query.limit) || 10; // Default to 10 items per page 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 offset = parseInt(req.query.offset) || 0; // Default to the first page
const q = `SELECT * FROM home_information LIMIT ${limit} OFFSET ${offset}`; const searchQuery = req.query.search || ''; // Get search query if provided
db.query(q, (err, data) => { // 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) { if (err) {
console.log(err); console.log(err);
return res.status(500).json({ error: "Database query failed" }); return res.status(500).json({ error: "Database query failed" });
} }
// 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 // Set cache-control headers to prevent caching
res.set('Cache-Control', 'no-store'); res.set('Cache-Control', 'no-store');
return res.json(data);
// 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

View File

@ -42,8 +42,8 @@
<!-- <script src="https://stackpath.bootstrapcdn.com/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script> --> <!-- <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 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> <script type="module" crossorigin src="/assets/index-Br5i4FR3.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DPr78kNB.css"> <link rel="stylesheet" crossorigin href="/assets/index-DlbKQED5.css">
</head> </head>

View File

@ -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 axios from "axios";
import Navbar from "./Navbar"; import Navbar from "./Navbar";
import Footer from "./Footer"; import Footer from "./Footer";
@ -9,26 +10,82 @@ const SearchMysql = () => {
const [totalRecords, setTotalRecords] = useState(0); const [totalRecords, setTotalRecords] = useState(0);
const [page, setPage] = useState(0); // Page number const [page, setPage] = useState(0); // Page number
const [limit] = useState(10); // Items per page 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 () => { const fetchProperties = async () => {
setLoading(true);
try { try {
const res = await axios.get( const res = await axios.get(
`${import.meta.env.VITE_REACT_APP_SECRET}/mysql/searchmysql`, `${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 headers: { "Cache-Control": "no-cache" }, // Disable caching
} }
); );
setProperties(res.data); setProperties(res.data.data); // Set properties
setTotalRecords(res.data.total); // Get total records from backend setTotalRecords(res.data.totalRecords); // Set total records
} catch (err) { } catch (err) {
console.log("Error fetching data:", 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(() => { useEffect(() => {
fetchProperties(); // Fetch default properties when the component mounts or page changes
if (search.trim() === "") {
fetchProperties(); // Fetch default properties
}
}, [page]); }, [page]);
const totalPages = Math.ceil(totalRecords / limit); const totalPages = Math.ceil(totalRecords / limit);
@ -38,7 +95,6 @@ const SearchMysql = () => {
<Navbar /> <Navbar />
<br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
{/* Display properties */} {/* Display properties */}
{properties.length > 0 ? (
<div className="container col-12"> <div className="container col-12">
<div className="row"> <div className="row">
<div className="col-lg-12 card-margin col-12"> <div className="col-lg-12 card-margin col-12">
@ -55,6 +111,10 @@ const SearchMysql = () => {
className="form-control" className="form-control"
id="search" id="search"
name="search" name="search"
value={search}
onChange={handleSearchChange}
onKeyDown={handleKeyPress}
onBlur={handleBlur} // Trigger search on blur
/> />
</div> </div>
<div className="col-lg-1 col-md-3 col-sm-12 p-0"></div> <div className="col-lg-1 col-md-3 col-sm-12 p-0"></div>
@ -70,6 +130,9 @@ const SearchMysql = () => {
<div className="col-12"> <div className="col-12">
<div className="card card-margin"> <div className="card card-margin">
<div className="card-body"> <div className="card-body">
{loading ? (
<div className="loader">Loading...</div> // Loader component
) : (
<div className="row search-body"> <div className="row search-body">
<div className="col-lg-12"> <div className="col-lg-12">
<div className="search-result col-12"> <div className="search-result col-12">
@ -82,6 +145,10 @@ const SearchMysql = () => {
<button <button
onClick={() => setPage(page - 1)} onClick={() => setPage(page - 1)}
disabled={page === 0} disabled={page === 0}
style={{
backgroundColor: "#fda417",
border: "#fda417",
}}
> >
Previous Previous
</button> </button>
@ -92,54 +159,68 @@ const SearchMysql = () => {
<button <button
onClick={() => setPage(page + 1)} onClick={() => setPage(page + 1)}
disabled={page + 1 >= totalPages} disabled={page + 1 >= totalPages}
style={{
backgroundColor: "#fda417",
border: "#fda417",
}}
> >
Next Next
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="result-body"> <div className="result-body">
<div className="table-responsive"> <div className="table-responsive">
<table className="table widget-26"> <table className="table widget-26">
<tbody> {/* Add table headers */}
{properties.map((property, index) => ( <thead>
<div key={index} className="property">
<tr> <tr>
<td> <th>Details</th>
<div className="widget-26-job-emp-img"> <th>Total Living Square Foot</th>
<img <th>Year Built</th>
src="https://bootdey.com/img/Content/avatar/avatar2.png" <th>Cost per Square Foot</th>
alt="Company" </tr>
/> </thead>
</div> <tbody>
</td> {properties.length > 0 ? (
properties.map((property, index) => (
<tr key={index}>
<td> <td>
<div className="widget-26-job-title"> <div className="widget-26-job-title">
<a href="#">{property.address}</a> <NavLink
<p className="m-0"> to={`/property/${property.house_id}`}
<span className="link-primary text-decoration-none"
className="employer-name"
> >
{property.city}, {property.county}, {property.state} {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>{" "} </span>{" "}
<p className="text-muted m-0"> <p className="text-muted m-0">
House Id: {property.house_id} House Id: {property.house_id}
</p> </p>
</p> </p>
</div> </div>
</td> </td>
<td> <td>
<div className="widget-26-job-info"> <div className="widget-26-job-info">
<p className="type m-0">Total Living Square foot</p> <p className="m-0">
<p className="text-muted m-0">
is{" "}
<span className="location">
{property.total_living_sqft} {property.total_living_sqft}
</span> </p>
</div>
</td>
<td>
<div className="widget-26-job-info">
<p className="m-0">
{property.year_built}
</p> </p>
</div> </div>
</td> </td>
@ -148,51 +229,26 @@ const SearchMysql = () => {
$ {property.cost_per_sqft}/sqft $ {property.cost_per_sqft}/sqft
</div> </div>
</td> </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> </tr>
<hr style={{color: "#fda417"}} /> ))
) : (
<tr>
<td colSpan="4">No results found</td>
</tr>
)}
</tbody>
</table>
</div>
</div> </div>
))}
{/* Pagination Controls */} {/* Pagination Controls */}
<div> <div>
<button <button
onClick={() => setPage(page - 1)} onClick={() => setPage(page - 1)}
disabled={page === 0} disabled={page === 0}
style={{
backgroundColor: "#fda417",
border: "#fda417",
}}
> >
Previous Previous
</button> </button>
@ -203,26 +259,23 @@ const SearchMysql = () => {
<button <button
onClick={() => setPage(page + 1)} onClick={() => setPage(page + 1)}
disabled={page + 1 >= totalPages} disabled={page + 1 >= totalPages}
style={{
backgroundColor: "#fda417",
border: "#fda417",
}}
> >
Next Next
</button> </button>
</div> </div>
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
) : (
<p>No properties found.</p>
)} )}
{/* Pagination Controls */} </div>
</div>
</div>
</div>
</div>
<Footer /> <Footer />
</> </>
); );

View File

@ -37,7 +37,7 @@ body{
margin: 0; margin: 0;
line-height: 1.5; line-height: 1.5;
font-weight: 400; font-weight: 400;
color: #3c4142; color: #000000;
font-size: 0.9125rem; font-size: 0.9125rem;
color: #3c4142; color: #3c4142;
} }
@ -59,7 +59,7 @@ body{
.widget-26 .widget-26-job-info p { .widget-26 .widget-26-job-info p {
line-height: 1.5; line-height: 1.5;
color: #3c4142; color: #000000;
font-size: 0.9125rem; font-size: 0.9125rem;
} }
@ -70,7 +70,7 @@ body{
.widget-26 .widget-26-job-salary { .widget-26 .widget-26-job-salary {
min-width: 70px; min-width: 70px;
font-weight: 400; font-weight: 400;
color: #3c4142; color: #000000;
font-size: 0.9125rem; font-size: 0.9125rem;
} }
@ -91,7 +91,7 @@ body{
.widget-26 .widget-26-job-category span { .widget-26 .widget-26-job-category span {
font-size: 0.8125rem; font-size: 0.8125rem;
color: #3c4142; color: #000000;
font-weight: 600; font-weight: 600;
} }
@ -170,7 +170,7 @@ body{
} }
.search-body .search-filters .filter-list .title { .search-body .search-filters .filter-list .title {
color: #3c4142; color: #000000;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
@ -183,7 +183,7 @@ body{
} }
.search-body .search-result .result-header .records { .search-body .search-result .result-header .records {
color: #3c4142; color: #000000;
} }
.search-body .search-result .result-header .result-actions { .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 { .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) { @media (min-width: 768px) and (max-width: 991.98px) {
@ -253,5 +253,32 @@ body{
border-radius: 8px; 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); }
}