Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 54 additions & 27 deletions frontend/my-react-app/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const Header: React.FC = () => {
const open = Boolean(anchorEl);
const navigate = useNavigate();
const { isAuthenticated, logout, user } = useAuth();
const [headerSearch, setHeaderSearch] = useState<string>("");
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Header's headerSearch state is not synchronized with the URL search parameter. When a user navigates to the events page with a search query (e.g., from a bookmark or direct link to /events?search=test), the Header's search input will be empty instead of showing "test".

Consider using useSearchParams and a useEffect in the Header component to sync the headerSearch state with the URL parameter when the location changes, similar to how it's implemented in EventsPage.tsx (lines 125-131).

Copilot uses AI. Check for mistakes.

{/* Gotta handle functions at the start! */}
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
Expand Down Expand Up @@ -109,34 +110,60 @@ const Header: React.FC = () => {

{/* Search Bar */}
<Box sx={{ flexGrow: 1, maxWidth: "600px", mx: 4 }}>
<TextField
fullWidth
placeholder="Search for events..."
variant="outlined"
size="small"
sx={{
"& .MuiOutlinedInput-root": {
backgroundColor: "white",
borderRadius: "25px",
"& fieldset": {
borderColor: "var(--charcoal-25)",
},
"&:hover fieldset": {
borderColor: "var(--fluorescent-cyan)",
},
"&.Mui-focused fieldset": {
borderColor: "var(--fluorescent-cyan)",
},
},
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon sx={{ color: "var(--charcoal-50)" }} />
</InputAdornment>
),
<form
onSubmit={(e) => {
e.preventDefault();
const trimmed = headerSearch.trim();
if (trimmed.length === 0) {
navigate('/events');
} else {
navigate(`/events?search=${encodeURIComponent(trimmed)}`);
}
}}
/>
>
<TextField
fullWidth
placeholder="Search for events..."
variant="outlined"
size="small"
value={headerSearch}
onChange={(e) => setHeaderSearch(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Escape') {
setHeaderSearch('');
}
}}
sx={{
"& .MuiOutlinedInput-root": {
backgroundColor: "white",
borderRadius: "25px",
"& fieldset": {
borderColor: "var(--charcoal-25)",
},
"&:hover fieldset": {
borderColor: "var(--fluorescent-cyan)",
},
"&.Mui-focused fieldset": {
borderColor: "var(--fluorescent-cyan)",
},
},
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<IconButton
type="submit"
size="small"
sx={{ p: 0, mr: 0.5 }}
aria-label="search events"
>
<SearchIcon sx={{ color: "var(--charcoal-50)" }} />
</IconButton>
</InputAdornment>
),
}}
/>
</form>
</Box>

{/* User Menu */}
Expand Down
21 changes: 19 additions & 2 deletions frontend/my-react-app/src/pages/EventsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useMemo, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate, useSearchParams } from 'react-router-dom';
import {
Box,
Container,
Expand Down Expand Up @@ -122,6 +122,13 @@ function EventsPage() {
const [sortBy, setSortBy] = useState<string>('date');
const [filterCategory, setFilterCategory] = useState<string>('all');
const [searchQuery, setSearchQuery] = useState<string>('');
const [searchParams, setSearchParams] = useSearchParams();

// Initialize search query from URL param on mount / param change
useEffect(() => {
const qp = searchParams.get('search') || '';
setSearchQuery(qp);
}, [searchParams]);

// Get min and max prices from events
const { minPrice, maxPrice } = useMemo(() => {
Expand Down Expand Up @@ -262,7 +269,17 @@ function EventsPage() {
label="Search events"
variant="outlined"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onChange={(e) => {
const val = e.target.value;
setSearchQuery(val);
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onChange handler calls setSearchQuery(val) and then updates the URL params with setSearchParams. However, updating searchParams triggers the useEffect on line 128-131, which also calls setSearchQuery. This causes the search query state to be set twice for every keystroke, leading to redundant state updates and re-renders.

Consider removing the setSearchQuery(val) call from the onChange handler and letting the useEffect handle all search query updates from URL params. This maintains a single source of truth (the URL) for the search state.

Suggested change
setSearchQuery(val);

Copilot uses AI. Check for mistakes.
if (val.trim().length > 0) {
setSearchParams({ search: val });
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an inconsistency in how search parameters are handled between Header and EventsPage. The Header trims the search value before encoding it in the URL (trimmed = headerSearch.trim()), but EventsPage sets the URL parameter with the untrimmed value (setSearchParams({ search: val })).

This inconsistency could lead to unexpected behavior. For example, if a user types " test " (with spaces) in EventsPage, the URL will contain the spaces, but if they search from the Header with " test ", the URL will contain "test" without spaces.

Consider trimming the value before setting the search params: setSearchParams({ search: val.trim() })

Suggested change
setSearchParams({ search: val });
setSearchParams({ search: val.trim() });

Copilot uses AI. Check for mistakes.
} else {
// Remove param if empty
searchParams.delete('search');
setSearchParams(searchParams);
Comment on lines +279 to +280
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When clearing the search parameter, the code mutates the existing searchParams object by calling searchParams.delete('search') before passing it to setSearchParams. While this works, it's modifying a URLSearchParams object that React manages.

A cleaner approach would be to create a new URLSearchParams object:

const newParams = new URLSearchParams(searchParams);
newParams.delete('search');
setSearchParams(newParams);

or simply use setSearchParams({}) to clear all params, or avoid the mutation pattern.

Suggested change
searchParams.delete('search');
setSearchParams(searchParams);
const newParams = new URLSearchParams(searchParams);
newParams.delete('search');
setSearchParams(newParams);

Copilot uses AI. Check for mistakes.
}
}}
placeholder="Search by title, description, or location..."
/>
Comment on lines +273 to 284
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The search input updates URL parameters on every keystroke, which creates a new browser history entry for each character typed. This makes it difficult for users to use the browser's back button effectively and can cause performance issues.

Consider updating the URL only when the user finishes typing (using a debounce) or when they submit the search (blur event or Enter key). The Header.tsx implementation handles this better by only updating on form submission.

Suggested change
const val = e.target.value;
setSearchQuery(val);
if (val.trim().length > 0) {
setSearchParams({ search: val });
} else {
// Remove param if empty
searchParams.delete('search');
setSearchParams(searchParams);
}
}}
placeholder="Search by title, description, or location..."
/>
setSearchQuery(e.target.value);
}}
onBlur={() => {
if (searchQuery.trim().length > 0) {
setSearchParams({ search: searchQuery });
} else {
// Remove param if empty
searchParams.delete('search');
setSearchParams(searchParams);
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
if (searchQuery.trim().length > 0) {
setSearchParams({ search: searchQuery });
} else {
searchParams.delete('search');
setSearchParams(searchParams);
}
}
}}
placeholder="Search by title, description, or location..."

Copilot uses AI. Check for mistakes.
</Grid>
Expand Down
Loading