Skip to content

feat: add popups for import and add users on sponsors global#963

Open
tomrndom wants to merge 5 commits into
masterfrom
feature/sponsor-global-user-add-import
Open

feat: add popups for import and add users on sponsors global#963
tomrndom wants to merge 5 commits into
masterfrom
feature/sponsor-global-user-add-import

Conversation

@tomrndom

@tomrndom tomrndom commented Jun 3, 2026

Copy link
Copy Markdown

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

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

Summary by CodeRabbit

  • New Features
    • Added sponsor user dialogs: invite (sponsor picker + email validation) and global import (summit picker, sponsor picker, multi-user checkbox list, and “load more” pagination).
    • Import supports “select all” for bulk selection.
  • Bug Fixes & Improvements
    • Sponsor user invite can target a specified sponsor; otherwise it uses the currently active sponsor.
    • Sponsor user fetching now includes the relevant company context.
  • Tests
    • Added/extended unit tests for both dialogs, covering validation, loading/disabled states, selection changes, pagination behavior, and correct action dispatch.

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

coderabbitai Bot commented Jun 3, 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

Adds global sponsor-user invite and import popups, updates sponsor query and invite payload handling, integrates the popups into the sponsor users page, and adds tests for both flows.

Changes

Sponsor User Management Popups

Layer / File(s) Summary
Sponsor data enrichment with companyId mapping
src/actions/sponsor-actions.js
Updated querySponsors to include companyId in each sponsor option alongside name; minor import formatting change.
Flexible sponsor targeting in sendSponsorUserInvite action
src/actions/sponsor-users-actions.js
Modified sendSponsorUserInvite to accept optional sponsorId and use it for sponsor_id when provided, while preserving the existing loading and POST flow.
New user invitation popup with sponsor selection
src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-new-user-popup.js
Added SponsorGlobalNewUserPopup with Formik/Yup sponsor and email inputs, invite submission, post-submit refresh, and Redux-connected export.
Bulk import users popup with summit and sponsor targeting
src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js
Added SponsorGlobalImportUsersPopup with summit selection, sponsor lookup, paginated sponsor-user fetching, checkbox selection, select-all, and import submission.
SponsorUsersListPage integration of new popups
src/pages/sponsors/sponsor-users-list-page/index.js
Replaced placeholder popup renders with the new global popups and passed summitId from Redux.
Tests for SponsorGlobalImportUsersPopup
src/pages/sponsors/sponsor-users-list-page/components/__tests__/sponsor-global-import-users-popup.test.js
Added RTL coverage for selection, fetch, import payloads, select-all, pagination, and close behavior.
Tests for SponsorGlobalNewUserPopup
src/pages/sponsors/sponsor-users-list-page/components/__tests__/sponsor-global-new-user-popup.test.js
Added RTL coverage for rendering, successful invite flow, error handling, and validation guards.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • fntechgit/summit-admin#888: Introduces related sponsor-user import flow changes that overlap with the new import popup and action payload shape.

Suggested Reviewers

  • smarcet

Poem

🐰 I hopped through summits, sponsor by sponsor,
New invites and imports now wander and ponder.
A checkbox, a form, and a cheerful new call,
Then users appear — or get gathered in all.
🥕

🚥 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 matches the main change: adding global sponsor popups for importing and adding users.
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/sponsor-global-user-add-import

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

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

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

