Skip to content

[CSM Portal] mobile-device banner + fix {{currentTeam}} token in section headings - #1363

Merged
rksk merged 3 commits into
wso2-open-operations:mainfrom
rksk:csm-portal-mobile-app-banner
Aug 4, 2026
Merged

[CSM Portal] mobile-device banner + fix {{currentTeam}} token in section headings#1363
rksk merged 3 commits into
wso2-open-operations:mainfrom
rksk:csm-portal-mobile-app-banner

Conversation

@rksk

@rksk rksk commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Purpose

Two small, independent CSM Portal changes bundled into one PR:

  1. The customer portal blocks phone browsers outright with a full-page "install the WSO2 Super App" prompt. CSM Portal had no equivalent, and CS engineers got no nudge that the app is available as a Super App micro-app when opening the portal on a phone.
  2. A dashboard section title using the {{currentTeam}} text token (e.g. "Overall - {{currentTeam}}") showed the literal, unresolved token instead of the selected team's name — the token was only wired into individual widgets' own displayName/description, not into section headings.

Goals

  • Add a dismissible mobile-device banner to CSM Portal, unlike the customer portal's full-page block, since internal CS engineers may need emergency mobile access.
  • Make section headings resolve {{currentTeam}} the same way individual widgets already do.

Approach

Mobile banner: ported the customer portal's device-detection utilities (detectMobileDevice/shouldPromptForMobileApp, MobileOs/DeviceType types) into CSM Portal unchanged. Added an independent config surface with CSM-prefixed keys (CSM_PORTAL_MOBILE_APP_PROMPT_ENABLED/_IOS_STORE_URL/_ANDROID_STORE_URL/_INCLUDE_TABLETS), separate from the customer portal's own CUSTOMER_PORTAL_MOBILE_APP_* keys. Built the banner directly on MUI Alert rather than this app's NotificationBanner wrapper: NotificationBanner/Alert only auto-renders its own close icon when no custom action node is supplied, so a banner needing both a "Download" action and a close control has to pack both into action itself — matching the existing pattern already established in ErrorBanner.tsx for the same reason. Wired ahead of the existing maintenance banner (GlobalNotificationBanner) in AppLayout.tsx — the device nudge is per-session/dismissible, the maintenance banner is an admin-broadcast operational notice that should keep its position.

Section-title token: resolveWidgetText (already used for widget displayName/description) is now also applied to AgentsLandingPagePilot's section heading and its refresh button's accessible label, using the same selectedTeamLabel already threaded into the component.

No screenshot included — item 1 is a small info-severity Alert banner consistent with the existing ErrorBanner/GlobalNotificationBanner visual pattern; item 2 is a one-line text-resolution fix with no visual/layout change beyond the text itself now resolving correctly.

User stories

  • As a CS engineer opening CSM Portal on a phone, I see a dismissible nudge toward the WSO2 Super App micro-app instead of no guidance at all, and can dismiss it and keep using the browser view if I need to.
  • As a CS engineer viewing a team-based dashboard with a {{currentTeam}}-templated section title, I see the actual team name (or "All ABTs") instead of the literal template token.

Release note

  • Added a dismissible banner on CSM Portal nudging phone users toward the WSO2 Super App, configurable independently of the customer portal's own mobile-app prompt.
  • Fixed dashboard section headings not resolving the {{currentTeam}} text token, so a section like "Overall - {{currentTeam}}" now correctly shows e.g. "Overall - Castor" or "Overall - All ABTs".

Documentation

N/A — both changes are internal portal UI behavior, no external doc impact.

Training

N/A — not training content.

Certification

N/A — no certification exam impact.

Marketing

N/A — internal UX addition and a bug fix, not marketable features.

Automation tests

  • Unit tests
    Mobile banner: deviceDetection.test.ts (9 tests) + MobileAppBanner.test.tsx (7 tests), all passing. Section-title fix: new case in AgentsLandingPagePilot.test.tsx asserting {{currentTeam}} resolves in a section heading and the literal token never renders; full file re-run 7/7 passing. Full csm-dashboard suite re-verified on this branch after rebasing onto the now-merged main: 141/141 passing. eslint/tsc -b/vite build all clean.
  • Integration tests
    N/A — no e2e specs cover either interaction yet.

Security checks

  • Followed secure coding standards in http://wso2.com/technical-reports/wso2-secure-engineering-guidelines? yes
  • Ran FindSecurityBugs plugin and verified report? N/A for TypeScript — ran eslint/tsc -b, both clean
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets? yes — public/config.js.example only documents the new mobile-app keys, the real public/config.js is gitignored and untouched

Samples

N/A — no sample app impact.

Related PRs

None (cs-tools#1362, which this branch was originally alongside, has since merged; this branch was rebased onto the resulting main).

Migrations (if applicable)

N/A — no data/schema migration.

Test environment

Verified locally on this branch (post-rebase onto merged main): eslint clean, pnpm run build (tsc -b && vite build) clean, vitest run on the mobile-banner tests (16/16) and the full csm-dashboard suite (141/141), all passing.

Learning

Confirmed via MUI Alert source that it only auto-renders its own close button when action is unset — documented in MobileAppBanner's own doc comment for future maintainers extending NotificationBanner-based banners with an action.

Summary by CodeRabbit

  • New Features
    • Added an optional, dismissible prompt encouraging mobile users to download the app.
    • Supports iOS and Android store links, with optional tablet targeting.
    • Invalid or unsafe store links are ignored.
  • Bug Fixes
    • Resolved team placeholders in dashboard section headings and refresh labels.
  • Tests
    • Added coverage for mobile device detection, banner behavior, dismissal, store links, security validation, and placeholder resolution.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@rksk, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 829a6e02-4c57-42d3-bcad-da419f9f5661

📥 Commits

Reviewing files that changed from the base of the PR and between d00d1bd and 2a22ef0.

📒 Files selected for processing (2)
  • apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.test.tsx
  • apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.tsx
📝 Walkthrough

Walkthrough

The portal adds configurable mobile-device detection and a dismissible app-store banner. The layout mounts the banner globally. Dashboard section titles and refresh labels now resolve the {{currentTeam}} token.

Changes

Mobile app prompt

Layer / File(s) Summary
Mobile device and prompt contracts
apps/csm-portal/webapp/src/types/mobileDevice.ts, apps/csm-portal/webapp/src/config/..., apps/csm-portal/webapp/public/config.js.example
Defines mobile device types and runtime settings for prompt enablement, store URLs, and tablet inclusion.
Mobile device detection
apps/csm-portal/webapp/src/utils/deviceDetection.ts, apps/csm-portal/webapp/src/utils/deviceDetection.test.ts
Detects supported iOS and Android phones and tablets, with tests for device classification and prompt eligibility.
Banner rendering and layout integration
apps/csm-portal/webapp/src/components/mobile-app-banner/*, apps/csm-portal/webapp/src/layouts/AppLayout.tsx
Renders a dismissible banner for eligible devices, validates store URLs, supports opening and dismissal actions, and mounts the banner in AppLayout. Tests cover visibility, dismissal, store navigation, and invalid URLs.

Dashboard section text resolution

Layer / File(s) Summary
Resolve dashboard section titles
apps/csm-portal/webapp/src/features/csm-dashboard/components/AgentsLandingPagePilot.tsx, apps/csm-portal/webapp/src/features/csm-dashboard/components/AgentsLandingPagePilot.test.tsx
Resolves {{currentTeam}} using selectedTeamLabel for section headings and refresh-button labels. The test verifies the resolved heading output.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant AppLayout
  participant MobileAppBanner
  participant Config
  participant DeviceDetection
  participant Browser
  AppLayout->>MobileAppBanner: Render
  MobileAppBanner->>Config: Read prompt settings
  MobileAppBanner->>DeviceDetection: Detect mobile device
  DeviceDetection-->>MobileAppBanner: Return device information
  MobileAppBanner->>Config: Resolve platform store URL
  MobileAppBanner-->>AppLayout: Render dismissible banner
  MobileAppBanner->>Browser: Open validated store URL
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% 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
Title check ✅ Passed The title clearly summarizes both main changes: the mobile-device banner and the {{currentTeam}} section-heading fix.
Description check ✅ Passed The description covers the template sections, implementation details, user stories, testing, security checks, and release impact.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

rksk added 2 commits August 4, 2026 22:02
…Super App

Ports the customer portal's mobile-device detection to CSM Portal, but
as a dismissible banner instead of a full-page block: CS engineers may
need emergency mobile access, unlike external customers on the
customer-facing site. Config keys are CSM-prefixed
(CSM_PORTAL_MOBILE_APP_*) and independent of the customer portal's own.

Built on Alert directly rather than NotificationBanner, since
NotificationBanner/Alert only auto-renders its own close icon when no
custom action node is supplied -- a banner needing both a "Download"
action and a close control has to pack both into one action slot,
matching the existing pattern in ErrorBanner.tsx.
…gs too

Section titles support the same {{currentTeam}} interpolation as an
individual widget's own displayName/description, but the section
heading itself was rendered raw -- a section named e.g.
"Overall - {{currentTeam}}" showed the literal token instead of the
selected team's name.
@rksk
rksk force-pushed the csm-portal-mobile-app-banner branch from 68135f3 to d00d1bd Compare August 4, 2026 16:34
@rksk rksk changed the title [CSM Portal] add a dismissible mobile-device banner nudging the WSO2 Super App [CSM Portal] mobile-device banner + fix {{currentTeam}} token in section headings Aug 4, 2026
@rksk

rksk commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@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 (3)
apps/csm-portal/webapp/src/features/csm-dashboard/components/AgentsLandingPagePilot.test.tsx (1)

253-255: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the resolved refresh label.

This test checks the heading only. It does not check the RefreshButton label changed at Lines 231-234 in AgentsLandingPagePilot.tsx. Add an assertion that the refresh control exposes Refresh Overall - Castor.

🤖 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
`@apps/csm-portal/webapp/src/features/csm-dashboard/components/AgentsLandingPagePilot.test.tsx`
around lines 253 - 255, Add an assertion in the existing AgentsLandingPagePilot
test to verify the refresh control exposes the resolved label “Refresh Overall -
Castor,” alongside the current heading and unresolved-template checks. Target
the rendered RefreshButton control rather than only asserting visible heading
text.
apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.tsx (1)

73-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the inactive dismissal-reset effect.

The result of getMobileAppConfig() is memoized at Line 57. No reactive input can change visible during this mount. The effect cannot restore a dismissed banner, but it suppresses react-hooks/set-state-in-effect. Remove the effect. If live configuration updates are required, make configuration a reactive input and use a previous-value guard during render.

Proposed cleanup
-import { useEffect, useMemo, useState, type JSX } from "react";
+import { useMemo, useState, type JSX } from "react";
@@
-  // Reset the dismissed state when the visibility configuration changes to true.
-  useEffect(() => {
-    if (visible) {
-      // eslint-disable-next-line react-hooks/set-state-in-effect -- reset dismissal when banner is re-shown
-      setDismissed(false);
-    }
-  }, [visible]);

Based on learnings: do not call setState inside useEffect; adjust derived state during render.

🤖 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 `@apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.tsx`
around lines 73 - 79, Remove the visibility-based useEffect that calls
setDismissed(false) in MobileAppBanner; keep the existing dismissal state and
memoized getMobileAppConfig flow unchanged, without suppressing the
set-state-in-effect lint rule.

Source: Learnings

apps/csm-portal/webapp/src/utils/deviceDetection.test.ts (1)

46-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the iPadOS desktop-UA fallback.

The tests use a user agent that contains "iPad". They do not execute the MacIntel and maxTouchPoints fallback. Add a user agent without "iPad" and assert null by default and DeviceType.Tablet when includeTablets is true.

Proposed test
+  it("should detect an iPadOS desktop user agent when tablets are included", () => {
+    mockNavigator({
+      userAgent:
+        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.4 Safari/604.1",
+      platform: NavigatorPlatform.MacIntel,
+      maxTouchPoints: 5,
+    });
+
+    expect(detectMobileDevice()).toBeNull();
+    expect(detectMobileDevice({ includeTablets: true })).toEqual({
+      os: MobileOs.Ios,
+      deviceType: DeviceType.Tablet,
+    });
+  });
🤖 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 `@apps/csm-portal/webapp/src/utils/deviceDetection.test.ts` around lines 46 -
69, Extend the iPad coverage in the tests around detectMobileDevice with a
desktop-style iPadOS user agent that omits “iPad” while retaining MacIntel and
maxTouchPoints. Assert it returns null by default and returns an iOS Tablet
result when includeTablets is true, matching the existing cases.
🤖 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 `@apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.tsx`:
- Around line 64-68: Validate the URL returned by getMobileAppStoreUrl before
computing visible in MobileAppBanner, suppressing the banner for invalid URLs
and passing only the validated URL to window.open in handleDownload. In
apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.tsx
lines 64-68, update the store URL flow accordingly; in
apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.test.tsx
lines 132-143, add an invalid-URL case that asserts the banner is suppressed and
retain the no-window.open assertion.

---

Nitpick comments:
In `@apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.tsx`:
- Around line 73-79: Remove the visibility-based useEffect that calls
setDismissed(false) in MobileAppBanner; keep the existing dismissal state and
memoized getMobileAppConfig flow unchanged, without suppressing the
set-state-in-effect lint rule.

In
`@apps/csm-portal/webapp/src/features/csm-dashboard/components/AgentsLandingPagePilot.test.tsx`:
- Around line 253-255: Add an assertion in the existing AgentsLandingPagePilot
test to verify the refresh control exposes the resolved label “Refresh Overall -
Castor,” alongside the current heading and unresolved-template checks. Target
the rendered RefreshButton control rather than only asserting visible heading
text.

In `@apps/csm-portal/webapp/src/utils/deviceDetection.test.ts`:
- Around line 46-69: Extend the iPad coverage in the tests around
detectMobileDevice with a desktop-style iPadOS user agent that omits “iPad”
while retaining MacIntel and maxTouchPoints. Assert it returns null by default
and returns an iOS Tablet result when includeTablets is true, matching the
existing cases.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 88aa0c1e-0183-4020-9626-0d8171bfb3be

📥 Commits

Reviewing files that changed from the base of the PR and between ab45139 and d00d1bd.

📒 Files selected for processing (11)
  • apps/csm-portal/webapp/public/config.js.example
  • apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.test.tsx
  • apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.tsx
  • apps/csm-portal/webapp/src/config/authConfig.ts
  • apps/csm-portal/webapp/src/config/mobileAppConfig.ts
  • apps/csm-portal/webapp/src/features/csm-dashboard/components/AgentsLandingPagePilot.test.tsx
  • apps/csm-portal/webapp/src/features/csm-dashboard/components/AgentsLandingPagePilot.tsx
  • apps/csm-portal/webapp/src/layouts/AppLayout.tsx
  • apps/csm-portal/webapp/src/types/mobileDevice.ts
  • apps/csm-portal/webapp/src/utils/deviceDetection.test.ts
  • apps/csm-portal/webapp/src/utils/deviceDetection.ts

Comment thread apps/csm-portal/webapp/src/components/mobile-app-banner/MobileAppBanner.tsx Outdated
visible only checked storeUrl's truthiness, so a misconfigured store
URL (unsupported scheme, or a string that fails to parse) rendered a
Download button that silently did nothing on click. Both the
visibility check and window.open now use the same validated URL.
@rksk
rksk merged commit 37b19cc into wso2-open-operations:main Aug 4, 2026
1 check passed
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.

2 participants