Skip to content

feat: move selection plans grid to mui#917

Open
priscila-moneo wants to merge 5 commits into
masterfrom
feature/move-selection-plans-grid-mui
Open

feat: move selection plans grid to mui#917
priscila-moneo wants to merge 5 commits into
masterfrom
feature/move-selection-plans-grid-mui

Conversation

@priscila-moneo

@priscila-moneo priscila-moneo commented May 5, 2026

Copy link
Copy Markdown

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

image

Summary by CodeRabbit

  • New Features
    • Modal-based selection plan editor now supports a post-save callback (onSaved).
    • Selection plans list now fully supports configurable per-page, sorting, and search.
  • Improvements
    • Reworked selection-plan list into a modern hook-based view with popup editing and consistent refresh after mutations.
    • Enhanced form UX: in-flight save guarding, improved error scrolling, and expanded marketing/CFP settings editing with sortable sections.
  • Bug Fixes
    • More reliable loading/notification behavior for create/update and guaranteed list refresh after delete, including on failures.
  • Tests
    • Added/expanded Jest + React Testing Library coverage for save and refresh behaviors.

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Modal-driven selection-plans list/editor using hooks; add per-page pagination through actions/reducer/components, update routing to redirect to the list, adjust imports, and add tests for save/delete flows.

Changes

Selection Plans Modal-Driven UI Refactor

Layer / File(s) Summary
Action signature & request wiring
src/actions/selection-plan-actions.js
getSelectionPlans now accepts perPage and uses DEFAULT_CURRENT_PAGE, DEFAULT_PER_PAGE, DEFAULT_ORDER_DIR; sends per_page: perPage and dispatches REQUEST_SELECTION_PLANS with { order, orderDir, page, perPage, term }.
Save action flows with error handling
src/actions/selection-plan-actions.js
saveSelectionPlan create/update paths dispatch translated success snackbar, return payload.response from .then(), and move stopLoading() to .finally() block.
Notification handler updates
src/actions/selection-plan-actions.js
Replaced showSuccessMessage with snackbarSuccessHandler in assignExtraQuestion2SelectionPlan, importAllowedMembersCSV, and assignProgressFlag2SelectionPlan.
Reducer pagination & mapping
src/reducers/selection_plans/selection-plan-list-reducer.js
REQUEST_SELECTION_PLANS writes order, orderDir, currentPage, perPage, term; RECEIVE_SELECTION_PLANS reads total, last_page, data, maps items adding is_enabled/is_hidden as "yes"/"no", and updates selectionPlans, totalSelectionPlans, lastPage.
SelectionPlanForm → hooks & save flow
src/components/forms/selection-plan-form.js
Rewritten as functional component with hooks; adds onSaved prop; submit flow calls onSubmit(entity) then saveSelectionPlanSettings(...) and invokes onSaved(savedEntity); marketing_settings guarded initialization; updates SortableTable reorder API and CFP settings UI via Formik field bindings.
EditSelectionPlanPage wrapper
src/pages/selection-plans/edit-selection-plan-page.js
Forwards new onSaved prop to SelectionPlanForm and removes previous header/title wrapper around the form.
SelectionPlanPopup modal
src/pages/selection-plans/selection-plan-popup.js
New component rendering MUI Dialog containing EditSelectionPlanPage; tracks save progress via state + ref to guard close during saving; wires onSavingChange/onSaved callbacks.
SelectionPlanListPage → modal list & table
src/pages/selection-plans/selection-plan-list-page.js
Converted to hooks and MUI table/search flow; loads data via getSelectionPlans(term,page,perPage,order,orderDir) on mount; opens SelectionPlanPopup modal after fetching entity + marketing settings; delete directly calls deleteSelectionPlan then refreshes; updated connect/mapStateToProps.
Routing & layout changes
src/layouts/selection-plan-layout.js, src/layouts/selection-plan-id-layout.js
Removed lazy edit base route and import; /new redirects to list; base redirect points to selection-plans list.
Import path & config updates
src/components/inputs/import-modal/index.jsx, src/pages/sponsors/__tests__/edit-sponsor-page.test.js
UploadInput import changed to default from narrowed path; Jest mock added to stub sponsor-cart-tab in sponsor tests.
Internationalization strings
src/i18n/en.json
Added delete confirmation messages for category groups, activity types, extra questions, rating types, progress flags, and allowed members; added search placeholders for category groups and activity types.
Test coverage: saveSelectionPlan action
src/actions/__tests__/selection-plan-actions.test.js
New Jest + redux-thunk test suite covering create (no id) and update (with id) flows, asserting Promise resolution and dispatched action ordering (SELECTION_PLAN_ADDED/UPDATED before STOP_LOADING).
Test coverage: SelectionPlanForm save flow
src/components/forms/__tests__/selection-plan-form.test.js
New React Testing Library test suite validating save-button disabling during pending onSubmit, prevention of duplicate submit clicks while disabled, re-enable on rejection, and successful save path calling saveSelectionPlanSettings and onSaved.
Test coverage: SelectionPlanListPage list flow
src/pages/selection-plans/__tests__/selection-plan-list-page.test.js
New test file verifying list reloads after save (mount + post-save refresh), after successful delete (mount + post-delete refresh), and re-syncs on failed delete via .finally() behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

  • fntechgit/summit-admin#760: Overlaps with selection-plan routing and layout refactors affecting SelectionPlanLayout/SelectionPlanIdLayout, form component conversion patterns.

Suggested reviewers

  • smarcet
  • martinquiroga-exo

Poem

🐰
I hopped through lists and modal light,
With hooks I stitched each field just right.
Pages count and orders cascade fine,
A nimble form saved settings in time.
Now off I bound — refactor done! ✨