🤖 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/actions/sponsor-users-actions.js`:
- Around line 535-547: The current promise chain swallows errors with
catch(console.log) and calls dispatch(stopLoading()) twice; remove the
catch(console.log) so rejections propagate to callers (or replace it with a
catch that logs and rethrows), and remove the stopLoading() call inside the
.then handler so stopLoading is invoked only from .finally; update the chain
around dispatch, stopLoading, snackbarSuccessHandler, and
T.translate("sponsor_users.new_user.success") accordingly so success still shows
the snackbar but loading is stopped exactly once in .finally.

In
`@src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`:
- Around line 86-95: The handleImport handler currently calls
importSponsorUsers(...).then(() => onClose()) which will close the dialog even
on failure; change it to await the thunk result from importSponsorUsers in
handleImport (or handle its returned promise) and only call onClose() when the
thunk returns an explicit success flag (e.g., { success: true }) or resolves
truthy; update importSponsorUsers so it resolves/rejects or returns an explicit
success value on completion, and gate onClose() on that success result (and
handle or surface errors when it fails instead of closing).
- Around line 49-50: The code reads the selected sponsor's id from
formik.values.sponsor?.value but the select stores {id, name, companyId}, so
sponsorId becomes undefined; update both occurrences (the sponsorId assignment
and the other similar block around lines where companyId is used) to use
formik.values.sponsor?.id instead of .value, and ensure any downstream uses
(e.g., the import target and candidate list loader) reference this corrected
sponsorId and continue to use formik.values.sponsor?.companyId for companyId.

In
`@src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-new-user-popup.js`:
- Around line 37-41: The handler handleOnSave currently closes the popup in a
finally block so failures also dismiss the form; change it to call
sendSponsorUserInvite(values.email, values.sponsor.id).then(() => {
getSponsorUsers(); handleClose(); }).catch((err) => { /* keep popup open and
surface error to user */ }); and ensure the sendSponsorUserInvite thunk returns
a rejected Promise on failure so the catch runs; reference handleOnSave,
sendSponsorUserInvite, getSponsorUsers, and handleClose when making the change.
🪄 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: 67165833-76a2-4a0f-91a7-33147faa7006

📥 Commits

Reviewing files that changed from the base of the PR and between 38c8fc5 and 338a22d.

📒 Files selected for processing (5)
  • src/actions/sponsor-actions.js
  • src/actions/sponsor-users-actions.js
  • src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js
  • src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-new-user-popup.js
  • src/pages/sponsors/sponsor-users-list-page/index.js

Comment thread src/actions/sponsor-users-actions.js
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@tomrndom tomrndom requested a review from smarcet June 4, 2026 13:21

@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.

@tomrndom please review comments

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

@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.

♻️ Duplicate comments (3)
src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-new-user-popup.js (1)

43-49: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Success path is still triggered on failed invites.

Line 43 chains .then(...), but the thunk currently swallows errors in src/actions/sponsor-users-actions.js (.catch(console.log)), so this block can still run after a failed API call and close/refresh the dialog incorrectly. Gate close/refresh on an explicit success result (or make the thunk reject on failure).

🤖 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/sponsors/sponsor-users-list-page/components/sponsor-global-new-user-popup.js`
around lines 43 - 49, The success path currently runs even when the invite fails
because sendSponsorUserInvite swallows errors; update the flow so
sendSponsorUserInvite (the thunk in src/actions/sponsor-users-actions.js)
returns a rejected promise on failure (remove the terminal .catch(console.log)
or re-throw the error) or have the caller inspect an explicit success result
before proceeding; then in sponsor-global-new-user-popup.js, only call
getSponsorUsers() and handleClose() when sendSponsorUserInvite resolves
successfully (or when the resolved payload indicates success) and ensure
setIsSaving(false) still runs in finally.
src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js (2)

94-96: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Import dialog can close even when import fails.

Line 95 closes in .then(...), but the current thunk contract in src/actions/sponsor-users-actions.js swallows failures, so this can still execute on failed imports. Only close after a verifiable success result (or make thunk reject on failure).

🤖 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/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`
around lines 94 - 96, The import dialog is closed unconditionally in the then()
callback of importSponsorUsers even though the import thunk in
sponsor-users-actions.js swallows failures; change the flow so onClose is only
called after a verifiable success: either update the thunk importSponsorUsers to
reject the promise on failure, or change the caller to inspect the resolved
value (e.g., check a success flag or returned result) before calling onClose;
keep setIsSaving(false) in finally() but move onClose into the branch that
confirms success so failures do not close the dialog.

51-52: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Use sponsor id, not value, for selected sponsor state.

Line 51 reads formik.values.sponsor?.value, but Line 156-160 stores { id, name, companyId }. This leaves sponsorId undefined and breaks candidate loading/import targeting.

Proposed fix
-  const sponsorId = formik.values.sponsor?.value;
+  const sponsorId = formik.values.sponsor?.id;

Also applies to: 156-160

🤖 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/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`
around lines 51 - 52, The selected sponsor was read from
formik.values.sponsor?.value but the stored object uses { id, name, companyId },
so sponsorId becomes undefined; update the code to read
formik.values.sponsor?.id (leave companyId as formik.values.sponsor?.companyId)
wherever sponsorId is derived (e.g., the sponsorId and companyId consts and any
import/targeting logic) so it matches the stored { id, name, companyId } shape
and restores correct candidate loading/import targeting.
🤖 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.

