Skip to content

Update cohort management module to use info banners#3546

Merged
Baalmart merged 10 commits into
airqo-platform:stagingfrom
BwanikaRobert:Update-Cohort-Management-Module-to-use-InfoBanners
May 28, 2026
Merged

Update cohort management module to use info banners#3546
Baalmart merged 10 commits into
airqo-platform:stagingfrom
BwanikaRobert:Update-Cohort-Management-Module-to-use-InfoBanners

Conversation

@BwanikaRobert

@BwanikaRobert BwanikaRobert commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary of Changes

Migrates the Cohort Management module from ReusableToast to the centralized InfoBanner system (useBanner hook). All user feedback is now surfaced at the component layer rather than inside hooks, following the architecture established by the Grid Management module refactor.

Hook changes (useCohorts.ts)

  • Removed all ReusableToast calls from the hook layer
  • Added optional onSuccess/onError callback options to all mutation hooks: useCreateCohort, useCreateCohortWithDevices, useCreateCohortFromCohorts, useAssignDevicesToCohort, useUnassignDevicesFromCohort, useAssignCohortsToGroup, useAssignCohortsToUser, useUpdateCohortDetails, useUpdateCohortName

Component changes

  • cohort-detail-card.tsx — replaced toasts with scoped/global banners; switched to mutateAsync for visibility and tags updates
  • cohort-measurements-api-card.tsx — replaced toast with useBanner for clipboard feedback
  • create-cohort.tsx — replaced toasts with banners; consolidated onSuccess/onError to hook-level (removed unreliable per-call callbacks)
  • device-name-parser.tsx — replaced all toast calls with scoped banners
  • edit-cohort-details-modal.tsx — replaced toasts with banners; added setTimeout success banner pattern after dialog close
  • assign-cohort-devices.tsx / unassign-cohort-devices.tsx — replaced toasts with useDeferredBanner for post-close success banners

Shared utilities used from Grid Management PR

  • useDeferredBanner hook — handles timer ref, cleanup on unmount, and AFTER_DIALOG_CLOSE_MS delay
  • getApiErrorMessage — now guards against empty/whitespace and raw HTML error responses

Status of maturity

  • I've tested this locally
  • I consider this code done
  • This change ready to hit production in its current state
  • The title of the PR states what changed and the related issues number
  • I've included the issue number in the "Closes [Vertex: Migration] Update Cohort Management Module to use InfoBanners #3492" section
  • I've updated corresponding documentation for the changes in this PR
  • I have written unit and/or e2e tests for my change(s)

How should this be manually tested?

  1. Navigate to the Cohorts section
  2. Create a cohort — confirm success banner appears globally after the dialog closes; confirm error banner appears inside the dialog on failure
  3. Edit cohort details (name, visibility, tags) — confirm success banner appears after dialog closes; confirm error banner appears inside dialog on failure
  4. Assign devices to a cohort — confirm success banner appears after dialog closes
  5. Remove devices from a cohort — confirm success banner appears after dialog closes
  6. Copy cohort ID and API URLs — confirm success/error banners appear correctly
  7. Navigate away immediately after a successful action — confirm no stale banner appears on the next page

What are the relevant tickets?

Screenshots

coh1 coh2 coh3 coh4 coh5 coh6 coh7 coh8 coh9 coh10 coh11

Summary by CodeRabbit

  • Refactor

    • Cohort/grid/device flows migrated to a centralized banner notification system for consistent inline feedback.
    • Success/error messages now use banners (scoped for in-dialog errors; unscoped and delayed after dialog close for post-dialog success).
    • Clipboard copy and import flows show banner feedback instead of toasts.
    • Mutation hooks now support caller-provided success/error callbacks to coordinate UI updates.
  • Chore

    • Added release notes entry documenting the migration.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Migrate toast/setTimeout notifications to InfoBanner: add useBanner and useBannerWithDelay usages; refactor cohort mutation hooks to accept optional onSuccess/onError callbacks and forward AxiosError; update cohort, grid, and device components to show scoped dialog errors and deferred global success banners.

Changes

Cohort Management Banner Migration