🚥 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 'feat: move selection plans grid to mui' accurately describes the primary objective of the PR—migrating the selection plans grid component to Material-UI. It is concise, clear, and directly related to the main changeset.
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/move-selection-plans-grid-mui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
src/pages/selection-plans/selection-plan-list-page.js (2)

88-95: 💤 Low value

Edit flow goes through URL push — works but worth a brief inline comment.

Clicking edit pushes /selection-plans/:id and relies on the routeSelectionPlanId effect to open the dialog. This is a nice property for deep-linking, but it's non-obvious and easy to break in future refactors. A one-line comment explaining the URL ↔ modal relationship would help.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/selection-plans/selection-plan-list-page.js` around lines 88 - 95,
Add a one-line inline comment in the handleEdit function explaining that calling
history.push(`/app/summits/${currentSummit.id}/selection-plans/${selectionPlanId}`)
intentionally updates the URL to trigger the routeSelectionPlanId effect which
opens the edit modal/dialog (enables deep-linking), so future refactors know the
URL change is relied upon to show the modal rather than directly invoking a
modal open method.

60-73: ⚡ Quick win

Add a rejection handler to openEditModal.

If getSelectionPlan or getMarketingSettingsBySelectionPlan rejects (e.g., 404 on a deep-linked stale ID, network failure), the popup never opens and the user is left on the list with no signal. A .catch ensures the route-driven open flow degrades gracefully.