Duplicate comments:
In
`@src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`:
- Around line 94-96: The import dialog is closed unconditionally in the then()
callback of importSponsorUsers even though the import thunk in
sponsor-users-actions.js swallows failures; change the flow so onClose is only
called after a verifiable success: either update the thunk importSponsorUsers to
reject the promise on failure, or change the caller to inspect the resolved
value (e.g., check a success flag or returned result) before calling onClose;
keep setIsSaving(false) in finally() but move onClose into the branch that
confirms success so failures do not close the dialog.
- Around line 51-52: The selected sponsor was read from
formik.values.sponsor?.value but the stored object uses { id, name, companyId },
so sponsorId becomes undefined; update the code to read
formik.values.sponsor?.id (leave companyId as formik.values.sponsor?.companyId)
wherever sponsorId is derived (e.g., the sponsorId and companyId consts and any
import/targeting logic) so it matches the stored { id, name, companyId } shape
and restores correct candidate loading/import targeting.

In
`@src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-new-user-popup.js`:
- Around line 43-49: The success path currently runs even when the invite fails
because sendSponsorUserInvite swallows errors; update the flow so
sendSponsorUserInvite (the thunk in src/actions/sponsor-users-actions.js)
returns a rejected promise on failure (remove the terminal .catch(console.log)
or re-throw the error) or have the caller inspect an explicit success result
before proceeding; then in sponsor-global-new-user-popup.js, only call
getSponsorUsers() and handleClose() when sendSponsorUserInvite resolves
successfully (or when the resolved payload indicates success) and ensure
setIsSaving(false) still runs in finally.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5c64094e-48bc-48bc-8ff6-a00458e2f6bf

📥 Commits

Reviewing files that changed from the base of the PR and between b3c4f7a and fc08562.

📒 Files selected for processing (4)
  • src/pages/sponsors/sponsor-users-list-page/components/__tests__/sponsor-global-import-users-popup.test.js
  • src/pages/sponsors/sponsor-users-list-page/components/__tests__/sponsor-global-new-user-popup.test.js
  • src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js
  • src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-new-user-popup.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/pages/sponsors/sponsor-users-list-page/components/tests/sponsor-global-new-user-popup.test.js
  • src/pages/sponsors/sponsor-users-list-page/components/tests/sponsor-global-import-users-popup.test.js

@smarcet smarcet self-requested a review June 23, 2026 20:41
Comment thread src/actions/sponsor-users-actions.js Outdated

@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.

@tomrndom please review comments

@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.

♻️ Duplicate comments (2)
src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js (2)

102-108: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Gate onClose() on a confirmed successful import.

With the current thunk contract, importSponsorUsers resolves even on failure (its internal .catch), so this .then runs and the dialog closes / selection is lost even when nothing was imported, and there is no explicit .catch. Have the thunk reject or return an explicit success flag and only call onClose() on success.

🤖 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/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`
around lines 102 - 108, The `handleImport` flow closes the popup too early
because `importSponsorUsers` currently resolves even when the import fails, so
the `.then(() => onClose())` path runs unconditionally. Update the thunk
contract used by `handleImport` in `sponsor-global-import-users-popup.js` so
`importSponsorUsers` either rejects on failure or returns an explicit success
result, then gate `onClose()` behind a confirmed success check and add a failure
path that keeps the dialog open.

52-53: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Read the selected sponsor's id, not value.

formatSelectedValue stores { id, name, companyId }, so formik.values.sponsor?.value is always undefined. As a result sponsorId stays falsy: the fetch effect (Line 56) never runs, the user checklist (Line 175) never renders, and import (Line 105) sends an undefined target sponsor.

Proposed fix
-  const sponsorId = formik.values.sponsor?.value;
+  const sponsorId = formik.values.sponsor?.id;
🤖 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/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`
around lines 52 - 53, The sponsor lookup is reading the wrong field from the
selected sponsor object: in sponsor-global-import-users-popup.js, use the id
stored by formatSelectedValue instead of sponsor.value when deriving sponsorId
in the popup logic. Update the sponsorId assignment near the Formik value reads
so the fetch effect, checklist rendering, and import flow all receive the actual
sponsor identifier, while keeping companyId sourced from the same selected
sponsor object.
🤖 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.

