Vertex:(Migration)Update Network Management Module to use InfoBanners#3558
Conversation
|
Warning Review limit reached
More reviews will be available in 18 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughNetwork-management components switch notification delivery from ReusableToast to the centralized banner system via useBanner/useBannerWithDelay; admin actions emit global banners, table copy actions emit global banners, dialog forms emit scoped/delayed banners, and banner text is left-aligned with a changelog entry added. ChangesNetwork Management Toast-to-Banner Migration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/app/`(authenticated)/admin/networks/requests/NetworkRequestsClient.tsx:
- Around line 79-84: The error path currently calls showBanner({... scoped:
false ...}) while the confirmation dialog (selectedRequest) remains open so the
global banner is visually hidden; change the error handling in the catch block
of the function that manipulates selectedRequest to show a scoped/in-dialog
banner instead: render the banner via the dialog's BannerSlot (or set scoped:
true) so the error message is visible inside the modal, or alternatively clear
selectedRequest before calling showBanner so the global banner is visible;
update the catch to either call showBanner with scoped: true or trigger the
dialog's BannerSlot component to display getApiErrorMessage(error) while keeping
selectedRequest unchanged.
In `@src/vertex/components/features/networks/network-request-dialog.tsx`:
- Around line 43-49: The success banner called via showBannerWithDelay in the
onSuccess handler should use scoped:false (not scoped:true) because it fires
after handleClose and must go through the global path; update the
showBannerWithDelay call accordingly, and for the onError handler either mount a
<BannerSlot /> inside this dialog (e.g., under the header in
network-request-dialog) if you want scoped errors, or change showBanner({...,
scoped: true}) to scoped:false so errors are visible via
GlobalBannerContainer—pick one approach and make both handlers consistent with
that decision (reference showBannerWithDelay, showBanner, BannerSlot,
handleClose, and GlobalBannerContainer).
🪄 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: dd8e13cd-c77f-4db2-bbcd-5c43372da51e
📒 Files selected for processing (4)
src/vertex/app/(authenticated)/admin/networks/requests/NetworkRequestsClient.tsxsrc/vertex/components/features/networks/client-paginated-networks-table.tsxsrc/vertex/components/features/networks/create-network-form.tsxsrc/vertex/components/features/networks/network-request-dialog.tsx
| onSuccess: (resp) => { | ||
| queryClient.invalidateQueries({ queryKey: ["network-requests"] }); | ||
| showBannerWithDelay({severity:'success',message:resp.message || "Your request for a new Sensor Manufacturer has been submitted successfully!",scoped:true}); | ||
| }, | ||
| onError: (error) => { | ||
| ReusableToast({ message: getApiErrorMessage(error), type: "ERROR" }); | ||
| onError: (error) => { | ||
| showBanner({severity:'error',message:getApiErrorMessage(error),scoped:true}) | ||
| }, |
There was a problem hiding this comment.
Both banners are currently unreachable as wired.
Two separate problems here, mirroring (and partly contradicting) create-network-form.tsx:
-
Delayed success (line 45) uses
scoped: true. The success banner fires after the dialog closes (handleCloseon line 90), at which point any in-dialogBannerSlotis unmounted — andGlobalBannerContainerignores scoped banners. So it never shows. This should bescoped: false(the after-close global path), matchingcreate-network-form.tsx. -
Error (line 48) uses
scoped: truebut there's no<BannerSlot />mounted in this dialog, so submission errors are invisible. Either mount aBannerSlotbelow the header for the error case, or fall back toscoped: false.
🛠️ Suggested fix
- const {showBanner}=useBanner();
+ const { showBanner, BannerSlot } = useBanner();
@@
- onSuccess: (resp) => {
- queryClient.invalidateQueries({ queryKey: ["network-requests"] });
- showBannerWithDelay({severity:'success',message:resp.message || "Your request for a new Sensor Manufacturer has been submitted successfully!",scoped:true});
- },
- onError: (error) => {
- showBanner({severity:'error',message:getApiErrorMessage(error),scoped:true})
- },
+ onSuccess: (resp) => {
+ queryClient.invalidateQueries({ queryKey: ["network-requests"] });
+ showBannerWithDelay({ severity: 'success', message: resp.message || "Your request for a new Sensor Manufacturer has been submitted successfully!", scoped: false });
+ },
+ onError: (error) => {
+ showBanner({ severity: 'error', message: getApiErrorMessage(error), scoped: true });
+ },And mount the slot inside the form for the scoped error:
<Form {...form}>
+ <BannerSlot />
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">📝 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.
| onSuccess: (resp) => { | |
| queryClient.invalidateQueries({ queryKey: ["network-requests"] }); | |
| showBannerWithDelay({severity:'success',message:resp.message || "Your request for a new Sensor Manufacturer has been submitted successfully!",scoped:true}); | |
| }, | |
| onError: (error) => { | |
| ReusableToast({ message: getApiErrorMessage(error), type: "ERROR" }); | |
| onError: (error) => { | |
| showBanner({severity:'error',message:getApiErrorMessage(error),scoped:true}) | |
| }, | |
| onSuccess: (resp) => { | |
| queryClient.invalidateQueries({ queryKey: ["network-requests"] }); | |
| showBannerWithDelay({ severity: 'success', message: resp.message || "Your request for a new Sensor Manufacturer has been submitted successfully!", scoped: false }); | |
| }, | |
| onError: (error) => { | |
| showBanner({ severity: 'error', message: getApiErrorMessage(error), scoped: true }); | |
| }, |
🤖 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/networks/network-request-dialog.tsx` around
lines 43 - 49, The success banner called via showBannerWithDelay in the
onSuccess handler should use scoped:false (not scoped:true) because it fires
after handleClose and must go through the global path; update the
showBannerWithDelay call accordingly, and for the onError handler either mount a
<BannerSlot /> inside this dialog (e.g., under the header in
network-request-dialog) if you want scoped errors, or change showBanner({...,
scoped: true}) to scoped:false so errors are visible via
GlobalBannerContainer—pick one approach and make both handlers consistent with
that decision (reference showBannerWithDelay, showBanner, BannerSlot,
handleClose, and GlobalBannerContainer).
There was a problem hiding this comment.
The success banner has to be scoped true because here we have two modals open the banner displays in the second open modal setting it to false the banner will display in the global scope yet we still have an open modal
There was a problem hiding this comment.
The PR description images show three scenarios: (1) net1 — "Create A New Sensor Manufacturer" modal with a scoped error banner rendered inside it; (2) net2 — "Request New Sensor Manufacturer" modal with a scoped error banner ("Network request timed out") visible inside the modal; (3) net3 — "Import External Device" modal displaying a green success banner ("sensor manufacturer creation request submitted successfully") after the Request dialog has already closed.
@BwanikaRobert, that context clears it up — net3 confirms it perfectly. The NetworkRequestDialog is opened from within the Import External Device modal, which has its own BannerSlot still mounted. After the child dialog closes, showBannerWithDelay with scoped: true correctly targets that parent modal's slot, so the success message surfaces inside the still-open workflow rather than leaking to the global scope. That's actually a cleaner UX than scoped: false would produce here.
Similarly, the error banner in net2 appearing inside the dialog makes sense for the same reason — the parent modal's BannerSlot is the active scoped receiver.
My original concern was based on a single-modal assumption; the stacked-modal context makes scoped: true the right call. Thanks for walking through it — withdrawing that suggestion.
(◕‿◕)🐇
✏️ Learnings added
Learnt from: BwanikaRobert
Repo: airqo-platform/AirQo-frontend PR: 3558
File: src/vertex/components/features/networks/network-request-dialog.tsx:43-49
Timestamp: 2026-05-29T16:31:51.091Z
Learning: In `src/vertex/components/features/networks/network-request-dialog.tsx`, `NetworkRequestDialog` is opened from within a parent modal (e.g., "Import External Device"). Because the parent modal has its own `BannerSlot` that stays mounted after the child dialog closes, both success and error banners are intentionally set to `scoped: true`. The success banner uses `showBannerWithDelay({ scoped: true })` so it appears in the parent modal's BannerSlot after the child closes; the error banner uses `showBanner({ scoped: true })` to render inside the active dialog context. Using `scoped: false` would send banners to the global container, which is incorrect when a parent modal is still visible.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: Codebmk
Repo: airqo-platform/AirQo-frontend PR: 3092
File: src/vertex/components/features/networks/network-detail-card.tsx:34-36
Timestamp: 2025-12-12T05:22:05.515Z
Learning: In the networks feature, when deriving display names for network entities, do not append ' Network' if the name already includes 'Network'. This prevents duplication like 'XYZ Network Network'. Apply this check in network-detail-card.tsx and similar components under this folder; add a conditional formatting guard and ensure tests cover both names containing and not containing 'Network'.
Learnt from: Codebmk
Repo: airqo-platform/AirQo-frontend PR: 3092
File: src/vertex/components/features/networks/network-detail-card.tsx:34-36
Timestamp: 2025-12-12T05:22:05.515Z
Learning: In the Vertex project, ensure network names shown in NetworkDetailsCard are styled with the Tailwind 'uppercase' class to render uppercase text. Apply the 'uppercase' class to the element that displays the network name in src/vertex/components/features/networks/network-detail-card.tsx, ensuring the change covers all similar render paths in that component. Avoid duplicating styles, preserve accessibility attributes, and verify no regressions in theming by testing in both light/dark modes.
Codebmk
left a comment
There was a problem hiding this comment.
Kindly investigate why some banners have centered text. The text is expected to be left-aligned. Identify root cause and fix this @BwanikaRobert
There was a problem hiding this comment.
Pull request overview
Migrates user feedback in the Vertex Network Management (Sensor Manufacturers) module from the legacy ReusableToast to the new banner system (useBanner + useBannerWithDelay). Scoped banners are used inside dialogs (rendered through ReusableDialog's built-in BannerSlot), and global/unscoped banners are used for in-page actions (approve/deny/review, copy-to-clipboard, post-close success).
Changes:
- Replace toast calls with banner calls across
NetworkRequestsClient,client-paginated-networks-table,create-network-form, andnetwork-request-dialog. - Use
showBannerWithDelayfor success messages that follow a dialog close (so the dialog's banner-clearing effect doesn't swallow them). - Add changelog entry for version
1.23.56.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/vertex/app/(authenticated)/admin/networks/requests/NetworkRequestsClient.tsx |
Approve/deny/review success and error now dispatched as global banners (scoped: false). |
src/vertex/components/features/networks/client-paginated-networks-table.tsx |
Copy-ID success/error switched from toasts to global banners. |
src/vertex/components/features/networks/create-network-form.tsx |
Success uses delayed global banner after handleClose(); errors use scoped in-dialog banner. |
src/vertex/components/features/networks/network-request-dialog.tsx |
Success uses showBannerWithDelay, errors use scoped showBanner. Note: success uses scoped: true despite dialog being closed first — see inline comment. |
src/vertex/app/changelog.md |
Adds 1.23.56 changelog entry describing the migration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| }); | ||
| onSuccess: (resp) => { | ||
| queryClient.invalidateQueries({ queryKey: ["network-requests"] }); | ||
| showBannerWithDelay({severity:'success',message:resp.message || "Your request for a new Sensor Manufacturer has been submitted successfully!",scoped:true}); |
There was a problem hiding this comment.
The success banner has to be scoped true because here we have two modals open the banner displays in the second open modal setting it to false the banner will display in the global scope yet we still have an open modal
| export function NetworkRequestDialog({ open,onOpenChange }: NetworkRequestDialogProps) { | ||
| const userDetails = useAppSelector((state) => state.user.userDetails); | ||
| const queryClient = useQueryClient(); | ||
| const {showBanner}=useBanner(); | ||
| const {showBannerWithDelay}=useBannerWithDelay(); |
| const {showBanner}=useBanner(); | ||
| const {showBannerWithDelay}=useBannerWithDelay(); |
| showBanner({ message: "Failed to copy ID", severity: "error", scoped:false }); | ||
|
|
||
| } |
| } | ||
|
|
||
| export function NetworkRequestDialog({ open, onOpenChange }: NetworkRequestDialogProps) { | ||
| export function NetworkRequestDialog({ open,onOpenChange }: NetworkRequestDialogProps) { |
| const {showBanner}=useBanner(); | ||
| const {showBannerWithDelay}=useBannerWithDelay(); |
| onError: (error) => { | ||
| showBanner({severity:'error',message:getApiErrorMessage(error),scoped:true}) | ||
| }, |
| } catch { | ||
| ReusableToast({ message: "Failed to copy ID", type: "ERROR" }); | ||
| showBanner({ message: "Failed to copy ID", severity: "error", scoped:false }); | ||
|
|
||
| } |
| const errorMessage = getApiErrorMessage(error); | ||
| showBanner({severity:'error',message:errorMessage,scoped:true}) |
There was a problem hiding this comment.
The banner only closes after a successful request after closing it we display the message using showBannerWithDelay in the global scope.
| ## Version 1.23.56 | ||
| **Released:** May 29, 2026 |
| const errorMessage = getApiErrorMessage(error); | ||
| showBanner({severity:'error',message:errorMessage,scoped:true}) |
| onSuccess: (resp) => { | ||
| queryClient.invalidateQueries({ queryKey: ["network-requests"] }); | ||
| showBannerWithDelay({severity:'success',message:resp.message || "Your request for a new Sensor Manufacturer has been submitted successfully!",scoped:true}); |
| Migrated user feedback in the network management feature from toast notifications to the banner system. This standardizes notifications across the networks admin area for a consistent feedback experience. | ||
|
|
||
| <details> | ||
| <summary><strong>Changes (5)</strong></summary> |
| } | ||
| }, | ||
| [handleClose, queryClient] | ||
| [handleClose,queryClient,showBanner,showBannerWithDelay] |
| const errorMessage = getApiErrorMessage(error); | ||
| showBanner({ severity: 'error', message: errorMessage, scoped: true }); |
There was a problem hiding this comment.
The Bannerslot is form ReusableDialog and the dialog only closes after success.
| const [activeTab, setActiveTab] = useState<TabStatus>("pending"); | ||
| const [isUpdating, setIsUpdating] = useState(false); | ||
|
|
||
| const {showBanner} = useBanner(); |
| showBanner({ message: "Sensor Manufacturer ID copied", severity:"success",scoped: false }); | ||
| } catch { | ||
| ReusableToast({ message: "Failed to copy ID", type: "ERROR" }); | ||
| showBanner({ message: "Failed to copy ID", severity: "error", scoped:false }); |
| message: resp.message || "Your request for a new Sensor Manufacturer has been submitted successfully!", | ||
| type: "SUCCESS", | ||
| }); | ||
| onSuccess: (resp) => { |
| }, | ||
| onError: (error) => { | ||
| ReusableToast({ message: getApiErrorMessage(error), type: "ERROR" }); | ||
| onError: (error) => { |
| }); | ||
| onSuccess: (resp) => { | ||
| queryClient.invalidateQueries({ queryKey: ["network-requests"] }); | ||
| showBannerWithDelay({ severity: 'success', message: resp.message || "Your request for a new Sensor Manufacturer has been submitted successfully!", scoped: true }); |
There was a problem hiding this comment.
This was already explained
| showBannerWithDelay({ severity: 'success', message: 'Sensor Manufacturer created successfully!', scoped: false }); | ||
| } catch (error: unknown) { | ||
| const errorMessage = getApiErrorMessage(error); | ||
| ReusableToast({ message: errorMessage, type: 'ERROR' }); | ||
| const errorMessage = getApiErrorMessage(error); | ||
| showBanner({ severity: 'error', message: errorMessage, scoped: true }); |
There was a problem hiding this comment.
@copilot ReusableDialog already includes internally no changes needed.
| message: resp.message || "Your request for a new Sensor Manufacturer has been submitted successfully!", | ||
| type: "SUCCESS", | ||
| }); | ||
| onSuccess: (resp) => { |
| scoped: false | ||
| }); |
| Migrated user feedback in the network management feature from toast notifications to the banner system. This standardizes notifications across the networks admin area for a consistent feedback experience. | ||
|
|
||
| <details> | ||
| <summary><strong>Changes (5)</strong></summary> |

Summary of Changes (What does this PR do?)
ReusableToast) to the banner-based system (useBannercontext and theuseBannerWithDelayhook).NetworkRequestsClient.tsx– approve / deny / review status updates now show success and error banners (scoped: false).client-paginated-networks-table.tsx– the "copy Sensor Manufacturer ID" action now shows success/error banners (scoped: false).create-network-form.tsx– success path usesshowBannerWithDelay(so the banner appears after the dialog closes,scoped: false); error path usesshowBanner(scoped: true).network-request-dialog.tsx–onSuccessusesshowBannerWithDelay(scoped: true);onErrorusesshowBanner(scoped: true).Status of maturity (all need to be checked before merging):
How should this be manually tested?
ReusableToastnotifications appear anywhere in these flows.What are the relevant tickets?
Screenshots
Summary by CodeRabbit