Skip to content

fix: migrate tier list to mui components#890

Open
tomrndom wants to merge 4 commits intomasterfrom
feature/sponsorship-type-list-mui
Open

fix: migrate tier list to mui components#890
tomrndom wants to merge 4 commits intomasterfrom
feature/sponsorship-type-list-mui

Conversation

@tomrndom
Copy link
Copy Markdown

@tomrndom tomrndom commented Apr 23, 2026

ref: https://app.clickup.com/t/86b9j5rez

image image

Signed-off-by: Tomás Castillo tcastilloboireau@gmail.com

Summary by CodeRabbit

  • New Features

    • Search by term for tiers and an in-page dialog for adding/editing tiers.
  • Bug Fixes

    • Consistent loading handling, unified on-screen success/error snackbars, removed unexpected navigation during save flows, and improved query debounce behavior.
  • Refactor

    • Tier list rewritten to hooks-based implementation with updated UI/table components and in-place edit flow.
  • Tests

    • Added unit tests covering the tier list and dialog interactions.
  • Localization

    • UI copy updated to use “Tier” terminology and added a search placeholder.

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@tomrndom tomrndom requested a review from smarcet April 23, 2026 14:07
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

Actions standardized for loading/snackbars; getSponsorships signature extended with search term and defaults; debounce import changed; SponsorshipListPage migrated to hooks with MUI MuiTable and in-page Formik SponsorshipDialog; i18n switched to “Tier”; tests added for list page and dialog.

Changes

Cohort / File(s) Summary
Sponsorship Actions
src/actions/sponsorship-actions.js
Replaced authErrorHandler/showMessage/showSuccessMessage with snackbarErrorHandler/snackbarSuccessHandler; moved stopLoading() to .finally() for requests; saveSponsorship returns request promise for both branches and dispatches translated success snackbars; getSponsorships signature changed to accept term with defaults and adds escaped filter[] when present; switched debounce import to lodash/debounce.
List Page (hooks + UI)
src/pages/sponsorship-types/sponsorship-list-page.js
Converted class to functional component using hooks; replaced react-bootstrap/sweetalert2 pagination/table with MUI layout and MuiTable; add/edit now open in-page SponsorshipDialog; delete uses deleteSponsorship and table delete-dialog; handlers use new getSponsorships(term, page, perPage, order, orderDir) signature.
Dialog Component & Tests
src/pages/sponsorship-types/components/sponsorship-dialog.js, src/pages/sponsorship-types/components/__tests__/sponsorship-dialog.test.js
Added SponsorshipDialog (Formik with Yup validation, reinit on entity change, fields: id,name,label,size,order) and unit tests covering add/edit title, prefilled fields, validation, close and save behavior.
List Page Tests
src/pages/sponsorship-types/__tests__/sponsorship-list-page.test.js
Added tests verifying dispatch of getSponsorships on mount, search Enter triggers getSponsorships with term, add/edit flows open dialog, edit fetches entity, save calls saveSponsorship and refreshes list, and dialog close/reset behavior.
i18n
src/i18n/en.json
Updated strings to use “Tier” terminology (list empty-state, Add CTA, search placeholder, delete confirmation token {name} and edit/save success messages).
Reducer
src/reducers/sponsorships/sponsorship-list-reducer.js
Small refactor: use const destructuring of payload, include perPage in REQUEST_SPONSORSHIPS, simplified RECEIVE_SPONSORSHIPS assignments; import relocation only.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant List as SponsorshipListPage
    participant Redux as Redux Store
    participant API
    participant Dialog as SponsorshipDialog

    User->>List: open page / enter search / click Add/Edit
    List->>Redux: dispatch getSponsorships(term,page,perPage,order,orderDir)
    Redux->>API: GET /sponsorships?filter[]=...
    API-->>Redux: sponsorships list
    Redux-->>List: update sponsorships state
    List->>Dialog: open (Add) / dispatch getSponsorship(id) (Edit)
    Redux->>API: GET /sponsorships/{id}
    API-->>Redux: sponsorship entity
    Redux-->>List: currentSponsorship updated
    List->>Dialog: open with entity
    User->>Dialog: submit form
    Dialog->>List: onSave(values)
    List->>Redux: dispatch saveSponsorship(values)
    Redux->>API: POST/PUT /sponsorships
    API-->>Redux: saved sponsorship
    Redux-->>List: dispatch snackbarSuccess / stopLoading
    List->>Dialog: close
    List->>Redux: dispatch getSponsorships(currentTerm,...)
    Redux->>API: GET /sponsorships?...
    API-->>Redux: refreshed list
    Redux-->>List: updated sponsorships
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • smarcet
  • martinquiroga-exo

