Skip to content

fix: adding forms does not close modal#824

Open
priscila-moneo wants to merge 1 commit intomasterfrom
fix/adding-forms-does-not-close-modal
Open

fix: adding forms does not close modal#824
priscila-moneo wants to merge 1 commit intomasterfrom
fix/adding-forms-does-not-close-modal

Conversation

@priscila-moneo
Copy link

@priscila-moneo priscila-moneo commented Mar 11, 2026

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

  • Fixed modal behavior after form creation: modals now close only on successful save and remain open on API errors (no data loss on validation/duplicate-code errors).
  • Fixed list refresh behavior after successful form creation so newly added forms appear immediately without manual page reload.
  • Removed swallowed promise errors in sponsor form actions, allowing UI components to correctly handle success vs failure flows.
  • Added submit hardening isSaving to prevent duplicate requests from double-clicks in form create/clone dialogs.
  • Standardized dialog close handling in global template flow so stage/state resets correctly on close.
  • Added focused regression tests for all updated popup flows:
    • stays open on save/clone error
    • closes on success
    • prevents duplicate submissions
    • resets dialog stage on close

Summary by CodeRabbit

  • Bug Fixes

    • Dialogs remain open on save failure and preserve user input; closing (including Escape) is blocked while a save is in progress.
    • Submit/Close controls are disabled during saves and duplicate save requests are prevented.
    • Loading state reliably stops after save attempts.
  • New Features

    • After saving a customized form, the forms list refreshes automatically.
    • Add-template popup resets selections when opened and handles empty sponsor lists safely.
  • Tests

    • Added unit tests for save-failure handling, prevention of concurrent saves, and post-save close behavior.

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

📝 Walkthrough

Walkthrough

Adds isSaving guards and UI disablement to multiple sponsor form popups to prevent duplicate submissions, moves stopLoading() dispatches into promise .finally() in actions, and adds unit tests for save deduplication and failure retention.

Changes

Cohort / File(s) Summary
Action Handlers
src/actions/sponsor-forms-actions.js
Replace empty .catch() handlers with .finally() to ensure dispatch(stopLoading()) runs for cloneGlobalTemplate, saveFormTemplate, and updateFormTemplate.
Form Template
src/pages/sponsors/.../form-template/form-template-popup.js, src/pages/sponsors/.../form-template/form-template-form.js, src/pages/sponsors/.../form-template/__tests__/form-template-popup.test.js
Add isSaving state/prop, disable submit while saving, prevent re-entrant saves, keep modal open on save failure, close on success; tests for failure retention, deduplication, and success close.
Global Template
src/pages/sponsors/.../global-template/global-template-popup.js, src/pages/sponsors/.../global-template/select-sponsorships-dialog.js, src/pages/sponsors/.../global-template/__tests__/global-template-popup.test.js
Add isSaving state, guard closing while saving, propagate isSaving to SelectSponsorshipsDialog (new prop) to disable Apply during saves; tests for failure retention, close flow, and deduping.
Customized Form
src/pages/sponsors/.../customized-form/customized-form-popup.js, src/pages/sponsors/.../customized-form/customized-form.js, src/pages/sponsors/.../customized-form/__tests__/customized-form-popup.test.js
Add isSaving state and prop, disable submit during save, prevent concurrent saves, preserve dialog on failure, invoke optional onSaved on success; tests for failure retention and deduping.
Sponsor Forms Tab / Integration
src/pages/sponsors/sponsor-forms-tab/index.js, src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js
Wire explicit open prop into AddSponsorFormTemplatePopup (propType added), add handleCustomizedFormSaved and pass onSaved to CustomizedFormPopup to refresh list after save.

Sequence Diagram

