Skip to content

Commit 03a1d8b

Browse files
committed
Facet fix
1 parent 0754282 commit 03a1d8b

File tree

9 files changed

+207
-88
lines changed

9 files changed

+207
-88
lines changed

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ ENV/
1111
env.bak/
1212
venv.bak/
1313

14+
# Ignore all .env files anywhere in the repository
15+
**/.env*
16+
17+
# Ignore all node_modules directories anywhere in the repository
18+
**/node_modules/
19+
20+
# Ignore all build directories anywhere in the repository
21+
**/build/
22+
1423
# Spyder project settings
1524
.spyderproject
1625
.spyproject

api/http/search.http

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ content-type: application/json
44
{
55
"q": "dog",
66
"top": 3,
7-
"skip": 5,
7+
"skip": 5
88

99
}

client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"version": "0.0.0",
55
"type": "module",
66
"scripts": {
7-
"dev": "vite",
7+
"dev": "vite --host",
88
"build": "vite build",
99
"lint": "eslint .",
1010
"preview": "vite preview"
Lines changed: 142 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,164 @@
1-
import React, {useState, useEffect} from 'react';
1+
import React, { useMemo } from 'react';
2+
import PropTypes from 'prop-types';
23

34
import './Pager.css';
45

6+
// Constants
7+
const PAGE_WINDOW = 2; // Pages to show before and after the current page
8+
9+
/**
10+
* Pagination component for search results - fully controlled by parent
11+
* @param {Object} props
12+
* @param {number} props.currentPage - Current active page number (1-based)
13+
* @param {number} props.resultCount - Total number of results across all pages
14+
* @param {number} props.resultsPerPage - Number of results displayed per page
15+
* @param {function} props.onPageChange - Callback function when page changes
16+
*/
517
export default function Pager(props) {
6-
7-
let [selectedPage, setSelectedPage] = useState(props.currentPage);
8-
let totalPages = Math.ceil(props.resultCount / props.resultsPerPage);
9-
10-
useEffect(_=>{
11-
props.setCurrentPage(selectedPage);
12-
}, [selectedPage, props]);
13-
14-
function goToNextPage() {
15-
setSelectedPage(selectedPage + 1);
16-
}
17-
18-
function goToPreviousPage() {
19-
setSelectedPage(selectedPage - 1);
18+
// Destructure props for cleaner code and proper dependency tracking
19+
const { currentPage, resultCount, resultsPerPage, onPageChange } = props;
20+
21+
// Ensure currentPage is always an integer
22+
const page = parseInt(currentPage) || 1;
23+
const totalPages = Math.max(1, Math.ceil(resultCount / resultsPerPage));
24+
25+
// Handler for changing the current page
26+
function handlePageChange(pageNumber) {
27+
// Convert to integer and clamp within valid range
28+
const newPage = Math.max(1, Math.min(totalPages, parseInt(pageNumber) || 1));
29+
30+
// Only update if actually changing page
31+
if (newPage !== page) {
32+
onPageChange(newPage);
33+
}
2034
}
2135

22-
var i = 0;
23-
var page_links = [];
24-
25-
var minPage = 1;
26-
var maxPage = totalPages;
27-
28-
if (selectedPage - minPage > 2) {
29-
minPage = selectedPage - 2;
36+
// Handler for next page button click
37+
function handleNextPage() {
38+
if (page < totalPages) {
39+
handlePageChange(page + 1);
40+
}
3041
}
3142

32-
if (maxPage - selectedPage > 2) {
33-
maxPage = parseInt(selectedPage) + 2;
43+
// Handler for previous page button click
44+
function handlePreviousPage() {
45+
if (page > 1) {
46+
handlePageChange(page - 1);
47+
}
3448
}
35-
36-
37-
for (i = minPage; i <= maxPage; i++) {
38-
if (i === parseInt(selectedPage)) {
39-
page_links.push(
40-
<li className="page-item active" key={i}>
41-
<span className="page-link">
42-
{i}
43-
</span>
44-
</li>
45-
);
46-
} else {
47-
page_links.push(
48-
<li className="page-item" key={i}>
49-
<button className="page-link" id={i} onClick={(e) => setSelectedPage(e.currentTarget.id)}>{i}</button>
50-
</li>
51-
);
49+
50+
// Calculate page range and memoize to avoid recalculation on every render
51+
const { minPage, maxPage } = useMemo(() => {
52+
let minPage = Math.max(1, page - PAGE_WINDOW);
53+
let maxPage = Math.min(totalPages, page + PAGE_WINDOW);
54+
55+
// Adjust range if we're near the start or end
56+
// This ensures we always show 5 pages if available
57+
if (maxPage - minPage < PAGE_WINDOW * 2) {
58+
if (page < totalPages / 2) {
59+
// Near start, expand end
60+
maxPage = Math.min(totalPages, minPage + PAGE_WINDOW * 2);
61+
} else {
62+
// Near end, expand start
63+
minPage = Math.max(1, maxPage - PAGE_WINDOW * 2);
64+
}
5265
}
66+
67+
return { minPage, maxPage };
68+
}, [page, totalPages]);
69+
70+
// Generate page links array
71+
function renderPageLinks() {
72+
const links = [];
73+
74+
for (let i = minPage; i <= maxPage; i++) {
75+
if (i === page) {
76+
links.push(
77+
<li className="page-item active" key={i}>
78+
<span className="page-link" aria-current="page">
79+
{i}
80+
</span>
81+
</li>
82+
);
83+
} else {
84+
links.push(
85+
<li className="page-item" key={i}>
86+
<button
87+
className="page-link"
88+
onClick={() => handlePageChange(i)}
89+
aria-label={`Go to page ${i}`}>
90+
{i}
91+
</button>
92+
</li>
93+
);
94+
}
95+
}
96+
return links;
5397
}
5498

55-
var previousButton;
56-
if (parseInt(selectedPage) === 1) {
57-
previousButton = (<li className="page-item disabled" key="prev">
58-
<span className="page-link">Previous</span>
59-
</li>);
60-
} else {
61-
previousButton = (<li className="page-item" key="prev" onClick={goToPreviousPage}>
62-
<button className="page-link">Previous</button>
63-
</li>);
99+
// Create previous button component
100+
function renderPreviousButton() {
101+
const isFirstPage = page === 1;
102+
return (
103+
<li className={`page-item ${isFirstPage ? 'disabled' : ''}`} key="prev">
104+
{isFirstPage ? (
105+
<span className="page-link">Previous</span>
106+
) : (
107+
<button
108+
className="page-link"
109+
onClick={handlePreviousPage}
110+
aria-label="Go to previous page">
111+
Previous
112+
</button>
113+
)}
114+
</li>
115+
);
64116
}
65117

66-
var nextButton;
67-
if (parseInt(selectedPage) === totalPages) {
68-
nextButton = (<li className="page-item disabled" key="next">
69-
<span className="page-link">Next</span>
70-
</li>);
71-
} else {
72-
nextButton = (<li className="page-item" key="next" >
73-
<button className="page-link" onClick={goToNextPage}>Next</button>
74-
</li>);
118+
// Create next button component
119+
function renderNextButton() {
120+
const isLastPage = page === totalPages;
121+
return (
122+
<li className={`page-item ${isLastPage ? 'disabled' : ''}`} key="next">
123+
{isLastPage ? (
124+
<span className="page-link">Next</span>
125+
) : (
126+
<button
127+
className="page-link"
128+
onClick={handleNextPage}
129+
aria-label="Go to next page">
130+
Next
131+
</button>
132+
)}
133+
</li>
134+
);
75135
}
76136

77-
137+
// Handle case with no results
138+
if (totalPages <= 0) {
139+
return null; // No pagination needed when there are no results
140+
}
78141

79142
return (
80-
<nav aria-label="..." className="pager">
143+
<nav aria-label="Search results pagination" className="pager">
81144
<ul className="pagination item">
82-
{previousButton}
83-
{page_links}
84-
{nextButton}
145+
{renderPreviousButton()}
146+
{renderPageLinks()}
147+
{renderNextButton()}
85148
</ul>
86149
</nav>
87150
);
88-
89-
}
151+
}
152+
153+
// PropTypes for better documentation and runtime type checking
154+
Pager.propTypes = {
155+
currentPage: PropTypes.number,
156+
resultCount: PropTypes.number.isRequired,
157+
resultsPerPage: PropTypes.number.isRequired,
158+
onPageChange: PropTypes.func.isRequired
159+
};
160+
161+
// Default props
162+
Pager.defaultProps = {
163+
currentPage: 1
164+
};

client/src/components/SearchBar/SearchBar.jsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,11 @@ export default function SearchBar({ postSearchHandler, query, width }) {
77
const [q, setQ] = useState(() => query || '');
88
const [suggestions, setSuggestions] = useState([]);
99

10-
console.log(`width = ${width}`);
11-
1210
const search = (value) => {
13-
console.log(`search: ${value}`);
1411
postSearchHandler(value);
1512
};
1613

1714
useEffect(() => {
18-
console.log(`useEffect getSuggestions: ${q}`);
1915
if (q) {
2016

2117
const body = { q, top: 5, suggester: 'sg' };
@@ -33,19 +29,17 @@ export default function SearchBar({ postSearchHandler, query, width }) {
3329

3430

3531
const onInputChangeHandler = (event, value) => {
36-
console.log(`onInputChangeHandler: ${value}`);
3732
setQ(value);
3833
};
3934

4035

4136
const onChangeHandler = (event, value) => {
42-
console.log(`onChangeHandler: ${value}`);
37+
4338
setQ(value);
4439
search(value);
4540
};
4641

4742
const onEnterButton = (event) => {
48-
console.log(`onEnterButton: ${q}`);
4943
// if enter key is pressed
5044
if (event.key === 'Enter') {
5145
search(q);
@@ -78,7 +72,6 @@ export default function SearchBar({ postSearchHandler, query, width }) {
7872
/>
7973
<div className="search-button" >
8074
<Button variant="contained" color="primary" onClick={() => {
81-
console.log(`search button: ${q}`);
8275
search(q)
8376
}
8477
}>

client/src/pages/Search/Search.jsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,33 @@ export default function Search() {
2727

2828
let resultsPerPage = top;
2929

30+
// Handle page changes in a controlled manner
31+
function handlePageChange(newPage) {
32+
setCurrentPage(newPage);
33+
}
34+
35+
// Calculate skip value and fetch results when relevant parameters change
3036
useEffect(() => {
37+
// Calculate skip based on current page
38+
const calculatedSkip = (currentPage - 1) * top;
39+
40+
// Only update if skip has actually changed
41+
if (calculatedSkip !== skip) {
42+
setSkip(calculatedSkip);
43+
return; // Skip the fetch since skip will change and trigger another useEffect
44+
}
45+
46+
// Proceed with fetch
3147
setIsLoading(true);
32-
setSkip((currentPage - 1) * top);
33-
48+
3449
const body = {
3550
q: q,
3651
top: top,
3752
skip: skip,
3853
filters: filters
3954
};
4055

56+
4157
fetchInstance('/api/search', { body, method: 'POST' })
4258
.then(response => {
4359
setResults(response.results);
@@ -49,7 +65,6 @@ export default function Search() {
4965
console.log(error);
5066
setIsLoading(false);
5167
});
52-
5368
}, [q, top, skip, filters, currentPage]);
5469

5570
// pushing the new search term to history when q is updated
@@ -63,7 +78,6 @@ export default function Search() {
6378

6479

6580
let postSearchHandler = (searchTerm) => {
66-
//console.log(searchTerm);
6781
setQ(searchTerm);
6882
}
6983

@@ -97,7 +111,7 @@ export default function Search() {
97111
) : (
98112
<div className="search-results-container">
99113
<Results documents={results} top={top} skip={skip} count={resultCount} query={q}></Results>
100-
<Pager className="pager-style" currentPage={currentPage} resultCount={resultCount} resultsPerPage={resultsPerPage} setCurrentPage={setCurrentPage}></Pager>
114+
<Pager className="pager-style" currentPage={currentPage} resultCount={resultCount} resultsPerPage={resultsPerPage} onPageChange={handlePageChange}></Pager>
101115
</div>
102116
)}
103117
</div>

client/src/url-fetch.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
const baseURL = import.meta.env.VITE_REACT_APP_BACKEND_URL || 'http://localhost:7071';
1+
// Use localhost in development, empty string in production for relative URLs
2+
const baseURL = import.meta.env.DEV
3+
? (import.meta.env.VITE_REACT_APP_BACKEND_URL || 'http://localhost:7071')
4+
: (import.meta.env.VITE_REACT_APP_BACKEND_URL || '');
25
console.log(`baseURL = ${baseURL}`);
6+
console.log(`Environment: ${import.meta.env.MODE}`);
37

48
function buildQueryString(params) {
59
return Object.keys(params)
@@ -9,11 +13,8 @@ function buildQueryString(params) {
913

1014
async function fetchInstance(url, { query = {}, body = null, headers = {}, method = 'GET' } = {}) {
1115
const queryString = buildQueryString(query);
12-
const fullUrl = `${baseURL}${url}${queryString ? `?${queryString}` : ''}`;
13-
14-
console.log(`fetching ${fullUrl} with method ${method}`);
15-
console.log(`headers:`, headers);
16-
console.log(`body:`, body);
16+
// Handle empty baseURL for production (relative URLs)
17+
const fullUrl = baseURL ? `${baseURL}${url}${queryString ? `?${queryString}` : ''}` : `${url}${queryString ? `?${queryString}` : ''}`;
1718

1819
const response = await fetch(fullUrl, {
1920
method,

0 commit comments

Comments
 (0)