Poem

🐇 I hopped through code to change a name,
Tiers now bloom where Sponsorship once came,
Dialogs cradle forms with Formik cheer,
Debounced searches whisper results near,
Redux, snacks, and hooks — a tidy game.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: migrating the sponsorship tier list page from class components with react-bootstrap to hooks-based functional components with Material UI (MUI) components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/sponsorship-type-list-mui

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/i18n/en.json`:
- Line 2895: The i18n key "remove_warning" currently reads "Are you sure you
want to delete sponsorship " and must be updated to use the new terminology;
locate the "remove_warning" entry in src/i18n/en.json and replace the value with
the corrected delete prompt (for example: "Are you sure you want to delete this
Tier?") so the confirmation dialog on the Tier page uses "Tier" instead of
"sponsorship".

In `@src/pages/sponsorship-types/components/sponsorship-dialog.js`:
- Around line 59-72: The Dialog component has had MUI focus-management disabled
and the close IconButton lacks an accessible name; restore accessibility by
removing disableEnforceFocus, disableAutoFocus, and disableRestoreFocus from the
Dialog (unless you have a documented, justified reason) so focus is
trapped/returned properly, and add an aria-label (or localized label) to the
IconButton that renders the CloseIcon (the close control tied to handleClose) so
screen readers can announce it; after changes, verify keyboard-only navigation
enters the Dialog on open and returns focus to the trigger on close.

In `@src/pages/sponsorship-types/sponsorship-list-page.js`:
- Around line 45-47: The current implementation uses searchTerm (const
[searchTerm, setSearchTerm]) as both the live input value and the applied
filter, causing the fetch via getSponsorships to use an unsubmitted draft;
change this by introducing a separate state (e.g., draftSearch and
setDraftSearch) bound to the input onChange, and an applied state (e.g.,
appliedSearch or searchFilter with setSearchFilter) that you update only on
submit/Enter; then pass appliedSearch/searchFilter to getSponsorships and keep
searchTerm (or rename) for the applied value used by paging/sorting to prevent
unexpected dataset changes when typing.
- Around line 196-210: The UI hides the table when sponsorships.length === 0,
making the empty-state copy sponsorship_list.no_sponsorships unreachable; update
the render so MuiTable is always rendered (pass an empty array when there are no
rows) or explicitly render the empty-state message when sponsorships.length ===
0. Locate the block that conditionally renders <MuiTable> and change it to
either always render <MuiTable columns={columns} data={sponsorships} ... /> so
the component can show its built-in empty state, or replace the false branch
with a simple element that displays the sponsorship_list.no_sponsorships message
(using current translation utilities) when sponsorships.length === 0.
- Around line 97-108: The handleSave function currently closes the dialog in the
finally block so setOpen(false) runs even when saveSponsorship rejects; change
the flow so setOpen(false) is called only after a successful save: call
saveSponsorship(entity).then(() => { await/return getSponsorships(...);
setOpen(false); }).catch(...) to leave the dialog open on errors (and optionally
handle/log the rejection) — adjust the promise chain around saveSponsorship,
getSponsorships, and setOpen to move setOpen(false) into the success (.then)
path and remove it from finally.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ec3c6f78-30a0-4f69-958c-ba13cc3c508a

📥 Commits

Reviewing files that changed from the base of the PR and between 70d9882 and bc1beab.

📒 Files selected for processing (7)
  • src/actions/sponsorship-actions.js
  • src/i18n/en.json
  • src/pages/sponsorship-types/__tests__/sponsorship-list-page.test.js
  • src/pages/sponsorship-types/components/__tests__/sponsorship-dialog.test.js
  • src/pages/sponsorship-types/components/sponsorship-dialog.js
  • src/pages/sponsorship-types/sponsorship-list-page.js
  • src/reducers/sponsorships/sponsorship-list-reducer.js

Comment thread src/i18n/en.json Outdated
"size": "Size",
"order": "Order",
"remove_warning": "Are you sure you want to delete sponsorship "
"remove_warning": "Are you sure you want to delete sponsorship ",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Update the delete prompt to use “Tier”.

This string still says “sponsorship”, so the delete confirmation will regress to the old terminology on the new tier page.

✏️ Proposed copy fix
-    "remove_warning": "Are you sure you want to delete sponsorship ",
+    "remove_warning": "Are you sure you want to delete tier ",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"remove_warning": "Are you sure you want to delete sponsorship ",
"remove_warning": "Are you sure you want to delete tier ",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/i18n/en.json` at line 2895, The i18n key "remove_warning" currently reads
"Are you sure you want to delete sponsorship " and must be updated to use the
new terminology; locate the "remove_warning" entry in src/i18n/en.json and
replace the value with the corrected delete prompt (for example: "Are you sure
you want to delete this Tier?") so the confirmation dialog on the Tier page uses
"Tier" instead of "sponsorship".

Comment thread src/pages/sponsorship-types/components/sponsorship-dialog.js Outdated
Comment on lines +45 to +47
const [open, setOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState("");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Separate the typed value from the applied search filter.

searchTerm is mutated on every keystroke, but the fetch only runs on Enter. That means paging/sorting later will reuse an unsubmitted draft term and unexpectedly change the dataset.

Use a draft input state plus an applied filter state, and only pass the applied value to getSponsorships.

🛠️ Suggested direction
-  const [open, setOpen] = useState(false);
-  const [searchTerm, setSearchTerm] = useState("");
+  const [open, setOpen] = useState(false);
+  const [draftSearchTerm, setDraftSearchTerm] = useState("");
+  const [appliedSearchTerm, setAppliedSearchTerm] = useState("");

   const handlePageChange = (page) => {
-    getSponsorships(searchTerm, page, perPage, order, orderDir);
+    getSponsorships(appliedSearchTerm, page, perPage, order, orderDir);
   };

   const handlePerPageChange = (newPerPage) => {
     getSponsorships(
-      searchTerm,
+      appliedSearchTerm,
       DEFAULT_CURRENT_PAGE,
       newPerPage,
       order,
       orderDir
     );
   };

   const handleSort = (key, dir) => {
-    getSponsorships(searchTerm, currentPage, perPage, key, dir);
+    getSponsorships(appliedSearchTerm, currentPage, perPage, key, dir);
   };

-  const handleSearch = (ev) => {
-    setSearchTerm(ev.target.value);
+  const handleSearch = (ev) => {
     if (ev.key === "Enter") {
+      const term = ev.target.value;
+      setAppliedSearchTerm(term);
       getSponsorships(
-        ev.target.value,
+        term,
         DEFAULT_CURRENT_PAGE,
         perPage,
         order,
         orderDir
       );
     }
   };
...
-            value={searchTerm}
+            value={draftSearchTerm}
...
-            onChange={(e) => setSearchTerm(e.target.value)}
+            onChange={(e) => setDraftSearchTerm(e.target.value)}

Also applies to: 52-80

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsorship-types/sponsorship-list-page.js` around lines 45 - 47,
The current implementation uses searchTerm (const [searchTerm, setSearchTerm])
as both the live input value and the applied filter, causing the fetch via
getSponsorships to use an unsubmitted draft; change this by introducing a
separate state (e.g., draftSearch and setDraftSearch) bound to the input
onChange, and an applied state (e.g., appliedSearch or searchFilter with
setSearchFilter) that you update only on submit/Enter; then pass
appliedSearch/searchFilter to getSponsorships and keep searchTerm (or rename)
for the applied value used by paging/sorting to prevent unexpected dataset
changes when typing.

Comment thread src/pages/sponsorship-types/sponsorship-list-page.js Outdated
Comment thread src/pages/sponsorship-types/sponsorship-list-page.js
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/pages/sponsorship-types/sponsorship-list-page.js (1)

45-67: ⚠️ Potential issue | 🟠 Major

Keep a separate applied search filter.

searchTerm is still acting as both the live input state and the applied backend filter. Typing a draft term and then paging, sorting, saving, or deleting will refresh the table with an unsubmitted filter and unexpectedly change the dataset.

Also applies to: 70-79, 97-120

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsorship-types/sponsorship-list-page.js` around lines 45 - 67,
searchTerm is being used as both the live input value and the applied backend
filter, causing unsubmitted draft input to affect calls to getSponsorships;
introduce a separate state (e.g., appliedSearch or appliedFilter) and keep
searchTerm as the controlled input, update appliedSearch only when the user
submits/commits the search, then change all calls to getSponsorships (including
in useEffect, handlePageChange, handlePerPageChange, handleSort, and any
save/delete flows referenced) to pass appliedSearch instead of searchTerm so
paging/sorting/deletes use the last applied filter rather than the live input.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pages/sponsorship-types/sponsorship-list-page.js`:
- Around line 168-176: The TextField for search (component TextField using
value={searchTerm}, onChange={setSearchTerm}, onKeyDown={handleSearch}) lacks an
accessible name; add a visible label prop or an aria-label/aria-labelledby to
the TextField so assistive tech can identify it (e.g., provide label="Search
sponsorships" or aria-label="Search sponsorships"), ensuring the label text is
localized via T.translate if needed and matches the placeholder intent.
- Around line 155-156: Replace the hardcoded "tiers" suffix with a translation
call so the count label uses the app's i18n and pluralization; locate the JSX
near Grid2 / Box where totalSponsorships is rendered and change the content to
use T.translate (or the project's pluralization helper) instead of the fixed
string, e.g. call T.translate with a key like "sponsorships.tiers" and pass the
count (totalSponsorships) so the label localizes and pluralizes correctly.
- Around line 17-20: The file imports MUI v6 packages but the project uses React
16.13.1; either upgrade React to ^17+ in package.json and reinstall, or revert
these components to Material-UI v4 equivalents: replace imports of Box, Button,
Grid2, TextField from "@mui/material" with the v4 packages (e.g., import { Box,
Button, Grid, TextField } from "@material-ui/core" and replace Grid2 usage with
Grid) and replace AddIcon/SearchIcon imports from "@mui/icons-material" with
"@material-ui/icons"; update any prop/API differences (e.g., Grid2 -> Grid) and
keep the existing MuiTable import (MuiTable) intact so runtime compatibility is
restored.

---

Duplicate comments:
In `@src/pages/sponsorship-types/sponsorship-list-page.js`:
- Around line 45-67: searchTerm is being used as both the live input value and
the applied backend filter, causing unsubmitted draft input to affect calls to
getSponsorships; introduce a separate state (e.g., appliedSearch or
appliedFilter) and keep searchTerm as the controlled input, update appliedSearch
only when the user submits/commits the search, then change all calls to
getSponsorships (including in useEffect, handlePageChange, handlePerPageChange,
handleSort, and any save/delete flows referenced) to pass appliedSearch instead
of searchTerm so paging/sorting/deletes use the last applied filter rather than
the live input.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 37ba9be6-1947-43d8-adf3-7f07f36d3629

📥 Commits

Reviewing files that changed from the base of the PR and between bc1beab and 3640657.

📒 Files selected for processing (2)
  • src/pages/sponsorship-types/components/sponsorship-dialog.js
  • src/pages/sponsorship-types/sponsorship-list-page.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/sponsorship-types/components/sponsorship-dialog.js

Comment on lines +17 to +20
import { Box, Button, Grid2, TextField } from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import SearchIcon from "@mui/icons-material/Search";
import MuiTable from "openstack-uicore-foundation/lib/components/mui/table";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check for package.json in root
cd /
find . -maxdepth 2 -name "package.json" -type f 2>/dev/null | head -5

Repository: fntechgit/summit-admin

Length of output: 1891


🏁 Script executed:

# Look for lock files to determine installed versions
git ls-files | grep -E "(package\.json|yarn\.lock|package-lock\.json)" | head -10

Repository: fntechgit/summit-admin

Length of output: 88


🏁 Script executed:

# Read the package.json to check React and MUI versions
cat package.json | head -100

Repository: fntechgit/summit-admin

Length of output: 3536


🏁 Script executed:

# Search specifically for React and MUI versions in package.json
rg '"react":|"@mui/material":|"@mui/icons-material":' package.json -A 1

Repository: fntechgit/summit-admin

Length of output: 242


MUI 6 requires React 17+ but the project uses React 16.13.1.

This page imports from @mui/material and @mui/icons-material (currently ^6.4.3 in package.json), which officially support only React ^17 || ^18 || ^19. The repo remains on React ^16.13.1, creating an unsupported dependency combination that will break at runtime.

Either upgrade React to 17+, or revert to a MUI version compatible with React 16.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsorship-types/sponsorship-list-page.js` around lines 17 - 20,
The file imports MUI v6 packages but the project uses React 16.13.1; either
upgrade React to ^17+ in package.json and reinstall, or revert these components
to Material-UI v4 equivalents: replace imports of Box, Button, Grid2, TextField
from "@mui/material" with the v4 packages (e.g., import { Box, Button, Grid,
TextField } from "@material-ui/core" and replace Grid2 usage with Grid) and
replace AddIcon/SearchIcon imports from "@mui/icons-material" with
"@material-ui/icons"; update any prop/API differences (e.g., Grid2 -> Grid) and
keep the existing MuiTable import (MuiTable) intact so runtime compatibility is
restored.

Comment on lines +155 to +156
<Grid2 size={2}>
<Box component="span">{totalSponsorships} tiers</Box>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid hardcoded tiers in the count badge.

Everything else on this screen goes through T.translate, but this suffix is fixed English, so it will not localize or pluralize correctly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsorship-types/sponsorship-list-page.js` around lines 155 - 156,
Replace the hardcoded "tiers" suffix with a translation call so the count label
uses the app's i18n and pluralization; locate the JSX near Grid2 / Box where
totalSponsorships is rendered and change the content to use T.translate (or the
project's pluralization helper) instead of the fixed string, e.g. call
T.translate with a key like "sponsorships.tiers" and pass the count
(totalSponsorships) so the label localizes and pluralizes correctly.

Comment on lines +168 to +176
<TextField
variant="outlined"
value={searchTerm}
placeholder={T.translate("sponsorship_list.placeholders.search")}
slotProps={{
input: { startAdornment: <SearchIcon sx={{ mr: 1 }} /> }
}}
onChange={(e) => setSearchTerm(e.target.value)}
onKeyDown={handleSearch}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Give the search field an accessible name.

This input only has a placeholder, so it does not expose a stable programmatic label for assistive tech. Add a visible label or at least an aria-label.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsorship-types/sponsorship-list-page.js` around lines 168 - 176,
The TextField for search (component TextField using value={searchTerm},
onChange={setSearchTerm}, onKeyDown={handleSearch}) lacks an accessible name;
add a visible label prop or an aria-label/aria-labelledby to the TextField so
assistive tech can identify it (e.g., provide label="Search sponsorships" or
aria-label="Search sponsorships"), ensuring the label text is localized via
T.translate if needed and matches the placeholder intent.

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (3)
src/pages/sponsorship-types/sponsorship-list-page.js (3)

171-179: ⚠️ Potential issue | 🟠 Major

Add an accessible name to the search input.

Line 171 defines a TextField with only a placeholder; please add a localized label or aria-label so assistive tech can identify the field reliably.

♿ Minimal patch
           <TextField
             variant="outlined"
+            aria-label={T.translate("general.search")}
             value={searchTerm}
             placeholder={T.translate("sponsorship_list.placeholders.search")}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsorship-types/sponsorship-list-page.js` around lines 171 - 179,
The search TextField currently only has a placeholder which is not sufficient
for assistive tech; add an accessible name by supplying either a localized label
prop or an aria-label prop (e.g., use
T.translate("sponsorship_list.labels.search") or similar) on the TextField
component that uses the same localization namespace as the placeholder so screen
readers can identify the input (target the TextField where searchTerm,
setSearchTerm, and handleSearch are used).

17-20: ⚠️ Potential issue | 🔴 Critical

MUI v6 + React 16 is an unsupported dependency combination.

Line 17 imports MUI v6 components while the repo is on React 16.13.1. This can break rendering/runtime behavior. Please either upgrade React to a supported version or downgrade MUI packages to a React-16-compatible line.

#!/bin/bash
set -euo pipefail

echo "React version:"
rg -n '"react"\s*:' package.json

echo "MUI versions:"
rg -n '"@mui/material"\s*:|"@mui/icons-material"\s*:' package.json
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsorship-types/sponsorship-list-page.js` around lines 17 - 20,
The file sponsorship-list-page.js currently imports MUI v6 symbols (Box, Button,
Grid2, TextField, AddIcon, SearchIcon) which is incompatible with the repo's
React 16.13.1; fix by either upgrading React in package.json to a version
supported by MUI v6 or replacing/downgrading the MUI packages to versions
compatible with React 16 (and update the imports accordingly), then run the
supplied verification script to confirm "react" and
"@mui/material"/"@mui/icons-material" versions match supported compatibility;
ensure the import lines in sponsorship-list-page.js reflect the chosen package
versions.

45-80: ⚠️ Potential issue | 🟠 Major

Decouple draft input from the applied search filter.

Line 53 and Line 67 use searchTerm for paging/sorting even though the fetch is only intended on Enter (Line 72). This means unsubmitted typing can still alter later requests. Also, Line 74 should not rely on state during the Enter event path.

💡 Suggested fix
-  const [searchTerm, setSearchTerm] = useState("");
+  const [draftSearchTerm, setDraftSearchTerm] = useState("");
+  const [appliedSearchTerm, setAppliedSearchTerm] = useState("");

   const handlePageChange = (page) => {
-    getSponsorships(searchTerm, page, perPage, order, orderDir);
+    getSponsorships(appliedSearchTerm, page, perPage, order, orderDir);
   };

   const handlePerPageChange = (newPerPage) => {
     getSponsorships(
-      searchTerm,
+      appliedSearchTerm,
       DEFAULT_CURRENT_PAGE,
       newPerPage,
       order,
       orderDir
     );
   };

   const handleSort = (key, dir) => {
-    getSponsorships(searchTerm, currentPage, perPage, key, dir);
+    getSponsorships(appliedSearchTerm, currentPage, perPage, key, dir);
   };

   const handleSearch = (ev) => {
-    setSearchTerm(ev.target.value);
     if (ev.key === "Enter") {
+      const term = ev.target.value;
+      setAppliedSearchTerm(term);
       getSponsorships(
-        searchTerm,
+        term,
         DEFAULT_CURRENT_PAGE,
         perPage,
         order,
         orderDir
       );
     }
   };
...
-            value={searchTerm}
+            value={draftSearchTerm}
...
-            onChange={(e) => setSearchTerm(e.target.value)}
+            onChange={(e) => setDraftSearchTerm(e.target.value)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsorship-types/sponsorship-list-page.js` around lines 45 - 80,
The current handlers (handlePageChange, handlePerPageChange, handleSort, and the
Enter path in handleSearch) wrongly use the live draft state searchTerm, causing
unsubmitted typing to affect requests; introduce a separate draft state (e.g.,
searchInput) and an applied state (e.g., appliedSearch) or at minimum use the
event value when Enter is pressed: update handleSearch to
setSearchInput(ev.target.value) for normal typing and on Enter either
setAppliedSearch(ev.target.value) then call getSponsorships(ev.target.value,
DEFAULT_CURRENT_PAGE, perPage, order, orderDir) (or call getSponsorships using
appliedSearch after setting it), and change
handlePageChange/handlePerPageChange/handleSort to call getSponsorships using
appliedSearch (not searchTerm) so paging/sorting use the last applied search
filter.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/pages/sponsorship-types/sponsorship-list-page.js`:
- Around line 171-179: The search TextField currently only has a placeholder
which is not sufficient for assistive tech; add an accessible name by supplying
either a localized label prop or an aria-label prop (e.g., use
T.translate("sponsorship_list.labels.search") or similar) on the TextField
component that uses the same localization namespace as the placeholder so screen
readers can identify the input (target the TextField where searchTerm,
setSearchTerm, and handleSearch are used).
- Around line 17-20: The file sponsorship-list-page.js currently imports MUI v6
symbols (Box, Button, Grid2, TextField, AddIcon, SearchIcon) which is
incompatible with the repo's React 16.13.1; fix by either upgrading React in
package.json to a version supported by MUI v6 or replacing/downgrading the MUI
packages to versions compatible with React 16 (and update the imports
accordingly), then run the supplied verification script to confirm "react" and
"@mui/material"/"@mui/icons-material" versions match supported compatibility;
ensure the import lines in sponsorship-list-page.js reflect the chosen package
versions.
- Around line 45-80: The current handlers (handlePageChange,
handlePerPageChange, handleSort, and the Enter path in handleSearch) wrongly use
the live draft state searchTerm, causing unsubmitted typing to affect requests;
introduce a separate draft state (e.g., searchInput) and an applied state (e.g.,
appliedSearch) or at minimum use the event value when Enter is pressed: update
handleSearch to setSearchInput(ev.target.value) for normal typing and on Enter
either setAppliedSearch(ev.target.value) then call
getSponsorships(ev.target.value, DEFAULT_CURRENT_PAGE, perPage, order, orderDir)
(or call getSponsorships using appliedSearch after setting it), and change
handlePageChange/handlePerPageChange/handleSort to call getSponsorships using
appliedSearch (not searchTerm) so paging/sorting use the last applied search
filter.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4b0042a0-1666-4859-a14e-96da5e9ff17b

📥 Commits

Reviewing files that changed from the base of the PR and between a3aeb53 and 29c5423.

📒 Files selected for processing (2)
  • src/i18n/en.json
  • src/pages/sponsorship-types/sponsorship-list-page.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant