Conversation
📝 WalkthroughWalkthroughAdds 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment Tip CodeRabbit can enforce grammar and style rules using `languagetool`.Configure the |
There was a problem hiding this comment.
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 theopen={openPopup === "template"}prop will always betruewhen the component mounts. This is functionally correct but slightly redundant.Consider either:
- Removing the conditional render and relying solely on the
openprop (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: MissingisSavingguard for duplicate submission prevention.This popup lacks the
isSavingpattern 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
isSavingstate 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
useEffectcorrectly 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
📒 Files selected for processing (12)
src/actions/sponsor-forms-actions.jssrc/pages/sponsors/sponsor-forms-list-page/components/form-template/__tests__/form-template-popup.test.jssrc/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-form.jssrc/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.jssrc/pages/sponsors/sponsor-forms-list-page/components/global-template/__tests__/global-template-popup.test.jssrc/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.jssrc/pages/sponsors/sponsor-forms-list-page/components/global-template/select-sponsorships-dialog.jssrc/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.jssrc/pages/sponsors/sponsor-forms-tab/components/customized-form/__tests__/customized-form-popup.test.jssrc/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form-popup.jssrc/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form.jssrc/pages/sponsors/sponsor-forms-tab/index.js
src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js
Show resolved
Hide resolved
fba522b to
798a7ce
Compare
There was a problem hiding this comment.
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 ofisSavingguard 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.handleSubmiteven when the button is disabled. If the parent'sonSubmitcallback 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
📒 Files selected for processing (12)
src/actions/sponsor-forms-actions.jssrc/pages/sponsors/sponsor-forms-list-page/components/form-template/__tests__/form-template-popup.test.jssrc/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-form.jssrc/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.jssrc/pages/sponsors/sponsor-forms-list-page/components/global-template/__tests__/global-template-popup.test.jssrc/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.jssrc/pages/sponsors/sponsor-forms-list-page/components/global-template/select-sponsorships-dialog.jssrc/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.jssrc/pages/sponsors/sponsor-forms-tab/components/customized-form/__tests__/customized-form-popup.test.jssrc/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form-popup.jssrc/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form.jssrc/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
| <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} |
There was a problem hiding this comment.
🧩 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 -80Repository: 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).
src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form-popup.js
Show resolved
Hide resolved
src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js
Show resolved
Hide resolved
src/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.js
Outdated
Show resolved
Hide resolved
src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js
Outdated
Show resolved
Hide resolved
src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js
Show resolved
Hide resolved
src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js
Show resolved
Hide resolved
...es/sponsors/sponsor-forms-list-page/components/global-template/select-sponsorships-dialog.js
Show resolved
Hide resolved
src/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form-popup.js
Show resolved
Hide resolved
798a7ce to
8124e39
Compare
8124e39 to
569a46f
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.js (1)
22-25:⚠️ Potential issue | 🟡 MinorState not reset when dialog is dismissed via backdrop click.
handleDismissonly callsonClose()without resettingselectedTemplatesandstage. Since this component stays mounted and controls visibility via theopenprop, state persists across open/close cycles. If a user:
- Selects templates → advances to sponsorships stage
- Clicks backdrop to dismiss (calls
handleDismiss)- Reopens the dialog
The dialog will incorrectly show the sponsorships stage with stale
selectedTemplatesinstead 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 justhandleClosesince 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
pendingPromisepattern. However, unlike the parallelform-template-popup.test.js, this file is missing a test that verifiesonSavedandonCloseare 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 addingisSavingguard for consistency with other popups.Unlike other popups in this PR (
FormTemplatePopup,CustomizedFormPopup,GlobalTemplatePopup), this component doesn't have anisSavingguard 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
📒 Files selected for processing (12)
src/actions/sponsor-forms-actions.jssrc/pages/sponsors/sponsor-forms-list-page/components/form-template/__tests__/form-template-popup.test.jssrc/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-form.jssrc/pages/sponsors/sponsor-forms-list-page/components/form-template/form-template-popup.jssrc/pages/sponsors/sponsor-forms-list-page/components/global-template/__tests__/global-template-popup.test.jssrc/pages/sponsors/sponsor-forms-list-page/components/global-template/global-template-popup.jssrc/pages/sponsors/sponsor-forms-list-page/components/global-template/select-sponsorships-dialog.jssrc/pages/sponsors/sponsor-forms-tab/components/add-sponsor-form-template-popup/index.jssrc/pages/sponsors/sponsor-forms-tab/components/customized-form/__tests__/customized-form-popup.test.jssrc/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form-popup.jssrc/pages/sponsors/sponsor-forms-tab/components/customized-form/customized-form.jssrc/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
ref: https://app.clickup.com/t/86b8tct5y
isSavingto prevent duplicate requests from double-clicks in form create/clone dialogs.Summary by CodeRabbit
Bug Fixes
New Features
Tests