sequenceDiagram
    actor User
    participant Form as Form Component
    participant Popup as Popup Component
    participant Redux as Redux Actions
    participant Server as Server

    User->>Form: Click Submit
    Note over Popup: isSaving initially false
    Form->>Popup: handleOnSave()
    alt isSaving === true
        Popup->>Popup: return early (no-op)
    else isSaving === false
        Popup->>Popup: set isSaving = true
        Form->>Form: disable submit button
        Popup->>Redux: dispatch save/update action
        Redux->>Server: send request
        alt success
            Server-->>Redux: success
            Redux-->>Popup: resolved promise
            Popup->>Popup: call onSaved (if any)
            Popup->>Popup: close dialog
        else failure
            Server-->>Redux: error
            Redux-->>Popup: rejected promise
            Popup->>Popup: keep dialog open (preserve input)
        end
        Popup->>Popup: finally -> set isSaving = false
        Form->>Form: re-enable submit button
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • smarcet
  • caseylocker

Poem

🐰 I nibble keys and watch the save,
One click, one hop—no duplicate wave.
If errors come, I keep your form brave,
Then close the door when success I pave. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix: adding forms does not close modal' directly aligns with the main change: fixing modal closure behavior after form operations. It clearly summarizes the primary issue being addressed.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/adding-forms-does-not-close-modal
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can enforce grammar and style rules using `languagetool`.

Configure the reviews.tools.languagetool setting to enable/disable rules and categories. Refer to the LanguageTool Community to learn more.

Copy link

@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: 1

🧹 Nitpick comments (3)
src/pages/sponsors/sponsor-forms-tab/index.js (1)

392-400: Minor redundancy in conditional rendering.

The component is already conditionally rendered with {openPopup === "template" && ...}, so the open={openPopup === "template"} prop will always be true when the component mounts. This is functionally correct but slightly redundant.

Consider either:

  • Removing the conditional render and relying solely on the open prop (preferred for consistent Dialog behavior)
  • Or using open={true} since the condition is already checked
♻️ Option 1: Remove conditional render
-      {openPopup === "template" && (
-        <AddSponsorFormTemplatePopup
-          open={openPopup === "template"}
-          onClose={() => setOpenPopup(null)}
-          onSubmit={handleSaveFormFromTemplate}
-          sponsor={sponsor}
-          summitId={summitId}
-        />
-      )}
+      <AddSponsorFormTemplatePopup
+        open={openPopup === "template"}
+        onClose={() => setOpenPopup(null)}
+        onSubmit={handleSaveFormFromTemplate}
+        sponsor={sponsor}
+        summitId={summitId}
+      />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-forms-tab/index.js` around lines 392 - 400, Remove
the redundant conditional wrapper and render AddSponsorFormTemplatePopup
unconditionally, relying on its open prop to control visibility; specifically,
replace the `{openPopup === "template" && ( <AddSponsorFormTemplatePopup ... />
)}` pattern with a direct <AddSponsorFormTemplatePopup open={openPopup ===
"template"} onClose={() => setOpenPopup(null)}
onSubmit={handleSaveFormFromTemplate} sponsor={sponsor} summitId={summitId} />
so the dialog component itself controls mount/unmount behavior via its open
prop.
src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js (2)

280-287: Missing isSaving guard for duplicate submission prevention.

This popup lacks the isSaving pattern that other popups in this PR implement (e.g., CustomizedFormPopup). The submit button is only disabled when no forms are selected, but not during the save operation. This could allow duplicate submissions via double-click.

Consider adding isSaving state similar to other popups:

♻️ Suggested implementation
 const AddSponsorFormTemplatePopup = ({
   open,
   onClose,
   onSubmit,
   ...
 }) => {
   const [searchTerm, setSearchTerm] = useState("");
   const [selectedForms, setSelectedForms] = useState([]);
+  const [isSaving, setIsSaving] = useState(false);
   
   // ... existing code ...
   
   const formik = useFormik({
     // ...
     onSubmit: (values) => {
+      if (isSaving) return;
       const { add_ons } = values;
       const entity = {
         forms: selectedForms,
         add_ons
       };
-      onSubmit(entity);
+      setIsSaving(true);
+      onSubmit(entity)
+        .finally(() => setIsSaving(false));
     },
     // ...
   });

And update the button:

           <Button
             type="submit"
-            disabled={selectedForms.length === 0}
+            disabled={selectedForms.length === 0 || isSaving}
             fullWidth
             variant="contained"
           >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js`