Duplicate comments:
In
`@src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`:
- Around line 102-108: The `handleImport` flow closes the popup too early
because `importSponsorUsers` currently resolves even when the import fails, so
the `.then(() => onClose())` path runs unconditionally. Update the thunk
contract used by `handleImport` in `sponsor-global-import-users-popup.js` so
`importSponsorUsers` either rejects on failure or returns an explicit success
result, then gate `onClose()` behind a confirmed success check and add a failure
path that keeps the dialog open.
- Around line 52-53: The sponsor lookup is reading the wrong field from the
selected sponsor object: in sponsor-global-import-users-popup.js, use the id
stored by formatSelectedValue instead of sponsor.value when deriving sponsorId
in the popup logic. Update the sponsorId assignment near the Formik value reads
so the fetch effect, checklist rendering, and import flow all receive the actual
sponsor identifier, while keeping companyId sourced from the same selected
sponsor object.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bfc6f2b3-f74f-407a-840d-25af6ce009df

📥 Commits

Reviewing files that changed from the base of the PR and between fc08562 and 3b2bc70.

📒 Files selected for processing (2)
  • src/actions/sponsor-users-actions.js
  • src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js
💤 Files with no reviewable changes (1)
  • src/actions/sponsor-users-actions.js

…omise errors

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@tomrndom tomrndom force-pushed the feature/sponsor-global-user-add-import branch from 3b2bc70 to cbc2f4b Compare June 24, 2026 15:15

@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/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js (1)

52-53: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Use sponsor.id instead of sponsor.value for the selected sponsor key.

Line 52 reads formik.values.sponsor?.value, but this form stores id (Line 41, Line 44, Line 168). That leaves sponsorId undefined for fetch/render/import paths (Line 56, Line 105, Line 176).

Proposed fix
-  const sponsorId = formik.values.sponsor?.value;
+  const sponsorId = formik.values.sponsor?.id;

Also applies to: 105-106, 176-176

🤖 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/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`
around lines 52 - 53, The selected sponsor key is being read from sponsor.value
even though this form stores the sponsor identifier on sponsor.id, which leaves
sponsorId undefined in the fetch/render/import flows. Update
sponsor-global-import-users-popup.js in the relevant sponsorId reads and any
dependent logic in the import/render paths to use the sponsor object’s id field
consistently, keeping companyId unchanged, and verify the same identifier is
used anywhere the selected sponsor is passed into the query or import handlers.
🤖 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/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`:
- Around line 77-93: The load-more handler in
sponsor-global-import-users-popup.js can merge stale results and crash when the
current userOptions state is null. Update handleLoadMoreUsers to guard the
setUserOptions merge against a missing previous state, and ensure the response
from fetchSponsorUsersBySummit is only applied if it still matches the current
sponsor/summit context (for example by checking currentSummit.id,
selectedSummit, and companyId against the request that was issued). Keep the
protection around isLoadingMore and the pagination check, but avoid appending
userData.data to a stale or null value.data.

---

Duplicate comments:
In
`@src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`:
- Around line 52-53: The selected sponsor key is being read from sponsor.value
even though this form stores the sponsor identifier on sponsor.id, which leaves
sponsorId undefined in the fetch/render/import flows. Update
sponsor-global-import-users-popup.js in the relevant sponsorId reads and any
dependent logic in the import/render paths to use the sponsor object’s id field
consistently, keeping companyId unchanged, and verify the same identifier is
used anywhere the selected sponsor is passed into the query or import handlers.
🪄 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: b38f6943-66e0-4fb0-9496-c633764c25e1