🛡️ Proposed fix
   const openEditModal = (selectionPlanId) => {
     if (!selectionPlanId) return;

     getSelectionPlan(selectionPlanId)
       .then(() =>
         getMarketingSettingsBySelectionPlan(
           selectionPlanId,
           null,
           DEFAULT_CURRENT_PAGE,
           MAX_PER_PAGE
         )
       )
-      .then(() => setOpenSelectionPlanPopup(true));
+      .then(() => setOpenSelectionPlanPopup(true))
+      .catch(() => {
+        if (routeSelectionPlanId) {
+          history.replace(`/app/summits/${currentSummit.id}/selection-plans`);
+        }
+      });
   };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/selection-plans/selection-plan-list-page.js` around lines 60 - 73,
openEditModal's promise chain currently has no rejection handler so failures
from getSelectionPlan or getMarketingSettingsBySelectionPlan leave the UI
unchanged; add a .catch to the chain that handles errors (log/show a
notification and/or update error state) and still calls
setOpenSelectionPlanPopup(true) so the modal opens in a degraded state; keep
references to openEditModal, getSelectionPlan,
getMarketingSettingsBySelectionPlan and setOpenSelectionPlanPopup when
implementing the catch behavior.
src/layouts/selection-plan-layout.js (1)

41-52: 💤 Low value

LGTM, with a small readability note.

Routing is correct: the exact strict route on :selection_plan_id(\\d+) renders the list page (which auto-opens the edit dialog from the URL), while the non-exact one continues to delegate sub-paths (/extra-questions, /rating-types) to SelectionPlanIdLayout. Two <Route> entries with the same path is unusual at first glance — a brief inline comment ("exact → list with auto-open dialog; non-exact → nested sub-pages") would save future readers a double-take.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/layouts/selection-plan-layout.js` around lines 41 - 52, Add a brief
inline comment above the two Route entries explaining their intent: the
exact/strict Route with path `${match.url}/:selection_plan_id(\\d+)` renders
SelectionPlanListPage (which auto-opens the edit dialog from the URL), while the
non-exact Route with the same path delegates nested sub-pages to
SelectionPlanIdLayout (e.g., `/extra-questions`, `/rating-types`); place the
comment near the two Route elements so future readers immediately understand why
both routes share the same path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/forms/selection-plan-form.js`:
- Around line 191-205: handleSubmit currently chains onSubmit →
saveSelectionPlanSettings → onSaved with no error handling; update handleSubmit
to return the promise chain, add a .catch at the end, and guard the intermediate
result before using e.id. Concretely: in handleSubmit (method name) make the
call return this.props.onSubmit(this.state.entity).then((e) => { if (!e ||
!e.id) throw new Error("Missing id from onSubmit result"); return
this.props.saveSelectionPlanSettings(this.state.entity.marketing_settings,
e.id).then(() => { if (onSaved) onSaved(e); }); }).catch(err => { /* handle
error: set local state (e.g. this.setState({saveError: err, saving:false}))
and/or call this.props.onError(err) if provided, and rethrow or swallow as
appropriate */ }); ensure you reference onSubmit, saveSelectionPlanSettings,
onSaved and this.state.entity.marketing_settings in your changes.

In `@src/pages/selection-plans/selection-plan-list-page.js`:
- Around line 174-242: Remove the redundant actions.edit entry from
tableOptions: locate the tableOptions constant and delete the actions: { edit: {
onClick: handleEdit } } block so that edit/delete behavior is provided only via
the top-level onEdit={handleEdit} and onDelete={handleDelete} props passed to
MuiTable; keep tableOptions containing sortCol and sortDir unchanged and ensure
no other references to actions.edit remain.
- Around line 246-254: Add an inline comment above the Dialog usage explaining
that focus management props (disableEnforceFocus, disableAutoFocus,
disableRestoreFocus) are intentionally disabled because EditSelectionPlanPage
triggers nested SweetAlert2 modals via Swal.fire() which conflict with MUI
Dialog's focus trap; mention that this is a deliberate tradeoff to allow those
modals to function and warn future maintainers not to re-enable those props
without addressing the SweetAlert2/MUI focus conflict.
- Around line 97-102: handleDelete currently performs a silent delete via
deleteSelectionPlan(selectionPlanId).then(() => refreshSelectionPlans()) without
user confirmation or error handling; update this flow so the table shows a
confirmation dialog (add the deleteDialogBody prop to the MuiTable instance for
this page, matching the pattern used in sponsor users table and keep
confirmButtonColor="error"), and modify handleDelete to attach a .catch(...) to
deleteSelectionPlan to surface server errors to the user (e.g., via the existing
notification/snackbar mechanism) and only call refreshSelectionPlans() on
success; ensure you reference the handleDelete function and
deleteSelectionPlan/refreshSelectionPlans identifiers when making these changes.
- Around line 75-83: The first useEffect calling getSelectionPlans should
include all referenced variables (term, perPage, order, orderDir, and
getSelectionPlans) in its dependency array (or, if it truly must only run on
mount, replace the array with [] and add a comment explaining why it must not
re-run); the second effect that checks routeSelectionPlanId must include a
stable openEditModal in its deps—wrap openEditModal in useCallback to stabilize
its identity and then add openEditModal (and routeSelectionPlanId) to that
effect's dependency array so react-hooks/exhaustive-deps is satisfied.

---

Nitpick comments:
In `@src/layouts/selection-plan-layout.js`:
- Around line 41-52: Add a brief inline comment above the two Route entries
explaining their intent: the exact/strict Route with path
`${match.url}/:selection_plan_id(\\d+)` renders SelectionPlanListPage (which
auto-opens the edit dialog from the URL), while the non-exact Route with the
same path delegates nested sub-pages to SelectionPlanIdLayout (e.g.,
`/extra-questions`, `/rating-types`); place the comment near the two Route
elements so future readers immediately understand why both routes share the same
path.

In `@src/pages/selection-plans/selection-plan-list-page.js`:
- Around line 88-95: Add a one-line inline comment in the handleEdit function
explaining that calling
history.push(`/app/summits/${currentSummit.id}/selection-plans/${selectionPlanId}`)
intentionally updates the URL to trigger the routeSelectionPlanId effect which
opens the edit modal/dialog (enables deep-linking), so future refactors know the
URL change is relied upon to show the modal rather than directly invoking a
modal open method.
- Around line 60-73: openEditModal's promise chain currently has no rejection
handler so failures from getSelectionPlan or getMarketingSettingsBySelectionPlan
leave the UI unchanged; add a .catch to the chain that handles errors (log/show
a notification and/or update error state) and still calls
setOpenSelectionPlanPopup(true) so the modal opens in a degraded state; keep
references to openEditModal, getSelectionPlan,
getMarketingSettingsBySelectionPlan and setOpenSelectionPlanPopup when
implementing the catch behavior.
🪄 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: e66a2e0b-b7c8-462d-8a25-e51770a30ccf

📥 Commits

Reviewing files that changed from the base of the PR and between 8607ac5 and 5aa12fb.

📒 Files selected for processing (8)
  • src/actions/selection-plan-actions.js
  • src/components/forms/selection-plan-form.js
  • src/components/inputs/import-modal/index.jsx
  • src/layouts/selection-plan-id-layout.js
  • src/layouts/selection-plan-layout.js
  • src/pages/selection-plans/edit-selection-plan-page.js
  • src/pages/selection-plans/selection-plan-list-page.js
  • src/reducers/selection_plans/selection-plan-list-reducer.js

Comment thread src/components/forms/selection-plan-form.js Outdated
Comment thread src/pages/selection-plans/selection-plan-list-page.js Outdated
Comment on lines 97 to 102
const handleDelete = (row) => {
const selectionPlanId = row?.id || row;
if (!selectionPlanId) return;

deleteSelectionPlan(selectionPlanId).then(() => refreshSelectionPlans());
};

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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether MuiTable wraps onDelete in a confirmation dialog and
# what `confirmButtonColor` actually controls.
fd -i 'mui' -t d
fd -i 'table' -t f --extension js --extension jsx --extension ts --extension tsx | xargs rg -nP -C3 'onDelete|confirmButtonColor' 2>/dev/null | head -200
rg -nP -C3 '"openstack-uicore-foundation/lib/components/mui/table"' --type=js --type=jsx

Repository: fntechgit/summit-admin

Length of output: 10887


🏁 Script executed:

cat -n src/pages/selection-plans/selection-plan-list-page.js | sed -n '170,250p'

Repository: fntechgit/summit-admin

Length of output: 2799


🏁 Script executed:

# Look for MuiTable component definition locally
find src/components -name "*table*" -o -name "*Table*" | head -20

Repository: fntechgit/summit-admin

Length of output: 1142


🏁 Script executed:

# Search for MuiTable imports and their usage pattern
rg "from.*openstack-uicore-foundation.*table" -A 5 -B 2

Repository: fntechgit/summit-admin

Length of output: 50378


🏁 Script executed:

sed -n '100,200p' src/pages/sponsors/sponsor-users-list-page/components/users-table.js

Repository: fntechgit/summit-admin

Length of output: 1332


🏁 Script executed:

sed -n '97,102p' src/pages/selection-plans/selection-plan-list-page.js

Repository: fntechgit/summit-admin

Length of output: 262


Add delete confirmation dialog and error handling to handleDelete.

handleDelete silently deletes selection plans without user confirmation. Unlike other MuiTable instances in the codebase (e.g., sponsor users table), this one lacks the deleteDialogBody prop required to display a confirmation dialog. confirmButtonColor="error" alone does not provide confirmation UX. Additionally, no .catch() is attached, so server-side failures leave the list unrefreshed and unsignaled to the user.

Suggested fix
  const handleDelete = (row) => {
    const selectionPlanId = row?.id || row;
    if (!selectionPlanId) return;

-    deleteSelectionPlan(selectionPlanId).then(() => refreshSelectionPlans());
+    deleteSelectionPlan(selectionPlanId)
+      .then(() => refreshSelectionPlans())
+      .catch(() => {
+        // surfaced via authErrorHandler; no-op
+      });
  };

Also add deleteDialogBody prop to MuiTable to match the pattern used elsewhere:

  <MuiTable
    // ...
    onDelete={handleDelete}
+   deleteDialogBody={(plan) =>
+     T.translate("selection_plan_list.delete_confirmation_body", { plan })
+   }
    confirmButtonColor="error"
  />
📝 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
const handleDelete = (row) => {
const selectionPlanId = row?.id || row;
if (!selectionPlanId) return;
deleteSelectionPlan(selectionPlanId).then(() => refreshSelectionPlans());
};
const handleDelete = (row) => {
const selectionPlanId = row?.id || row;
if (!selectionPlanId) return;
deleteSelectionPlan(selectionPlanId)
.then(() => refreshSelectionPlans())
.catch(() => {
// surfaced via authErrorHandler; no-op
});
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/selection-plans/selection-plan-list-page.js` around lines 97 - 102,
handleDelete currently performs a silent delete via
deleteSelectionPlan(selectionPlanId).then(() => refreshSelectionPlans()) without
user confirmation or error handling; update this flow so the table shows a
confirmation dialog (add the deleteDialogBody prop to the MuiTable instance for
this page, matching the pattern used in sponsor users table and keep
confirmButtonColor="error"), and modify handleDelete to attach a .catch(...) to
deleteSelectionPlan to surface server errors to the user (e.g., via the existing
notification/snackbar mechanism) and only call refreshSelectionPlans() on
success; ensure you reference the handleDelete function and
deleteSelectionPlan/refreshSelectionPlans identifiers when making these changes.