around lines 280 - 287, The submit button in AddSponsorFormTemplatePopup lacks
an isSaving guard and can be double-submitted; add a boolean isSaving state in
the component, set isSaving = true at the start of the submit handler (e.g.,
handleSubmit or onSubmit) and set it back to false on both success and error,
and update the Button disabled prop to disabled={selectedForms.length === 0 ||
isSaving} (and optionally show a spinner when isSaving is true) to prevent
duplicate submissions.

78-90: Consider resetting local state when popup opens.

The useEffect correctly fetches forms when the popup opens, but the local state (selectedForms, searchTerm) persists between open/close cycles. If a user closes and reopens the popup, their previous selections will still be checked.

♻️ Suggested fix
 useEffect(() => {
   if (open) {
+    setSelectedForms([]);
+    setSearchTerm("");
+    formik.resetForm();
     getSponsorForms(
       term,
       currentPage,
       FIVE_PER_PAGE,
       order,
       orderDir,
       false,
       sponsorshipTypeIds
     );
   }
 }, [open]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js`
around lines 78 - 90, The popup's local state (selectedForms and searchTerm)
persists across open/close cycles causing previous selections to reappear;
update the useEffect watching open (the block that calls getSponsorForms) to
also reset selectedForms to an empty array and searchTerm to an empty string
when open becomes true; ensure you update the same effect that calls
getSponsorForms (or add a small helper) so selectedForms and searchTerm are
cleared on open, leaving other params (term, currentPage, order, orderDir,
sponsorshipTypeIds) unchanged.
🤖 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/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js`:
- Around line 34-35: The modal allows closing via backdrop/Escape and the close
icon even while isSaving is true, which resets the form before the in-flight
save settles; update the close logic so handleClose (and any close icon click
handler and the modal's onClose/backdrop/escape handlers) early-return when
isSaving is true, and disable the close UI while saving. Specifically, add a
guard like "if (isSaving) return" at the top of handleClose, ensure the modal's
onClose callback checks isSaving before calling handleClose, and disable/hide
the close icon (and prevent backdrop/Escape dismissal) while isSaving is true so
the form is only reset after the save promise resolves or rejects.

---

Nitpick comments:
In
`@src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js`:
- Around line 280-287: The submit button in AddSponsorFormTemplatePopup lacks an
isSaving guard and can be double-submitted; add a boolean isSaving state in the
component, set isSaving = true at the start of the submit handler (e.g.,
handleSubmit or onSubmit) and set it back to false on both success and error,
and update the Button disabled prop to disabled={selectedForms.length === 0 ||
isSaving} (and optionally show a spinner when isSaving is true) to prevent
duplicate submissions.
- Around line 78-90: The popup's local state (selectedForms and searchTerm)
persists across open/close cycles causing previous selections to reappear;
update the useEffect watching open (the block that calls getSponsorForms) to
also reset selectedForms to an empty array and searchTerm to an empty string
when open becomes true; ensure you update the same effect that calls
getSponsorForms (or add a small helper) so selectedForms and searchTerm are
cleared on open, leaving other params (term, currentPage, order, orderDir,
sponsorshipTypeIds) unchanged.

In `@src/pages/sponsors/sponsor-forms-tab/index.js`:
- Around line 392-400: Remove the redundant conditional wrapper and render
AddSponsorFormTemplatePopup unconditionally, relying on its open prop to control
visibility; specifically, replace the `{openPopup === "template" && (
<AddSponsorFormTemplatePopup ... /> )}` pattern with a direct
<AddSponsorFormTemplatePopup open={openPopup === "template"} onClose={() =>
setOpenPopup(null)} onSubmit={handleSaveFormFromTemplate} sponsor={sponsor}
summitId={summitId} /> so the dialog component itself controls mount/unmount
behavior via its open prop.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: abdffebb-1df3-4b81-963f-aae9f6fbf2be

📥 Commits

Reviewing files that changed from the base of the PR and between b29d23f and fba522b.

📒 Files selected for processing (12)
  • src/actions/sponsor-forms-actions.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/__tests__/form-template-popup.test.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-form.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/__tests__/global-template-popup.test.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/select-sponsorships-dialog.js
  • src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/__tests__/customized-form-popup.test.js
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form-popup.js
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form.js
  • src/pages/sponsors/sponsor-forms-tab/index.js

@priscila-moneo priscila-moneo force-pushed the fix/adding-forms-does-not-close-modal branch from fba522b to 798a7ce Compare March 12, 2026 15:42
Copy link

@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

🧹 Nitpick comments (1)
src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-form.js (1)

49-49: Good addition of isSaving guard to prevent duplicate submissions.

Disabling the button while saving is a solid UX improvement for preventing double-clicks.

One edge case to consider: pressing Enter in a text field can still trigger formik.handleSubmit even when the button is disabled. If the parent's onSubmit callback doesn't already guard against this, you could add form-level protection:

💡 Optional enhancement
       <Box
         component="form"
-        onSubmit={formik.handleSubmit}
+        onSubmit={(e) => {
+          if (isSaving) {
+            e.preventDefault();
+            return;
+          }
+          formik.handleSubmit(e);
+        }}
         noValidate
         autoComplete="off"
       >

Also applies to: 156-161

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

In
`@src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-form.js`
at line 49, Add a form-level guard so Enter-key submits can't run while isSaving
is true: wrap or replace formik.handleSubmit with a small handler that returns
immediately when isSaving is true before calling formik.handleSubmit or the
parent onSubmit; update the form element's onSubmit to use this guardedSubmit
and ensure any local submit handler (e.g., the component-level submit function
or the prop onSubmit passed to the parent) also checks isSaving to avoid
duplicate processing when Enter is pressed.
🤖 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/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js`:
- Around line 44-55: The dialog currently calls handleClose for all onClose
paths (Dialog and child components), which unconditionally resets
selectedTemplates and stage while an async clone/save (isSaving) may still be
pending, causing loss of state on failure; create a new handleDismiss that
checks isSaving and the close reason and only calls the reset when not saving,
pass handleDismiss to Dialog's onClose and to
SelectTemplatesDialog/SelectSponsorshipsDialog onClose props, add
disableEscapeKeyDown={isSaving} to Dialog, and move the unconditional state
reset into the success branch of the save handler (keep state intact on
failure).

In
`@src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form-popup.js`:
- Around line 35-61: The current handleOnSave starts an async save but does not
guard against stale completions closing a newer dialog session; modify
handleOnSave/handleClose to track a session token (use a ref like sessionRef
incremented when the dialog opens or on handleClose) and capture the token
before calling save (saveSponsorCustomizedForm/updateSponsorCustomizedForm),
then in the .then()/.catch()/.finally() only call onSaved() and handleClose()
(and clear isSaving) if the captured token still matches sessionRef.current;
this prevents an earlier promise from resetting/closing a later session while
keeping existing logic and names (handleOnSave, handleClose, isSaving,
saveSponsorCustomizedForm, updateSponsorCustomizedForm).

In `@src/pages/sponsors/sponsor-forms-tab/index.js`:
- Around line 175-189: handleCustomizedFormSaved currently forces the list to
page 1 by passing DEFAULT_CURRENT_PAGE to getSponsorCustomizedForms; instead
destructure the current page from customizedForms (e.g. const { currentPage:
customizedCurrentPage, perPage: customizedPerPage, order: customizedOrder,
orderDir: customizedOrderDir } = customizedForms) and pass that
customizedCurrentPage to getSponsorCustomizedForms so the table stays on the
same page after create/edit; update handleCustomizedFormSaved to use that
variable in place of DEFAULT_CURRENT_PAGE.

---

Nitpick comments:
In
`@src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-form.js`:
- Line 49: Add a form-level guard so Enter-key submits can't run while isSaving
is true: wrap or replace formik.handleSubmit with a small handler that returns
immediately when isSaving is true before calling formik.handleSubmit or the
parent onSubmit; update the form element's onSubmit to use this guardedSubmit
and ensure any local submit handler (e.g., the component-level submit function
or the prop onSubmit passed to the parent) also checks isSaving to avoid
duplicate processing when Enter is pressed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6fb1011b-5c89-481d-ac2c-f2407a8436d7

📥 Commits

Reviewing files that changed from the base of the PR and between fba522b and 798a7ce.

📒 Files selected for processing (12)
  • src/actions/sponsor-forms-actions.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/__tests__/form-template-popup.test.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-form.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/__tests__/global-template-popup.test.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/select-sponsorships-dialog.js
  • src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/__tests__/customized-form-popup.test.js
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form-popup.js
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form.js
  • src/pages/sponsors/sponsor-forms-tab/index.js
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/actions/sponsor-forms-actions.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/select-sponsorships-dialog.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/tests/form-template-popup.test.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/tests/global-template-popup.test.js

Comment on lines +44 to +55
<Dialog open={open} onClose={handleClose} maxWidth={dialogSize} fullWidth>
{stage === "templates" && (
<SelectTemplatesDialog
onSave={handleOnSelectTemplates}
onClose={handleClose}
/>
)}
{stage === "sponsorships" && (
<SelectSponsorshipsDialog onSave={handleOnSave} onClose={handleClose} />
<SelectSponsorshipsDialog
onSave={handleOnSave}
onClose={handleClose}
isSaving={isSaving}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In @mui/materialv6 Dialog, doesonClose fire for backdrop clicks and Escape, and what is the recommended way to prevent dismissal while an async save is pending?

💡 Result:

Yes. In @mui/material v6 Dialog, onClose is called when the user clicks the backdrop or presses Escape; the handler receives a second argument reason with values "backdropClick" or "escapeKeyDown". [1] If you set disableEscapeKeyDown, then pressing Escape will not fire onClose. [1]

Recommended pattern to prevent dismissal while an async save is pending: keep the dialog controlled (open in state), and in onClose ignore backdrop/Escape close attempts while saving is true (and typically set disableEscapeKeyDown while saving). This is also the recommended replacement for the removed/deprecated backdrop-specific props—use onClose + reason. [2]

const [open, setOpen] = useState(false);
const [saving, setSaving] = useState(false);

const handleClose = (_event: unknown, reason?: string) => {
  if (saving && (reason === 'backdropClick' || reason === 'escapeKeyDown')) return;
  setOpen(false);
};

<Dialog
  open={open}
  onClose={handleClose}
  disableEscapeKeyDown={saving}
>
  ...
</Dialog>

Sources: [1], [2]


🏁 Script executed:

cat -n src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js | head -80

Repository: fntechgit/summit-admin

Length of output: 2552


Block manual dismissal while the clone request is pending.

Lines 44 and 52–54 route all user-initiated closes (backdrop click, Escape, cancel button) directly to handleClose, which unconditionally resets selectedTemplates and stage before the async request settles. If the save fails, the user's progress is lost despite .catch() attempting to keep the dialog open (the state is already cleared).

Gate UI dismissal on isSaving by creating a handleDismiss function that checks the close reason and only proceeds if not saving, add disableEscapeKeyDown={isSaving} to the Dialog, and pass handleDismiss to both the Dialog and child components' onClose props. Reserve the unconditional reset for the success path only.

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

In
`@src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js`
around lines 44 - 55, The dialog currently calls handleClose for all onClose
paths (Dialog and child components), which unconditionally resets
selectedTemplates and stage while an async clone/save (isSaving) may still be
pending, causing loss of state on failure; create a new handleDismiss that
checks isSaving and the close reason and only calls the reset when not saving,
pass handleDismiss to Dialog's onClose and to
SelectTemplatesDialog/SelectSponsorshipsDialog onClose props, add
disableEscapeKeyDown={isSaving} to Dialog, and move the unconditional state
reset into the success branch of the save handler (keep state intact on
failure).

@priscila-moneo priscila-moneo marked this pull request as draft March 12, 2026 20:29
@priscila-moneo priscila-moneo force-pushed the fix/adding-forms-does-not-close-modal branch from 798a7ce to 8124e39 Compare March 16, 2026 23:22
@priscila-moneo priscila-moneo force-pushed the fix/adding-forms-does-not-close-modal branch from 8124e39 to 569a46f Compare March 16, 2026 23:36
@priscila-moneo priscila-moneo marked this pull request as ready for review March 17, 2026 13:57
Copy link

@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 (1)
src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js (1)

22-25: ⚠️ Potential issue | 🟡 Minor

State not reset when dialog is dismissed via backdrop click.

handleDismiss only calls onClose() without resetting selectedTemplates and stage. Since this component stays mounted and controls visibility via the open prop, state persists across open/close cycles. If a user:

  1. Selects templates → advances to sponsorships stage
  2. Clicks backdrop to dismiss (calls handleDismiss)
  3. Reopens the dialog

The dialog will incorrectly show the sponsorships stage with stale selectedTemplates instead of starting fresh at the templates stage.

Suggested fix: Reset state in handleDismiss or add useEffect

Option 1 - Reset in handleDismiss:

 const handleDismiss = () => {
   if (isSaving) return;
+  setSelectedTemplates([]);
+  setStage("templates");
   onClose();
 };

Option 2 - Reset on open via useEffect:

+useEffect(() => {
+  if (open) {
+    setSelectedTemplates([]);
+    setStage("templates");
+  }
+}, [open]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js`
around lines 22 - 25, handleDismiss currently only calls onClose(), so
selectedTemplates and stage persist across opens; update handleDismiss in
global-template-popup.js to reset selectedTemplates and stage to their initial
values before calling onClose (or alternatively add a useEffect that watches the
open prop and resets selectedTemplates and stage when open becomes false/true);
reference the state variables selectedTemplates and stage and the handler
handleDismiss (or implement the useEffect tied to the open prop) so the dialog
always starts on the templates stage with an empty selection when dismissed and
reopened.
🧹 Nitpick comments (3)
src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js (1)

71-73: Minor: Inline arrow function is unnecessary.

The wrapper () => { handleClose(); } can be simplified to just handleClose since they're functionally equivalent here.

Suggested simplification
     <Dialog
       open={open}
-      onClose={() => {
-        handleClose();
-      }}
+      onClose={handleClose}
       maxWidth="md"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js`
around lines 71 - 73, Replace the unnecessary inline arrow function passed to
the onClose prop with the direct function reference to simplify the code: change
the onClose handler currently written as () => { handleClose(); } to use
handleClose directly (this affects the component where onClose is set and the
handleClose function defined).
src/pages/sponsors/sponsor-forms-tab/components/customized-form/__tests__/customized-form-popup.test.js (1)

86-120: Good test for save deduplication, but consider adding a success case test.

The duplicate save prevention test is well-structured with the pendingPromise pattern. However, unlike the parallel form-template-popup.test.js, this file is missing a test that verifies onSaved and onClose are called on successful save.

Suggested test case to add
it("closes modal and calls onSaved after successful save", async () => {
  const onClose = jest.fn();
  const onSaved = jest.fn();
  saveSponsorCustomizedForm.mockReturnValue(() => Promise.resolve());

  renderWithRedux(
    <CustomizedFormPopup
      formId={null}
      open
      onClose={onClose}
      onSaved={onSaved}
      sponsor={sponsor}
      summitId={69}
    />,
    { initialState }
  );

  await act(async () => {
    await userEvent.click(
      screen.getByRole("button", { name: "submit-customized-form" })
    );
    await Promise.resolve();
  });

  expect(saveSponsorCustomizedForm).toHaveBeenCalledTimes(1);
  expect(onSaved).toHaveBeenCalledTimes(1);
  expect(onClose).toHaveBeenCalledTimes(1);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-forms-tab/components/customized-form/__tests__/customized-form-popup.test.js`
around lines 86 - 120, Add a new test to verify the success path: mock
saveSponsorCustomizedForm to return a function that resolves
(saveSponsorCustomizedForm.mockReturnValue(() => Promise.resolve())), render
CustomizedFormPopup via renderWithRedux passing jest.fn() for onSaved and
onClose, trigger the submit button via
userEvent.click(screen.getByRole("button", { name: "submit-customized-form" }))
inside act and await Promise.resolve() to flush promises, then assert
saveSponsorCustomizedForm was called once and both onSaved and onClose were each
called once.
src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js (1)

296-303: Consider adding isSaving guard for consistency with other popups.

Unlike other popups in this PR (FormTemplatePopup, CustomizedFormPopup, GlobalTemplatePopup), this component doesn't have an isSaving guard to prevent duplicate submissions. While the parent handles the async save, adding a consistent pattern would prevent double-click issues if users rapidly click the submit button.

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

In
`@src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js`
around lines 296 - 303, The submit flow lacks an isSaving guard which allows
duplicate submissions; add a local isSaving state in the
AddSponsorFormTemplatePopup component, update the Button to be disabled when
either selectedForms.length === 0 or isSaving, and modify the submit handler
(the function passed to the form's onSubmit or the existing handleSubmit) to
early-return if isSaving, set isSaving = true at the start, await the parent
async save call, and finally set isSaving = false (in a finally block) so rapid
double-clicks are ignored and the UX matches
FormTemplatePopup/CustomizedFormPopup/GlobalTemplatePopup.
🤖 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/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js`:
- Around line 22-25: handleDismiss currently only calls onClose(), so
selectedTemplates and stage persist across opens; update handleDismiss in
global-template-popup.js to reset selectedTemplates and stage to their initial
values before calling onClose (or alternatively add a useEffect that watches the
open prop and resets selectedTemplates and stage when open becomes false/true);
reference the state variables selectedTemplates and stage and the handler
handleDismiss (or implement the useEffect tied to the open prop) so the dialog
always starts on the templates stage with an empty selection when dismissed and
reopened.

---

Nitpick comments:
In
`@src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js`:
- Around line 71-73: Replace the unnecessary inline arrow function passed to the
onClose prop with the direct function reference to simplify the code: change the
onClose handler currently written as () => { handleClose(); } to use handleClose
directly (this affects the component where onClose is set and the handleClose
function defined).

In
`@src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js`:
- Around line 296-303: The submit flow lacks an isSaving guard which allows
duplicate submissions; add a local isSaving state in the
AddSponsorFormTemplatePopup component, update the Button to be disabled when
either selectedForms.length === 0 or isSaving, and modify the submit handler
(the function passed to the form's onSubmit or the existing handleSubmit) to
early-return if isSaving, set isSaving = true at the start, await the parent
async save call, and finally set isSaving = false (in a finally block) so rapid
double-clicks are ignored and the UX matches
FormTemplatePopup/CustomizedFormPopup/GlobalTemplatePopup.

In
`@src/pages/sponsors/sponsor-forms-tab/components/customized-form/__tests__/customized-form-popup.test.js`:
- Around line 86-120: Add a new test to verify the success path: mock
saveSponsorCustomizedForm to return a function that resolves
(saveSponsorCustomizedForm.mockReturnValue(() => Promise.resolve())), render
CustomizedFormPopup via renderWithRedux passing jest.fn() for onSaved and
onClose, trigger the submit button via
userEvent.click(screen.getByRole("button", { name: "submit-customized-form" }))
inside act and await Promise.resolve() to flush promises, then assert
saveSponsorCustomizedForm was called once and both onSaved and onClose were each
called once.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6dee8308-7223-435f-8a48-8a0a9284bee9

📥 Commits

Reviewing files that changed from the base of the PR and between 798a7ce and 569a46f.

📒 Files selected for processing (12)
  • src/actions/sponsor-forms-actions.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/__tests__/form-template-popup.test.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-form.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/__tests__/global-template-popup.test.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/select-sponsorships-dialog.js
  • src/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.js
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/__tests__/customized-form-popup.test.js
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form-popup.js
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form.js
  • src/pages/sponsors/sponsor-forms-tab/index.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form.js
  • src/pages/sponsors/sponsor-forms-tab/index.js
  • src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-form.js
  • src/pages/sponsors/sponsor-forms-list-page/components/global-template/tests/global-template-popup.test.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.

2 participants