📥 Commits

Reviewing files that changed from the base of the PR and between 3b2bc70 and cbc2f4b.

📒 Files selected for processing (2)
  • src/actions/sponsor-users-actions.js
  • src/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js
💤 Files with no reviewable changes (1)
  • src/actions/sponsor-users-actions.js

Comment on lines +77 to +93
const handleLoadMoreUsers = () => {
if (isLoadingMore) return;
if (userOptions.current_page < userOptions.last_page) {
setIsLoadingMore(true);
fetchSponsorUsersBySummit(
currentSummit.id,
selectedSummit,
companyId,
userOptions.current_page + 1
)
.then((userData) => {
setUserOptions((value) => ({
...userData,
data: [...value.data, ...userData.data]
}));
})
.finally(() => setIsLoadingMore(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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Protect load-more from stale responses and null merge state.

Lines 88-91 assume value.data exists and always belong to the current sponsor/summit context. If context changes mid-request (Lines 58-59/149-150), this can merge stale rows or throw on value.data when value is null.

Proposed fix
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
...
+  const loadMoreRequestIdRef = useRef(0);
...
   useEffect(() => {
     if (selectedSummit && sponsorId && companyId) {
+      loadMoreRequestIdRef.current += 1; // invalidate in-flight load-more requests
       let cancelled = false;
       setUserOptions(null);
       setSelectedUsers([]);
...
   const handleLoadMoreUsers = () => {
-    if (isLoadingMore) return;
-    if (userOptions.current_page < userOptions.last_page) {
+    if (isLoadingMore || !userOptions) return;
+    if (userOptions.current_page < userOptions.last_page) {
+      const requestId = ++loadMoreRequestIdRef.current;
       setIsLoadingMore(true);
       fetchSponsorUsersBySummit(
         currentSummit.id,
         selectedSummit,
         companyId,
         userOptions.current_page + 1
       )
         .then((userData) => {
+          if (requestId !== loadMoreRequestIdRef.current || !userData?.data) return;
           setUserOptions((value) => ({
+            ...(value || {}),
             ...userData,
-            data: [...value.data, ...userData.data]
+            data: [...(value?.data || []), ...userData.data]
           }));
         })
         .finally(() => setIsLoadingMore(false));
     }
   };
📝 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 handleLoadMoreUsers = () => {
if (isLoadingMore) return;
if (userOptions.current_page < userOptions.last_page) {
setIsLoadingMore(true);
fetchSponsorUsersBySummit(
currentSummit.id,
selectedSummit,
companyId,
userOptions.current_page + 1
)
.then((userData) => {
setUserOptions((value) => ({
...userData,
data: [...value.data, ...userData.data]
}));
})
.finally(() => setIsLoadingMore(false));
const handleLoadMoreUsers = () => {
if (isLoadingMore || !userOptions) return;
if (userOptions.current_page < userOptions.last_page) {
const requestId = ++loadMoreRequestIdRef.current;
setIsLoadingMore(true);
fetchSponsorUsersBySummit(
currentSummit.id,
selectedSummit,
companyId,
userOptions.current_page + 1
)
.then((userData) => {
if (requestId !== loadMoreRequestIdRef.current || !userData?.data) return;
setUserOptions((value) => ({
...(value || {}),
...userData,
data: [...(value?.data || []), ...userData.data]
}));
})
.finally(() => setIsLoadingMore(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/pages/sponsors/sponsor-users-list-page/components/sponsor-global-import-users-popup.js`
around lines 77 - 93, The load-more handler in
sponsor-global-import-users-popup.js can merge stale results and crash when the
current userOptions state is null. Update handleLoadMoreUsers to guard the
setUserOptions merge against a missing previous state, and ensure the response
from fetchSponsorUsersBySummit is only applied if it still matches the current
sponsor/summit context (for example by checking currentSummit.id,
selectedSummit, and companyId against the request that was issued). Keep the
protection around isLoadingMore and the pagination check, but avoid appending
userData.data to a stale or null value.data.

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