Comment thread src/pages/selection-plans/selection-plan-list-page.js
Comment thread src/pages/selection-plans/selection-plan-list-page.js Outdated
} from "../../reducers/selection_plans/selection-plan-reducer";
import history from "../../history";

class SelectionPlanForm extends React.Component {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

please refactor to functional component

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I thought this would be out of scope so I didn't do It initially but I'm adding this now.

entity,
allowedMembers,
errors,
hideHeader = false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

do we really need a prop ? I don't see where this is used WITH the header

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Just did a refactor so this is not needed anymore! Thanks

totalSelectionPlans: total,
currentPage: current_page,
lastPage: last_page
lastPage: last_page,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

no need to do this if you are already doing it in the request action

getSelectionPlans(term, currentPage, perPage, order, orderDir);

const handleEdit = (selectionPlan) => {
const selectionPlanId = selectionPlan?.id || selectionPlan;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

why selectionPlan can be both an object or an id ? we should consolidate this on either one or the other

if (!selectionPlanId) return;

const handleEdit = (selectionPlanId) => {
history.push(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@smarcet linking the user to /id kind of defeats the purpose of having the edit screen in a modal, shouldn't we just open the modal in this case ? or do we want the url to reflect the edit action ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The edit page for a selection plan uses sub-routes (e.g. /extra-questions, /rating-types) that depend on the selection_plan_id URL parameter being present. Removing the ID from the URL on edit would break navigation to those nested routes inside SelectionPlanIdLayout. Keeping the ID ensures the layout can resolve the correct selection plan context for all child routes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not really, you can just open the popup on edit but keep the /id a valid route for the rest of the routes

const handleNew = () => {
history.push(`/app/summits/${currentSummit.id}/selection-plans/new`);
resetSelectionPlanForm();
setOpenSelectionPlanPopup(true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

on edit you do change the url, but on new not .. this is not consistent

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The /new path existed in the layout but was never functional — it just redirected back to the list. Creation is handled directly from the list page by opening the form with id=0. Once the plan is saved, the API returns the new ID and the user is redirected to the proper edit URL. There was no reason to keep a dead redirect route.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Also, in task description it was requested to delete this route legacy route https://showadmin.dev.fnopen.com/app/summits/73/selection-plans/new should be removed

@santipalenque santipalenque left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Assuming the form is out of scope. Good job @priscila-moneo

};

return (
<form className="selection-plan-form">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

shouldn't we use MUI components ? or this file is out of scope ? cc @smarcet

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

good observation, I had some time so I updated this file with MUI components

@priscila-moneo priscila-moneo force-pushed the feature/move-selection-plans-grid-mui branch from c8488e1 to e66daff Compare May 12, 2026 21:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/pages/selection-plans/selection-plan-list-page.js (1)

96-100: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix handleDelete to accept the row id, not a row object.

Line 97 returns early for the value MuiTable actually sends to onDelete, so delete clicks become no-ops here.

Suggested fix
-  const handleDelete = (selectionPlan) => {
-    if (!selectionPlan?.id) return;
-
-    deleteSelectionPlan(selectionPlan.id).then(() => refreshSelectionPlans());
+  const handleDelete = (selectionPlanId) => {
+    if (!selectionPlanId) return;
+
+    deleteSelectionPlan(selectionPlanId).then(() => refreshSelectionPlans());
   };

Based on learnings: When using MuiTable from openstack-uicore-foundation/lib/components/mui/table in this codebase, onDelete is called with the primitive row identifier, not the full row object.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/selection-plans/selection-plan-list-page.js` around lines 96 - 100,
The handleDelete function currently expects a selectionPlan object and guards on
selectionPlan?.id, but MuiTable's onDelete passes the primitive row id instead
of the row object; update handleDelete to accept an id parameter (e.g., id) and
return early if id is falsy, then call deleteSelectionPlan(id).then(() =>
refreshSelectionPlans()); adjust any references that call handleDelete to ensure
they pass the id directly; keep function name handleDelete and preserve use of
deleteSelectionPlan and refreshSelectionPlans for locating the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/selection-plans/selection-plan-list-page.js`:
- Around line 255-256: The IconButton rendering the CloseIcon (the element using
IconButton with onClick={handleClosePopup} and CloseIcon) lacks an accessible
name; add an aria-label (for example aria-label="Close" or a localized string)
to the IconButton so screen readers announce its purpose and optionally add a
tooltip/title if desired for sighted users.

---

Duplicate comments:
In `@src/pages/selection-plans/selection-plan-list-page.js`:
- Around line 96-100: The handleDelete function currently expects a
selectionPlan object and guards on selectionPlan?.id, but MuiTable's onDelete
passes the primitive row id instead of the row object; update handleDelete to
accept an id parameter (e.g., id) and return early if id is falsy, then call
deleteSelectionPlan(id).then(() => refreshSelectionPlans()); adjust any
references that call handleDelete to ensure they pass the id directly; keep
function name handleDelete and preserve use of deleteSelectionPlan and
refreshSelectionPlans for locating the code.
🪄 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: 8e9228ae-e203-4da7-9b8d-a3a6c7c0c3f3

📥 Commits

Reviewing files that changed from the base of the PR and between 5aa12fb and e66daff.

📒 Files selected for processing (9)
  • src/actions/selection-plan-actions.js
  • src/components/forms/selection-plan-form.js
  • src/components/inputs/import-modal/index.jsx
  • src/layouts/selection-plan-id-layout.js
  • src/layouts/selection-plan-layout.js
  • src/pages/selection-plans/edit-selection-plan-page.js
  • src/pages/selection-plans/selection-plan-list-page.js
  • src/pages/sponsors/__tests__/edit-sponsor-page.test.js
  • src/reducers/selection_plans/selection-plan-list-reducer.js
✅ Files skipped from review due to trivial changes (1)
  • src/components/inputs/import-modal/index.jsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/pages/selection-plans/edit-selection-plan-page.js
  • src/reducers/selection_plans/selection-plan-list-reducer.js
  • src/layouts/selection-plan-layout.js
  • src/actions/selection-plan-actions.js
  • src/components/forms/selection-plan-form.js

Comment on lines +255 to +256
<IconButton size="small" onClick={handleClosePopup}>
<CloseIcon fontSize="small" />

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 | ⚡ Quick win

Add an accessible name to the close button.

The icon-only IconButton on Line 255 has no label, so assistive tech will announce a generic button. Add an aria-label.

Suggested fix
-          <IconButton size="small" onClick={handleClosePopup}>
+          <IconButton
+            size="small"
+            onClick={handleClosePopup}
+            aria-label={T.translate("general.close")}
+          >
             <CloseIcon fontSize="small" />
           </IconButton>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/selection-plans/selection-plan-list-page.js` around lines 255 -
256, The IconButton rendering the CloseIcon (the element using IconButton with
onClick={handleClosePopup} and CloseIcon) lacks an accessible name; add an
aria-label (for example aria-label="Close" or a localized string) to the
IconButton so screen readers announce its purpose and optionally add a
tooltip/title if desired for sighted users.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/components/forms/selection-plan-form.js (1)

145-151: 💤 Low value

Consider guarding against undefined marketing_settings.

The code assumes marketing_settings exists when calling hasOwnProperty, but the render code uses optional chaining (?.) when reading these values. If propsEntity.marketing_settings is ever undefined, this will throw a TypeError. The same pattern appears in handleOnSwitchChange.

♻️ Suggested defensive guard
     if (id.startsWith("cfp_")) {
+      if (!newEntity.marketing_settings) {
+        newEntity.marketing_settings = {};
+      }
       if (
         !Object.prototype.hasOwnProperty.call(newEntity.marketing_settings, id)
       ) {

Apply similar guard in handleOnSwitchChange around line 247.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/forms/selection-plan-form.js` around lines 145 - 151, The code
assumes newEntity.marketing_settings exists before calling
Object.prototype.hasOwnProperty.call and before assigning keys; add a defensive
guard to initialize marketing_settings when missing (e.g., if
(!newEntity.marketing_settings) newEntity.marketing_settings = {}) before the
block that checks id.startsWith("cfp_") and likewise in handleOnSwitchChange so
both the hasOwnProperty call and subsequent assignment to
newEntity.marketing_settings[id].value are safe when
propsEntity.marketing_settings is undefined; update references to
newEntity.marketing_settings in those functions (the id.startsWith("cfp_")
branch and the handleOnSwitchChange handler) to rely on the initialized object.
src/components/forms/__tests__/selection-plan-form.test.js (1)

150-172: ⚡ Quick win

Consider asserting cleanup after successful save.

Test 3 verifies that onSavingChange(false) is called after rejection, but this test does not verify the same cleanup happens after success. According to the implementation in context snippet 1, .finally() always calls onSavingChange(false), so asserting that behavior here would make the test more complete.

✨ Suggested enhancement
    await act(async () => {
      resolveSave({ id: 1 });
      await Promise.resolve();
    });
+
+   await waitFor(() => {
+     expect(onSavingChange).toHaveBeenCalledWith(false);
+   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/forms/__tests__/selection-plan-form.test.js` around lines 150
- 172, Add an assertion that the component runs its cleanup after a successful
save: after resolving the mocked onSubmit promise (resolveSave) in the test
"disables submit and calls onSavingChange(true) while save is pending", assert
that onSavingChange was later called with false and that the submit button is
enabled again (verifying the .finally() cleanup path implemented around
onSubmit/onSavingChange). Reference the mocked onSubmit, the onSavingChange spy,
and resolveSave when adding these assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/components/forms/__tests__/selection-plan-form.test.js`:
- Around line 150-172: Add an assertion that the component runs its cleanup
after a successful save: after resolving the mocked onSubmit promise
(resolveSave) in the test "disables submit and calls onSavingChange(true) while
save is pending", assert that onSavingChange was later called with false and
that the submit button is enabled again (verifying the .finally() cleanup path
implemented around onSubmit/onSavingChange). Reference the mocked onSubmit, the
onSavingChange spy, and resolveSave when adding these assertions.

In `@src/components/forms/selection-plan-form.js`:
- Around line 145-151: The code assumes newEntity.marketing_settings exists
before calling Object.prototype.hasOwnProperty.call and before assigning keys;
add a defensive guard to initialize marketing_settings when missing (e.g., if
(!newEntity.marketing_settings) newEntity.marketing_settings = {}) before the
block that checks id.startsWith("cfp_") and likewise in handleOnSwitchChange so
both the hasOwnProperty call and subsequent assignment to
newEntity.marketing_settings[id].value are safe when
propsEntity.marketing_settings is undefined; update references to
newEntity.marketing_settings in those functions (the id.startsWith("cfp_")
branch and the handleOnSwitchChange handler) to rely on the initialized object.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5256af02-8c39-4b53-b37b-4d45ea80ad1e

📥 Commits

Reviewing files that changed from the base of the PR and between e66daff and 13befb9.

📒 Files selected for processing (7)
  • src/actions/__tests__/selection-plan-actions.test.js
  • src/actions/selection-plan-actions.js
  • src/components/forms/__tests__/selection-plan-form.test.js
  • src/components/forms/selection-plan-form.js
  • src/pages/selection-plans/__tests__/selection-plan-list-page.test.js
  • src/pages/selection-plans/edit-selection-plan-page.js
  • src/pages/selection-plans/selection-plan-list-page.js
✅ Files skipped from review due to trivial changes (1)
  • src/actions/tests/selection-plan-actions.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/pages/selection-plans/edit-selection-plan-page.js
  • src/pages/selection-plans/selection-plan-list-page.js

});
expect(onSavingChange).toHaveBeenCalledWith(false);
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The three tests here only cover the save-guard (button disabled / double-submit prevention / re-enable on reject). The most critical new behaviour in this PR — the two-step save chain — has no coverage.

Missing: verify that after a successful onSubmit, saveSelectionPlanSettings is called with the correct args and onSaved is called with the saved entity.

it('calls saveSelectionPlanSettings then onSaved on successful submit', async () => {
  const savedEntity = { id: 42 };
  const onSubmitMock = jest.fn().mockResolvedValue(savedEntity);
  const onSavedMock = jest.fn();
  const saveSettingsMock = jest.fn().mockResolvedValue();

  render(
    <SelectionPlanForm
      {...defaultProps}
      onSubmit={onSubmitMock}
      onSaved={onSavedMock}
      saveSelectionPlanSettings={saveSettingsMock}
    />
  );

  await userEvent.click(screen.getByRole('button', { name: 'general.save' }));

  await waitFor(() => {
    expect(saveSettingsMock).toHaveBeenCalledWith(
      defaultEntity.marketing_settings,
      savedEntity.id
    );
    expect(onSavedMock).toHaveBeenCalledWith(savedEntity);
  });
});

@smarcet smarcet left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@priscila-moneo please review

@priscila-moneo priscila-moneo force-pushed the feature/move-selection-plans-grid-mui branch from 13befb9 to 14973bc Compare June 12, 2026 20:36
@priscila-moneo priscila-moneo requested a review from smarcet June 12, 2026 20:36
const handleSort = (index, key, dir) => {
getSelectionPlans(term, currentPage, key, dir);
const handleSavingChange = (saving) => {
isSavingRef.current = saving;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Popup pattern deviation: duplicated isSaving + onSavingChange callback

The dominant pattern across this codebase (media-file-types, form-templates, tags, etc.) is:

  • isSaving lives exclusively in the popup/form component
  • the parent passes a single onSave prop that returns a Promise
  • the popup drives its own close via .then(() => onClose())

Here the form already has its own isSaving (correct), but it also calls onSavingChange(true/false) to bubble the state up to the list page, which then maintains a parallel isSaving + isSavingRef to control the Dialog chrome (escape key, close icon). The isSavingRef workaround exists precisely because of this cross-layer sync.

The cleaner approach: extract the Dialog + DialogTitle into a dedicated SelectionPlanPopup component that receives isSaving as a prop from the form directly, or passes onSavingChange only down to that wrapper — eliminating the ref and the duplicated state in the list page.

Note: the complexity of selection plan (embedded full-page form, multiple sub-resources) makes this a harder refactor than the simpler popups. The deviation is understandable given the constraints, but worth tracking.

Comment thread src/layouts/selection-plan-layout.js Outdated
strict
exact
path={`${match.url}/:selection_plan_id(\\d+)`}
component={SelectionPlanListPage}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This exact /:selection_plan_id route rendering SelectionPlanListPage introduces route-driven popup opening — navigating to /selection-plans/42 loads the list page and auto-opens the dialog for that plan via match.params.selection_plan_id.

This was not part of the requested migration. PR #897 (media-file-types), the reference for this migration wave, took the opposite approach: removed all /new and /:id routes entirely, leaving only list + catch-all redirect. No URL-based popup opening was added there.

The feature adds meaningful complexity: it requires a non-exact duplicate /:id → SelectionPlanIdLayout route to coexist in the same Switch (for sub-routes), plus the useEffect + openEditModal path in the list page, plus isSavingRef as a workaround for the cross-layer state sync this routing requires. None of that would be needed if the popup were opened only by UI interaction (clicking a row), consistent with the rest of the migration.

Remove this route and the corresponding useEffect([openEditModal, routeSelectionPlanId]) in selection-plan-list-page.js. Row edit should open the popup directly, same as media-file-types.

Dropdown
} from "openstack-uicore-foundation/lib/components";
import Input from "openstack-uicore-foundation/lib/components/inputs/text-input";
import DateTimePicker from "openstack-uicore-foundation/lib/components/inputs/datetimepicker";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The form still uses the legacy DateTimePicker from openstack-uicore-foundation/lib/components/inputs/datetimepicker (bootstrap-based, handled via ev.target.type === 'datetime' in handleChange).

Other recently migrated forms in this codebase (sponsor forms, page-template modules) use MuiFormikDatepicker from openstack-uicore-foundation/lib/components/mui/formik-inputs/datepicker, which renders a MUI DatePicker and is consistent with the rest of the MUI migration.

The blocker is that MuiFormikDatepicker is Formik-aware (useField internally), so it cannot be dropped in as a direct replacement without migrating the form to Formik. This form still uses its own useState + handleChange state management.

The selection plan form should be migrated to Formik + MuiFormikDatepicker for the date fields so the date UI is consistent with the rest of the forms shown in the same admin UI.

@smarcet smarcet left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@priscila-moneo please review comments

@priscila-moneo priscila-moneo force-pushed the feature/move-selection-plans-grid-mui branch from 14973bc to b93c014 Compare June 17, 2026 21:50
@priscila-moneo priscila-moneo requested a review from smarcet June 17, 2026 21:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/forms/__tests__/selection-plan-form.test.js (2)

94-98: 💤 Low value

Add time_zone_id to currentSummit for test robustness.

The form uses currentSummit.time_zone_id for date normalization in buildInitialValues and handleFormikSubmit. Tests pass because all date fields are null, but if any test later sets date values, moment.tz(value, undefined) will behave unexpectedly.

 const defaultProps = {
   entity: defaultEntity,
   errors: {},
-  currentSummit: { id: 1 },
+  currentSummit: { id: 1, time_zone_id: "UTC" },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/forms/__tests__/selection-plan-form.test.js` around lines 94 -
98, In the defaultProps object, add the missing time_zone_id property to the
currentSummit object. Set it to a valid timezone string value (such as "UTC" or
another appropriate timezone identifier) so that when the form's
buildInitialValues and handleFormikSubmit methods use currentSummit.time_zone_id
for date normalization with moment.tz(), the timezone will be properly defined
rather than undefined.

18-68: 💤 Low value

Incomplete mocks for form's direct imports.

The form imports components from specific subpaths (e.g., openstack-uicore-foundation/lib/components/inputs/text-input, openstack-uicore-foundation/lib/components/mui/formik-inputs/datepicker) but the mock on line 18 targets the parent /lib/components path. Jest doesn't automatically mock subpaths.

Missing mocks for: MuiFormikDatepicker, Input (text-input), Panel, Dropdown, TextEditorV3, SortableTable (mui), SimpleLinkList.

Tests may pass now because these components render without throwing, but this creates fragility—if any component adds required context or validation, tests will fail unexpectedly.

🧪 Add targeted mocks for imported components
+jest.mock(
+  "openstack-uicore-foundation/lib/components/inputs/text-input",
+  () => ({
+    __esModule: true,
+    default: ({ id, value, onChange }) => (
+      <input id={id} value={value ?? ""} onChange={onChange} />
+    )
+  })
+);
+
+jest.mock(
+  "openstack-uicore-foundation/lib/components/mui/formik-inputs/datepicker",
+  () => ({
+    __esModule: true,
+    default: () => null
+  })
+);
+
+jest.mock(
+  "openstack-uicore-foundation/lib/components/sections/panel",
+  () => ({
+    __esModule: true,
+    default: ({ children }) => <div>{children}</div>
+  })
+);
+
+jest.mock(
+  "openstack-uicore-foundation/lib/components/inputs/dropdown",
+  () => ({
+    __esModule: true,
+    default: () => null
+  })
+);
+
+jest.mock(
+  "openstack-uicore-foundation/lib/components/inputs/editor-input-v3",
+  () => ({
+    __esModule: true,
+    default: () => null
+  })
+);
+
+jest.mock(
+  "openstack-uicore-foundation/lib/components/mui/sortable-table",
+  () => ({
+    __esModule: true,
+    default: () => null
+  })
+);
+
+jest.mock(
+  "openstack-uicore-foundation/lib/components/simple-link-list",
+  () => ({
+    __esModule: true,
+    default: () => null
+  })
+);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/forms/__tests__/selection-plan-form.test.js` around lines 18 -
68, The current mocks only target the parent path
`openstack-uicore-foundation/lib/components` but the form imports components
from specific subpaths like
`openstack-uicore-foundation/lib/components/inputs/text-input` and
`openstack-uicore-foundation/lib/components/mui/formik-inputs/datepicker`. Add
individual jest.mock() calls for each specific subpath that the
selection-plan-form component imports (including MuiFormikDatepicker, Input from
text-input, Panel, Dropdown, TextEditorV3, SortableTable from mui, and
SimpleLinkList), ensuring each mock properly exports the component as either a
default export or named export matching what the component expects.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/selection-plans/selection-plan-list-page.js`:
- Around line 83-89: The handleDelete function currently expects selectionPlan
to be an object and tries to access selectionPlan?.id, but MuiTable's onDelete
callback passes only the primitive ID value directly (not the full row object).
Change the parameter name from selectionPlan to id, update the null check from
if (!selectionPlan?.id) to if (!id), and pass id directly to deleteSelectionPlan
instead of selectionPlan.id.

---

Nitpick comments:
In `@src/components/forms/__tests__/selection-plan-form.test.js`:
- Around line 94-98: In the defaultProps object, add the missing time_zone_id
property to the currentSummit object. Set it to a valid timezone string value
(such as "UTC" or another appropriate timezone identifier) so that when the
form's buildInitialValues and handleFormikSubmit methods use
currentSummit.time_zone_id for date normalization with moment.tz(), the timezone
will be properly defined rather than undefined.
- Around line 18-68: The current mocks only target the parent path
`openstack-uicore-foundation/lib/components` but the form imports components
from specific subpaths like
`openstack-uicore-foundation/lib/components/inputs/text-input` and
`openstack-uicore-foundation/lib/components/mui/formik-inputs/datepicker`. Add
individual jest.mock() calls for each specific subpath that the
selection-plan-form component imports (including MuiFormikDatepicker, Input from
text-input, Panel, Dropdown, TextEditorV3, SortableTable from mui, and
SimpleLinkList), ensuring each mock properly exports the component as either a
default export or named export matching what the component expects.
🪄 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: 10a5bc10-1a24-4623-b74c-1416870b6991

📥 Commits

Reviewing files that changed from the base of the PR and between 13befb9 and b93c014.

📒 Files selected for processing (13)
  • src/actions/__tests__/selection-plan-actions.test.js
  • src/actions/selection-plan-actions.js
  • src/components/forms/__tests__/selection-plan-form.test.js
  • src/components/forms/selection-plan-form.js
  • src/components/inputs/import-modal/index.jsx
  • src/layouts/selection-plan-id-layout.js
  • src/layouts/selection-plan-layout.js
  • src/pages/selection-plans/__tests__/selection-plan-list-page.test.js
  • src/pages/selection-plans/edit-selection-plan-page.js
  • src/pages/selection-plans/selection-plan-list-page.js
  • src/pages/selection-plans/selection-plan-popup.js
  • src/pages/sponsors/__tests__/edit-sponsor-page.test.js
  • src/reducers/selection_plans/selection-plan-list-reducer.js
✅ Files skipped from review due to trivial changes (1)
  • src/components/inputs/import-modal/index.jsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/layouts/selection-plan-id-layout.js
  • src/pages/selection-plans/tests/selection-plan-list-page.test.js
  • src/pages/sponsors/tests/edit-sponsor-page.test.js
  • src/actions/tests/selection-plan-actions.test.js
  • src/reducers/selection_plans/selection-plan-list-reducer.js
  • src/actions/selection-plan-actions.js
  • src/pages/selection-plans/edit-selection-plan-page.js

Comment on lines +83 to 89
const handleDelete = (selectionPlan) => {
if (!selectionPlan?.id) return;

deleteSelectionPlan(selectionPlan.id)
.finally(() => refreshSelectionPlans())
.catch(() => {});
};

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 | ⚡ Quick win

handleDelete receives a primitive ID, not an object — deletes will silently fail.

Based on learnings, MuiTable's onDelete callback receives the primitive row identifier (the id value), not the full row object. Since selectionPlan will be a number like 42, the expression selectionPlan?.id evaluates to undefined, causing the early return on Line 84 to trigger and skip the delete.

Proposed fix
- const handleDelete = (selectionPlan) => {
-   if (!selectionPlan?.id) return;
-
-   deleteSelectionPlan(selectionPlan.id)
+ const handleDelete = (selectionPlanId) => {
+   if (!selectionPlanId) return;
+
+   deleteSelectionPlan(selectionPlanId)
      .finally(() => refreshSelectionPlans())
      .catch(() => {});
  };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/selection-plans/selection-plan-list-page.js` around lines 83 - 89,
The handleDelete function currently expects selectionPlan to be an object and
tries to access selectionPlan?.id, but MuiTable's onDelete callback passes only
the primitive ID value directly (not the full row object). Change the parameter
name from selectionPlan to id, update the null check from if
(!selectionPlan?.id) to if (!id), and pass id directly to deleteSelectionPlan
instead of selectionPlan.id.

Source: Learnings

Signed-off-by: Priscila Moneo <priscila_moneo@hotmail.com.ar>
Signed-off-by: Priscila Moneo <priscila_moneo@hotmail.com.ar>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/i18n/en.json`:
- Around line 571-572: The placeholder text for the link_question and
link_presentation_action_type keys in the English localization file contains the
misspelled word "Existent" which should be "Existing" for correct grammar and
user-facing copy. Update both occurrences by replacing "Existent" with
"Existing" in the respective message strings.
🪄 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: 063b12fd-914d-43d5-b0bb-eadbec7d7a1b

📥 Commits

Reviewing files that changed from the base of the PR and between b93c014 and 8fa5e75.

📒 Files selected for processing (5)
  • src/actions/selection-plan-actions.js
  • src/components/forms/__tests__/selection-plan-form.test.js
  • src/components/forms/selection-plan-form.js
  • src/i18n/en.json
  • src/pages/selection-plans/selection-plan-popup.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/selection-plans/selection-plan-popup.js

Comment thread src/i18n/en.json
Comment on lines 571 to 572
"link_question": "Link an Existent Question...",
"link_presentation_action_type": "Link an Existent Action Type...",

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 | ⚡ Quick win

Fix placeholder wording typo in link actions.

Line 571 and Line 572 use “Existent,” which should be “Existing” for correct user-facing copy.

Suggested diff
-      "link_question": "Link an Existent Question...",
-      "link_presentation_action_type": "Link an Existent Action Type...",
+      "link_question": "Link an Existing Question...",
+      "link_presentation_action_type": "Link an Existing Action Type...",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/en.json` around lines 571 - 572, The placeholder text for the
link_question and link_presentation_action_type keys in the English localization
file contains the misspelled word "Existent" which should be "Existing" for
correct grammar and user-facing copy. Update both occurrences by replacing
"Existent" with "Existing" in the respective message strings.

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.

3 participants