Layer / File(s) Summary
Hook callback contract refactoring
src/vertex/core/hooks/useCohorts.ts
Nine cohort mutation hooks now accept typed options with optional onSuccess and onError callbacks; hooks invalidate relevant queries and forward AxiosError to error callbacks.
Create Cohort dialog and device import flow
src/vertex/components/features/cohorts/create-cohort.tsx, src/vertex/components/features/cohorts/device-name-parser.tsx
CreateCohortDialog and DeviceNameParser replace toast calls with useBanner scoped banners for file validation, parsing errors, import warnings, and cohort-creation feedback; create-with-devices hook is wired with callbacks.
Device assignment and unassignment operations
src/vertex/components/features/cohorts/assign-cohort-devices.tsx, src/vertex/components/features/cohorts/unassign-cohort-devices.tsx
Assignment/unassignment dialogs supply onSuccess/onError to hooks; success triggers deferred global banners reporting counts, errors trigger scoped dialog banners using getApiErrorMessage.
Cohort detail updates and copy actions
src/vertex/components/features/cohorts/cohort-detail-card.tsx, src/vertex/components/features/cohorts/cohort-measurements-api-card.tsx, src/vertex/components/features/cohorts/edit-cohort-details-modal.tsx
Components replace ReusableToast with useBanner/useBannerWithDelay and getApiErrorMessage. Mutations use async/try-catch and show scoped errors or global/deferred successes; clipboard copy uses banner success/error messages.
Banner delay hook & grids
src/vertex/core/hooks/useBannerWithDelay.ts, src/vertex/components/features/grids/*
Renamed useDeferredBanneruseBannerWithDelay and updated grid admin/create/edit components to call showBannerWithDelay for deferred success banners.
Device components deferred success
src/vertex/components/features/devices/*
Device modals and flows now call showBannerWithDelay instead of ad-hoc setTimeout wrapping showBanner to standardize deferred success display timing.
Release notes documentation
src/vertex/app/changelog.md
Adds Version 1.23.52 changelog entry documenting the Cohort Management Banner Migration and enumerating updated files.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant Dialog as Dialog/Component
  participant Hook as useCohorts Hook
  participant API as Cohorts API
  participant Banner as InfoBanner

  User->>Dialog: trigger action (create/assign/update)
  Dialog->>Hook: call mutation with { onSuccess, onError }
  Hook->>API: execute request
  alt Success
    API-->>Hook: success response
    Hook->>Dialog: invoke onSuccess(variables/data)
    Dialog->>Banner: showBannerWithDelay({ scoped:false, message })
  else Error
    API-->>Hook: AxiosError
    Hook->>Dialog: invoke onError(error)
    Dialog->>Banner: showBanner({ scoped:true, message: getApiErrorMessage(error) })
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • Codebmk
  • Baalmart

"Toasts traded for banners, calm and bright,
Dialogs now hold errors within their light.
Hooks hand back Axios' terse cry,
Deferred success waits till the dialog says goodbye.
Cohorts sing clearer through the site tonight."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main objective: migrating the cohort management module from ReusableToast to InfoBanner system for user feedback.
Linked Issues check ✅ Passed The PR comprehensively addresses all coding requirements from issue #3492: removed ReusableToast calls from mutation hooks, added optional onSuccess/onError callbacks, replaced toast notifications with useBanner across all specified components, and implemented proper scoping for modal vs. global banners.
Out of Scope Changes check ✅ Passed All code changes are directly aligned with the InfoBanner migration objective. The component updates and hook refactorings are necessary to fulfill the requirements; the device components' useBannerWithDelay updates represent a parallel standardization of delayed-banner patterns mentioned in the changelog.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

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.

@BwanikaRobert

Copy link
Copy Markdown
Contributor Author

@copilot review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/vertex/components/features/cohorts/edit-cohort-details-modal.tsx (1)

128-134: ⚡ Quick win

Use useDeferredBanner instead of a raw timeout for post-close success messaging.

This keeps deferred banner behavior consistent across dialogs and provides timer lifecycle cleanup.

🤖 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/vertex/components/features/cohorts/edit-cohort-details-modal.tsx` around
lines 128 - 134, Replace the ad-hoc setTimeout call that invokes showBanner in
edit-cohort-details-modal.tsx with the standardized useDeferredBanner hook:
import and call useDeferredBanner in the component, get the deferred banner
function (e.g., deferredShow or showDeferred), and invoke that with the same
payload ('severity: success', 'Cohort details updated successfully', scoped:
false) instead of using setTimeout; remove the setTimeout and ensure the
deferred hook is called after the modal closes (where setTimeout currently sits)
so the hook manages the timer lifecycle and cleanup for you.
src/vertex/components/features/cohorts/cohort-detail-card.tsx (1)

63-69: ⚡ Quick win

Replace inline success setTimeout with the shared deferred banner hook.

Using raw timers here duplicates logic and skips cleanup; adopting useDeferredBanner keeps behavior consistent with assign/unassign dialogs and avoids stale delayed banners.

Also applies to: 83-89

🤖 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/vertex/components/features/cohorts/cohort-detail-card.tsx` around lines
63 - 69, The inline setTimeouts in cohort-detail-card.tsx that call showBanner
(the blocks at lines around the setTimeout using message `Cohort is now
${targetVisibility ? 'public' : 'private'}` and the similar block at 83-89)
should be replaced with the shared useDeferredBanner hook; remove the raw
setTimeout calls, import and call useDeferredBanner from the shared hooks, and
use its API to schedule the same success banner (passing the same payload:
severity:'success', message:`Cohort is now ${...}`, scoped:false). Locate the
setTimeout usages and replace them with the hook’s deferred show invocation
(e.g., const { deferBanner } = useDeferredBanner(); deferBanner(() =>
showBanner({...})) or the hook’s documented showDeferredBanner({ ... })) so
cleanup and consistent behavior match the assign/unassign dialogs.
🤖 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/vertex/core/hooks/useCohorts.ts`:
- Around line 314-317: The invalidateQueries calls use a different key shape
than useCohorts (they pass ['cohorts', activeNetwork?.net_name] while useCohorts
registers keys as ['cohorts', { ... }]), so update the three invalidation sites
(the queryClient.invalidateQueries calls at the blocks around lines 314-317,
424-427, and 466-468) to use the same second-element object shape as useCohorts;
e.g., replace ['cohorts', activeNetwork?.net_name] with ['cohorts',
activeNetwork] or ['cohorts', { net_name: activeNetwork?.net_name }] (matching
the exact object used by useCohorts) so the cohort summary queries are properly
invalidated.

---

Nitpick comments:
In `@src/vertex/components/features/cohorts/cohort-detail-card.tsx`:
- Around line 63-69: The inline setTimeouts in cohort-detail-card.tsx that call
showBanner (the blocks at lines around the setTimeout using message `Cohort is
now ${targetVisibility ? 'public' : 'private'}` and the similar block at 83-89)
should be replaced with the shared useDeferredBanner hook; remove the raw
setTimeout calls, import and call useDeferredBanner from the shared hooks, and
use its API to schedule the same success banner (passing the same payload:
severity:'success', message:`Cohort is now ${...}`, scoped:false). Locate the
setTimeout usages and replace them with the hook’s deferred show invocation
(e.g., const { deferBanner } = useDeferredBanner(); deferBanner(() =>
showBanner({...})) or the hook’s documented showDeferredBanner({ ... })) so
cleanup and consistent behavior match the assign/unassign dialogs.

In `@src/vertex/components/features/cohorts/edit-cohort-details-modal.tsx`:
- Around line 128-134: Replace the ad-hoc setTimeout call that invokes
showBanner in edit-cohort-details-modal.tsx with the standardized
useDeferredBanner hook: import and call useDeferredBanner in the component, get
the deferred banner function (e.g., deferredShow or showDeferred), and invoke
that with the same payload ('severity: success', 'Cohort details updated
successfully', scoped: false) instead of using setTimeout; remove the setTimeout
and ensure the deferred hook is called after the modal closes (where setTimeout
currently sits) so the hook manages the timer lifecycle and cleanup for you.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 23542b5d-805b-43e9-8b50-d5a83ae7dee1

📥 Commits

Reviewing files that changed from the base of the PR and between 53eccb1 and 2a25994.

📒 Files selected for processing (9)
  • src/vertex/app/changelog.md
  • src/vertex/components/features/cohorts/assign-cohort-devices.tsx
  • src/vertex/components/features/cohorts/cohort-detail-card.tsx
  • src/vertex/components/features/cohorts/cohort-measurements-api-card.tsx
  • src/vertex/components/features/cohorts/create-cohort.tsx
  • src/vertex/components/features/cohorts/device-name-parser.tsx
  • src/vertex/components/features/cohorts/edit-cohort-details-modal.tsx
  • src/vertex/components/features/cohorts/unassign-cohort-devices.tsx
  • src/vertex/core/hooks/useCohorts.ts

Comment thread src/vertex/core/hooks/useCohorts.ts Outdated
message: `${variables.deviceIds.length} device(s) assigned to cohort successfully`,
scoped: false,
});
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I should have asked this in the previous PR. What prompted you to create showDeferredBanner yet showBanner covered both global and "local" ie within the dialog, banner instances? @BwanikaRobert

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The fix is a 100 ms delay wait for the dialog to fully unmount, then show the banner. Without that delay, the success message silently disappears on every action that closes a dialog.

useDeferredBanner just encapsulates that pattern the useRef to hold the timer, the useEffect to cancel it on unmount, and the 100 ms delay so we don't copy and paste the same bannerTimerRef + setTimeout + cleanup block into every component that needs post-dialog feedback. showBanner alone can't express this.

It was one of the changes @copilot requested in the prevoius PR @Codebmk

@Codebmk Codebmk May 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Okay, that was a great change! Let's rename it to showBannerWithDelay and useBannerWithDelay where it is much easier to remember what they are for

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Kindly replace all parts in the codebase that use the new banner but apply the timer approach manually, they are few. @BwanikaRobert

@Codebmk

Codebmk commented May 28, 2026

Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Codebmk Codebmk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/vertex/components/features/devices/device-assignment-modal.tsx (1)

63-67: ⚡ Quick win

Make delayed success banner scope explicit.

At Line 63, add scoped: false explicitly so this post-close success message is guaranteed to render globally and stays aligned with the rest of this migration.

Suggested patch
       showBannerWithDelay({
         severity: 'success',
         title: 'Success',
         message: `${data.device.name} has been assigned successfully.`,
+        scoped: false,
       }, 300);
🤖 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/vertex/components/features/devices/device-assignment-modal.tsx` around
lines 63 - 67, The success banner shown after device assignment uses
showBannerWithDelay but doesn't set the banner scope; update the call to
showBannerWithDelay (in device-assignment-modal component where the success
message is constructed) to include scoped: false in the options object so the
post-close banner renders globally (e.g., add scoped: false alongside severity,
title, message, and delay).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/vertex/components/features/devices/device-assignment-modal.tsx`:
- Around line 63-67: The success banner shown after device assignment uses
showBannerWithDelay but doesn't set the banner scope; update the call to
showBannerWithDelay (in device-assignment-modal component where the success
message is constructed) to include scoped: false in the options object so the
post-close banner renders globally (e.g., add scoped: false alongside severity,
title, message, and delay).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9e839ebf-ac03-4518-ac81-c644e84464d0

📥 Commits

Reviewing files that changed from the base of the PR and between 799327c and 97e1744.

📒 Files selected for processing (8)
  • src/vertex/app/changelog.md
  • src/vertex/components/features/devices/add-maintenance-log-modal.tsx
  • src/vertex/components/features/devices/create-device-modal.tsx
  • src/vertex/components/features/devices/deploy-device-component.tsx
  • src/vertex/components/features/devices/device-assignment-modal.tsx
  • src/vertex/components/features/devices/import-device-modal.tsx
  • src/vertex/components/features/devices/recall-device-dialog.tsx
  • src/vertex/core/hooks/useBannerWithDelay.ts
✅ Files skipped from review due to trivial changes (1)
  • src/vertex/app/changelog.md

@Baalmart
Baalmart merged commit b599537 into airqo-platform:staging May 28, 2026
17 of 18 checks passed
@Baalmart Baalmart mentioned this pull request May 28, 2026
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Vertex: Migration] Update Cohort Management Module to use InfoBanners

3 participants