Skip to content

[CSM Portal Microapp] Support page: case list, case detail, and app shell - #1092

Merged
cloby99 merged 10 commits into
wso2-open-operations:dev-app-csm-portalfrom
2003dinijay:support
Jul 8, 2026
Merged

cloby99 merged 10 commits into
wso2-open-operations:dev-app-csm-portalfrom
2003dinijay:support

Conversation

@2003dinijay

@2003dinijay 2003dinijay commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Wires up the app shell (routing, layout, tab bar, theme, auth/API client) needed to bootstrap the microapp.
  • Implements the Support page: case list with type tabs (Cases/Service Requests/Security Reports/Engagements/Announcements) and a case detail page (summary, metadata, comments).
  • Fixes case cards showing the wrong/stale "Updated X ago" time — the backend's ServiceNow-sourced timestamps are zone-less and sometimes omit updatedOn entirely; timestamps are now normalized to UTC with a createdOn fallback.

Test plan

  • npm run lint / tsc --noEmit clean
  • Verified in a headless browser against a mocked backend: case list renders, tabs switch, case detail loads comments, and the timestamp fix renders correct relative times (including the missing-updatedOn fallback case)

Summary by CodeRabbit

  • New Features
    • Added Support and Case Detail screens with routing, Suspense loading, and retry-friendly error handling.
    • Introduced a fixed bottom tab bar, plus reusable case UI (cards, status/severity chips, empty/error states) and a shared app theme.
    • Implemented native bridge integration, token/auth flow, and API/case services for fetching and comments.
  • Bug Fixes
    • Improved timestamp parsing/formatting and added safe-area layout sizing for mobile.
    • Enhanced request handling with automatic access-token refresh on authorization failures.
  • Chores
    • Upgraded UI dependencies and removed obsolete script allowance.

Wires up the support case list and detail views with routing, layout,
and auth/API client updates needed to bootstrap the app.
Case timestamps come from the ServiceNow-backed backend as zone-less
strings ("2026-06-08 10:15:00" or "...T10:15:00", no offset/Z), but
the code parsed them with a bare `new Date(str)`, which treats an
unzoned date-time string as local time rather than UTC. Depending on
the viewer's timezone this silently shifts every timestamp by the
local UTC offset, and on stricter runtimes fails to parse at all —
dayjs renders that failure as "a month ago" instead of erroring,
which is what showed up on every case/service-request card.

Add parseBackendTimestamp()/parseOptionalBackendTimestamp(), which
normalize the zone-less string to explicit UTC before parsing, and
use them wherever case/comment timestamps are mapped from the DTO.
The previous fix only normalized "YYYY-MM-DD HH:mm:ss" and
"YYYY-MM-DDTHH:mm:ss" (no zone). The sibling csm-portal webapp's
proven normalizer (src/utils/dateTime.ts there) also handles a
"MM/DD/YYYY HH:mm:ss" variant and is lenient about non-zero-padded
components — some ServiceNow APIs return timestamps in that shape.
Port the same three patterns here so the microapp covers what the
webapp already covers, since this is what showed up as "Updated —"
(the invalid-date fallback) on real case cards.
Checked the sibling csm-portal webapp's mapping
(useGetCsmCases.ts: `updatedAt: c.updatedOn ?? c.createdOn ?? ""`) and
its hand-maintained backend types (api/backend/types.ts), which mark
both createdOn and updatedOn optional — unlike this repo's OpenAPI
doc, which declares them required. In practice the search/detail
views don't always populate updatedOn (e.g. a case untouched since
creation), which is what produced "Updated —" for real cases like
CS0440563 even after normalizing the timestamp format.

Mark createdOn/updatedOn optional on CaseSearchViewDto/CaseViewDto to
match the real contract, and fall back updatedOn to createdOn during
mapping, same as the webapp. Also guard formatDate() the same way
fromNow() already is, so an unparseable date renders "—" instead of
"Invalid Date" on the case detail page.
Copilot AI review requested due to automatic review settings July 8, 2026 10:35

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

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

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

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

Run ID: ba3cabde-bedf-432d-8601-4e9f6b047183

📥 Commits

Reviewing files that changed from the base of the PR and between f2298d7 and d8dbc74.

📒 Files selected for processing (1)
  • apps/csm-portal/microapp/src/config/endpoints.ts
📝 Walkthrough

Walkthrough

This PR adds the CSM Portal microapp shell, native bridge, auth and API layers, case data types and services, support/case detail screens, shared UI components, theming, and global styling updates.

Changes

CSM Portal Microapp Implementation

Layer / File(s) Summary
Native bridge and app entry/bootstrap
apps/csm-portal/microapp/package.json, apps/csm-portal/microapp/src/components/microapp-bridge/*, apps/csm-portal/microapp/src/config/config.ts, apps/csm-portal/microapp/src/utils/constants.ts, apps/csm-portal/microapp/src/utils/logger.ts, apps/csm-portal/microapp/src/App.tsx, apps/csm-portal/microapp/src/components/common/ErrorBoundary.tsx, apps/csm-portal/microapp/src/components/layout/*, apps/csm-portal/microapp/src/main.tsx
Adds bridge helpers for native communication, config and logger constants, routing with safe-area insets, tab layout wiring, React Query and theme providers, the error boundary component, and the Oxygen UI dependency upgrade.
Theming and global styles
apps/csm-portal/microapp/src/theme/*, apps/csm-portal/microapp/src/index.css
Adds the theme module extending AcrylicOrangeTheme with custom typography and updates global CSS for font loading, layout behavior, and keyframe animations.
Auth, token storage, and API client with refresh
apps/csm-portal/microapp/src/services/auth.ts, apps/csm-portal/microapp/src/services/apiClient.ts, apps/csm-portal/microapp/src/store/user.ts, apps/csm-portal/microapp/src/config/endpoints.ts
Adds localStorage token helpers, refresh logic using bridge callbacks, the Zustand user store, and an Axios client with interceptors that inject auth headers and coordinate single-flight 401 retries.
Case domain types, mappers, and services
apps/csm-portal/microapp/src/types/*, apps/csm-portal/microapp/src/utils/dateTime.ts, apps/csm-portal/microapp/src/utils/ApiError.ts, apps/csm-portal/microapp/src/services/cases.ts
Defines case DTOs and model interfaces with mapper functions, exports them through the types barrel, and implements the case query helpers and React Query options.
Shared support UI components and config
apps/csm-portal/microapp/src/components/support/*
Adds reusable support-case cards, chips, empty and error states, and the config mappings for tabs, icons, labels, and chip colors.
Support list and case detail pages
apps/csm-portal/microapp/src/pages/SupportPage.tsx, apps/csm-portal/microapp/src/pages/CaseDetailPage.tsx
Implements the tab-filtered support case list and the case detail page with suspense loading, skeleton fallbacks, and query-aware error boundaries.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: Type/New Feature, Platform/Microapp, App/CSM Portal

Suggested reviewers: Rashmika998, cloby99

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description only includes Summary and Test plan and omits the required template sections like Purpose, Goals, Approach, and Security checks. Add the missing template sections, especially Purpose, Goals, Approach, User stories, Release note, Documentation, Security checks, and Test environment.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly matches the main change: the support page, case detail, and app shell work.
✨ 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.

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

🧹 Nitpick comments (9)
apps/csm-portal/microapp/src/components/microapp-bridge/index.ts (1)

24-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate TOPIC and Topic into a single source of truth.

TOPIC duplicates several values already in the exported Topic constant from types.ts (e.g., TOKEN, SAVE_LOCAL_DATA, GET_LOCAL_DATA, OPEN_URL, MICRO_APP_VERSION) but is not type-checked against TopicType. Adding alert, confirm_alert, and totp to Topic and removing TOPIC would eliminate drift risk and give all topics compile-time safety.

🤖 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/microapp/src/components/microapp-bridge/index.ts` around
lines 24 - 34, Consolidate the duplicated topic definitions by removing the
local TOPIC object in microapp-bridge and using the exported Topic constant from
types.ts as the single source of truth. Update TopicType to include the missing
alert, confirm_alert, and totp values, then replace all TOPIC references in the
microapp bridge with Topic so the topic names stay compile-time checked and
cannot drift.
apps/csm-portal/microapp/src/components/layout/MainLayout.tsx (1)

21-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider using --tab-bar-height CSS variable instead of hardcoded pb={15}.

TabBar dynamically measures its height and sets --tab-bar-height via ResizeObserver, but MainLayout uses a fixed pb={15} (120px with 8px spacing). If the TabBar's actual height diverges from this value—due to font scaling, orientation changes, or safe-area insets—content could overlap the TabBar or leave excess space. Using the CSS variable keeps the layout in sync with the measured height.

♻️ Proposed refactor
 export default function MainLayout() {
   return (
     <>
-      <Box component="main" p={2} pb={15}>
+      <Box component="main" p={2} sx={{ pb: "calc(var(--tab-bar-height, 120px) + 16px)" }}>
         <Outlet />
       </Box>
       <TabBar />
     </>
   );
 }
🤖 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/microapp/src/components/layout/MainLayout.tsx` around lines
21 - 30, The MainLayout padding-bottom is hardcoded with pb={15}, which can
drift from the TabBar’s measured height. Update MainLayout to use the
--tab-bar-height CSS variable instead, referencing the existing TabBar
measurement behavior so the main content stays aligned as the bar size changes.
Keep the change localized in MainLayout and preserve the Outlet/TabBar
structure.
apps/csm-portal/microapp/src/services/apiClient.ts (2)

33-35: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

No request timeout configured — requests can hang indefinitely.

Without a timeout on the Axios instance, a non-responsive backend will leave requests pending forever, degrading the UX of the microapp.

♻️ Proposed fix
 const apiClient = axios.create({
   baseURL: BACKEND_URL,
+  timeout: 30000,
 });
🤖 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/microapp/src/services/apiClient.ts` around lines 33 - 35, The
Axios instance in apiClient is missing a request timeout, so requests can hang
indefinitely if the backend is unresponsive. Update the axios.create
configuration for apiClient to include a reasonable timeout value alongside
BACKEND_URL, and keep the change scoped to the shared client so all requests
made through this instance inherit the timeout automatically.

155-162: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

console.error bypasses the Logger and native DevTools pipeline.

Line 158 uses console.error directly while the rest of the file uses Logger.error. This error won't be forwarded to the native bridge.

♻️ Proposed fix
-        console.error("Token refresh failed:", refreshError);
+        Logger.error("Token refresh failed:", refreshError);
🤖 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/microapp/src/services/apiClient.ts` around lines 155 - 162,
The refresh-failure path in apiClient’s token handling is logging through
console.error instead of the shared Logger, so the native bridge won’t receive
it. Update the catch block around the token refresh flow to use Logger.error
consistently with the existing "Token refresh failed" message and refreshError,
and remove the direct console.error call so all errors go through the same
logging pipeline.
apps/csm-portal/microapp/src/config/config.ts (1)

39-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

serviceUrls has a redundant nested key.

The exported object nests a serviceUrls key inside serviceUrls, producing serviceUrls.serviceUrls. This is likely a copy-paste mistake.

♻️ Proposed fix
 export const serviceUrls = {
-  serviceUrls: {
-    // TODO: Add service URLs here as needed for future implementation.
-  },
+  // TODO: Add service URLs here as needed for future implementation.
 };
🤖 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/microapp/src/config/config.ts` around lines 39 - 43, The
exported serviceUrls object currently has an unnecessary nested serviceUrls key,
resulting in serviceUrls.serviceUrls instead of a flat shape. Update the
serviceUrls export in config.ts to remove the inner wrapper and keep only the
actual URL entries at the top level of the exported object. Use the serviceUrls
symbol as the location to adjust the structure so consumers access URLs directly
from serviceUrls.
apps/csm-portal/microapp/src/services/auth.ts (1)

44-51: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reject promises with Error objects, not strings.

Rejecting with strings ("ID Token failed") produces non-Error rejection values. Downstream handlers using instanceof Error or accessing .message/.stack will behave unexpectedly.

♻️ Proposed fix
-    getToken((token) => (token ? resolve(token) : reject("ID Token failed")));
+    getToken((token) => (token ? resolve(token) : reject(new Error("ID Token failed"))));
-    getAccessTokenFromBridge((token) => (token ? resolve(token) : reject("Access Token failed")));
+    getAccessTokenFromBridge((token) => (token ? resolve(token) : reject(new Error("Access Token failed"))));
🤖 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/microapp/src/services/auth.ts` around lines 44 - 51, Update
refreshToken so the promise rejections use Error objects instead of string
literals. In the idTokenPromise and accessTokenPromise callbacks, reject with a
new Error containing the same failure message, and keep the logic in
refreshToken unchanged otherwise so downstream handlers can reliably inspect
.message and .stack.
apps/csm-portal/microapp/src/pages/CaseDetailPage.tsx (1)

52-54: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use useSuspenseQueries for parallel fetching of case detail and comments.

The two useSuspenseQuery calls are sequential — the comments query doesn't start until the case detail query resolves. Switching to useSuspenseQueries allows both to fetch in parallel, cutting perceived load time when both requests take similar durations.

♻️ Proposed refactor
-import { useSuspenseQuery } from "`@tanstack/react-query`";
+import { useSuspenseQueries } from "`@tanstack/react-query`";

 function CaseDetailContent({ id }: { id: string }) {
-  const { data: caseDetail } = useSuspenseQuery(cases.get(id));
-  const { data: comments } = useSuspenseQuery(cases.comments(id));
+  const [{ data: caseDetail }, { data: comments }] = useSuspenseQueries({
+    queries: [cases.get(id), cases.comments(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 `@apps/csm-portal/microapp/src/pages/CaseDetailPage.tsx` around lines 52 - 54,
The two data fetches in CaseDetailContent are happening sequentially because
separate useSuspenseQuery calls are used for cases.get(id) and
cases.comments(id). Refactor this component to use useSuspenseQueries with both
query configs so case detail and comments start in parallel, and update the
destructuring in CaseDetailContent to read both results from the combined hook.
apps/csm-portal/microapp/src/types/case.model.ts (2)

107-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Invalid Date when both createdOn and updatedOn are absent.

When both fields are undefined, parseBackendTimestamp("") produces new Date("") — an Invalid Date. The model types declare createdOn: Date and updatedOn: Date (non-nullable), so consumers expect a valid Date. The formatDate/fromNow utilities guard with Number.isNaN(date.getTime()) and render "—", so the UI is safe. However, the type contract is misleading. Consider typing these as Date | null in the model and returning null when the source is absent, so the type system reflects reality.

Also applies to: 135-136

🤖 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/microapp/src/types/case.model.ts` around lines 107 - 108, The
Case model timestamps are typed as non-nullable Date even though
parseBackendTimestamp("") can return an Invalid Date when createdOn and
updatedOn are missing. Update the relevant model/DTO mapping in case.model.ts,
including the constructors or mappers around createdOn and updatedOn, so absent
backend values return null instead of an invalid Date. Adjust the associated
Case type definitions to Date | null and make sure any consumers rely on the
same symbols (parseBackendTimestamp, createdOn, updatedOn) reflect the nullable
contract consistently.

104-104: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Unsafe as CaseType casts on dto.type.

CaseSearchViewDto.type is string and CaseViewDto.type is string | null, but both are cast to CaseType / CaseType | null without runtime validation. If the backend returns an unexpected type string, the model will carry an invalid CaseType. The UI mitigates this with TYPE_CONFIG[item.type] ?? TYPE_CONFIG.case in CaseCard.tsx:27, so this is not a runtime defect, but the type assertion masks the gap. Consider narrowing the DTO type to CaseType directly (if the backend contract guarantees it), or adding a runtime guard.

Also applies to: 131-131

🤖 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/microapp/src/types/case.model.ts` at line 104, The
`CaseModel` mapping is unsafely asserting `dto.type` to `CaseType` (and the
nullable variant), which can hide invalid backend values. Update the conversion
logic in `CaseModel` to either use a runtime guard/validator before assigning
`type`, or tighten the DTO definitions for `CaseSearchViewDto`/`CaseViewDto` so
`type` is already `CaseType`-typed if the backend contract guarantees it. Make
the same fix in both mapping sites that currently use the `as CaseType` cast.
🤖 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/microapp/package.json`:
- Around line 22-24: The package.json versions for `@wso2/oxygen-ui`,
`@wso2/oxygen-ui-charts-react`, and `@wso2/oxygen-ui-icons-react` have been bumped,
but the lockfile still pins older resolved versions. Update the
package-lock.json entries for these three packages so they resolve to 0.6.1
consistently, and make sure the lockfile matches the dependency declarations in
package.json for the microapp package.

In `@apps/csm-portal/microapp/src/components/microapp-bridge/index.ts`:
- Around line 156-158: The error branches in saveLocalData, getLocalData, and
getVersion only log ErrorMessages.NATIVE_BRIDGE_NOT_AVAILABLE and leave the
caller hanging; update each native-bridge fallback path in
microapp-bridge/index.ts to invoke the provided callback with an error/failed
result after logging. Use the existing callback parameters in saveLocalData,
getLocalData, and getVersion so every code path completes even when the native
bridge is unavailable.
- Line 143: Normalize the key string in the microapp bridge helper by replacing
every space, not just the first one. Update the key normalization logic where
key.toString().replace(" ", "-").toLowerCase() is used so it handles all
whitespace occurrences (for example with replaceAll or a global regex), and
apply the same change to the duplicate normalization site noted in the review.
- Line 144: The current bridge serialization in microapp-bridge uses btoa/atob
on JSON.stringify(value), which breaks for UTF-8 payloads like emoji or CJK
characters. Update the paired encode/decode logic in the relevant
microapp-bridge helpers around the existing encodedValue handling to use a
UTF-8-safe approach such as TextEncoder/TextDecoder (or an equivalent safe
base64 wrapper) so both serialization and deserialization can handle non-Latin1
data reliably.

In `@apps/csm-portal/microapp/src/index.css`:
- Line 19: Fix the stylelint violations in index.css by changing the Google
Fonts import in the stylesheet to use plain `@import` string notation instead of
url(), and rename the keyframe definitions gentlePulse and stepIn to kebab-case
names gentle-pulse and step-in. Update any animation references in the same
stylesheet or related styles that use the old keyframe names so they match the
renamed identifiers.

In `@apps/csm-portal/microapp/src/main.tsx`:
- Around line 29-35: The retry guard in the query client setup is treating every
4xx response as non-retryable, which blocks 429 Too Many Requests from backing
off and retrying. Update the retry logic in the main bootstrap configuration to
special-case status 429 as retryable while keeping other 4xx responses excluded,
and keep the existing failureCount-based retry limit intact in the retry
callback.

In `@apps/csm-portal/microapp/src/pages/SupportPage.tsx`:
- Around line 29-43: The SupportPage tab state is being initialized from the URL
only once, so it can drift from the current search param during browser
back/forward navigation. Update SupportPage to derive the active tab directly
from searchParams.tab instead of storing it in local useState, and keep
handleTabChange only responsible for updating the URL via setSearchParams. Use
the existing symbols SupportPage, tabFromParams, tab, and handleTabChange to
make the change.

In `@apps/csm-portal/microapp/src/services/apiClient.ts`:
- Around line 43-49: The apiClient interceptors are logging sensitive data
through Logger and sendNativeLog. Update the request logging in apiClient to
avoid emitting config.headers (especially Authorization) and the full
URL/details that may expose tokens, and change the response logging path to stop
spreading response.data into logs. Use apiClient’s request/response interceptor
code to redact or omit sensitive fields before calling Logger.info and
sendNativeLog, keeping only minimal non-sensitive metadata.
- Around line 52-60: The token refresh flow in apiClient currently reuses
refreshTokenPromise only while a request is in flight, but refreshToken() still
always calls the native bridge and re-initializes the user on every request once
the promise settles. Update the logic around apiClient’s token retrieval path
and refreshToken() so it first checks the current token’s expiry or near-expiry
state, and only invokes the bridge refresh when the token is close to expiring
or already expired. Keep the existing refreshTokenPromise deduping in place, but
make the refresh decision conditional on expiry rather than unconditional.
- Around line 124-126: The Axios response interceptor in apiClient.ts assumes
error.config is always present, but some Axios errors can omit it and cause a
crash before the 401 retry logic runs. Update the interceptor around the
originalRequest/_retry check to first guard for a missing config and exit early
when it is absent. Also extend the request config type used by the interceptor
so it explicitly includes the _retry flag, ensuring the retry marker is
type-safe when accessed in the retry path.

In `@apps/csm-portal/microapp/src/services/auth.ts`:
- Around line 79-92: Wrap the jwtDecode call in checkUserGroups with try-catch
so a malformed ID token doesn’t throw an unhandled exception; on decode failure,
log a clear error with Logger.error and return false. Keep the existing
token-missing early return, and mirror the error-handling pattern used by
decodeTokenAndStoreUser so callers of checkUserGroups can safely rely on a
boolean result.
- Around line 99-123: The success path in decodeTokenAndStoreUser builds a User
but never saves it to the Zustand store, so the current user stays null. Update
decodeTokenAndStoreUser to call useUserStore.getState().setUser(user) after
decoding the token and before returning, while keeping the existing clearUser
calls on the null-token and catch paths. Also ensure initializeUserFromToken
relies on the store update rather than discarding the returned User.

In `@apps/csm-portal/microapp/src/services/cases.ts`:
- Around line 52-57: The getCaseComments helper currently hardcodes a 50-item
page and drops CaseCommentSearchResponseDto pagination metadata, so update it to
either return the full response details (such as total/hasMore) or otherwise
expose enough info for the UI to know more comments exist. Then adjust
CaseCommentsSection to use that metadata for a “Load more” flow or, at minimum,
show a “Showing first 50 comments” hint when the limit is reached, using
getCaseComments and toComment as the key symbols to update.

---

Nitpick comments:
In `@apps/csm-portal/microapp/src/components/layout/MainLayout.tsx`:
- Around line 21-30: The MainLayout padding-bottom is hardcoded with pb={15},
which can drift from the TabBar’s measured height. Update MainLayout to use the
--tab-bar-height CSS variable instead, referencing the existing TabBar
measurement behavior so the main content stays aligned as the bar size changes.
Keep the change localized in MainLayout and preserve the Outlet/TabBar
structure.

In `@apps/csm-portal/microapp/src/components/microapp-bridge/index.ts`:
- Around line 24-34: Consolidate the duplicated topic definitions by removing
the local TOPIC object in microapp-bridge and using the exported Topic constant
from types.ts as the single source of truth. Update TopicType to include the
missing alert, confirm_alert, and totp values, then replace all TOPIC references
in the microapp bridge with Topic so the topic names stay compile-time checked
and cannot drift.

In `@apps/csm-portal/microapp/src/config/config.ts`:
- Around line 39-43: The exported serviceUrls object currently has an
unnecessary nested serviceUrls key, resulting in serviceUrls.serviceUrls instead
of a flat shape. Update the serviceUrls export in config.ts to remove the inner
wrapper and keep only the actual URL entries at the top level of the exported
object. Use the serviceUrls symbol as the location to adjust the structure so
consumers access URLs directly from serviceUrls.

In `@apps/csm-portal/microapp/src/pages/CaseDetailPage.tsx`:
- Around line 52-54: The two data fetches in CaseDetailContent are happening
sequentially because separate useSuspenseQuery calls are used for cases.get(id)
and cases.comments(id). Refactor this component to use useSuspenseQueries with
both query configs so case detail and comments start in parallel, and update the
destructuring in CaseDetailContent to read both results from the combined hook.

In `@apps/csm-portal/microapp/src/services/apiClient.ts`:
- Around line 33-35: The Axios instance in apiClient is missing a request
timeout, so requests can hang indefinitely if the backend is unresponsive.
Update the axios.create configuration for apiClient to include a reasonable
timeout value alongside BACKEND_URL, and keep the change scoped to the shared
client so all requests made through this instance inherit the timeout
automatically.
- Around line 155-162: The refresh-failure path in apiClient’s token handling is
logging through console.error instead of the shared Logger, so the native bridge
won’t receive it. Update the catch block around the token refresh flow to use
Logger.error consistently with the existing "Token refresh failed" message and
refreshError, and remove the direct console.error call so all errors go through
the same logging pipeline.

In `@apps/csm-portal/microapp/src/services/auth.ts`:
- Around line 44-51: Update refreshToken so the promise rejections use Error
objects instead of string literals. In the idTokenPromise and accessTokenPromise
callbacks, reject with a new Error containing the same failure message, and keep
the logic in refreshToken unchanged otherwise so downstream handlers can
reliably inspect .message and .stack.

In `@apps/csm-portal/microapp/src/types/case.model.ts`:
- Around line 107-108: The Case model timestamps are typed as non-nullable Date
even though parseBackendTimestamp("") can return an Invalid Date when createdOn
and updatedOn are missing. Update the relevant model/DTO mapping in
case.model.ts, including the constructors or mappers around createdOn and
updatedOn, so absent backend values return null instead of an invalid Date.
Adjust the associated Case type definitions to Date | null and make sure any
consumers rely on the same symbols (parseBackendTimestamp, createdOn, updatedOn)
reflect the nullable contract consistently.
- Line 104: The `CaseModel` mapping is unsafely asserting `dto.type` to
`CaseType` (and the nullable variant), which can hide invalid backend values.
Update the conversion logic in `CaseModel` to either use a runtime
guard/validator before assigning `type`, or tighten the DTO definitions for
`CaseSearchViewDto`/`CaseViewDto` so `type` is already `CaseType`-typed if the
backend contract guarantees it. Make the same fix in both mapping sites that
currently use the `as CaseType` cast.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cb0878d2-ccd6-4bc7-b67e-8d1b59124148

📥 Commits

Reviewing files that changed from the base of the PR and between 690d00f and 71af982.

📒 Files selected for processing (31)
  • apps/csm-portal/microapp/package.json
  • apps/csm-portal/microapp/src/App.tsx
  • apps/csm-portal/microapp/src/components/common/ErrorBoundary.tsx
  • apps/csm-portal/microapp/src/components/layout/MainLayout.tsx
  • apps/csm-portal/microapp/src/components/layout/TabBar.tsx
  • apps/csm-portal/microapp/src/components/microapp-bridge/index.ts
  • apps/csm-portal/microapp/src/components/microapp-bridge/types.ts
  • apps/csm-portal/microapp/src/components/support/CaseCard.tsx
  • apps/csm-portal/microapp/src/components/support/Chips.tsx
  • apps/csm-portal/microapp/src/components/support/EmptyState.tsx
  • apps/csm-portal/microapp/src/components/support/ErrorState.tsx
  • apps/csm-portal/microapp/src/components/support/config.tsx
  • apps/csm-portal/microapp/src/config/config.ts
  • apps/csm-portal/microapp/src/config/endpoints.ts
  • apps/csm-portal/microapp/src/index.css
  • apps/csm-portal/microapp/src/main.tsx
  • apps/csm-portal/microapp/src/pages/CaseDetailPage.tsx
  • apps/csm-portal/microapp/src/pages/SupportPage.tsx
  • apps/csm-portal/microapp/src/services/apiClient.ts
  • apps/csm-portal/microapp/src/services/auth.ts
  • apps/csm-portal/microapp/src/services/cases.ts
  • apps/csm-portal/microapp/src/store/user.ts
  • apps/csm-portal/microapp/src/theme/index.ts
  • apps/csm-portal/microapp/src/theme/typography.ts
  • apps/csm-portal/microapp/src/types/case.dto.ts
  • apps/csm-portal/microapp/src/types/case.model.ts
  • apps/csm-portal/microapp/src/types/index.ts
  • apps/csm-portal/microapp/src/utils/ApiError.ts
  • apps/csm-portal/microapp/src/utils/constants.ts
  • apps/csm-portal/microapp/src/utils/dateTime.ts
  • apps/csm-portal/microapp/src/utils/logger.ts

Comment thread apps/csm-portal/microapp/package.json
Comment thread apps/csm-portal/microapp/src/components/microapp-bridge/index.ts
Comment thread apps/csm-portal/microapp/src/components/microapp-bridge/index.ts
Comment thread apps/csm-portal/microapp/src/components/microapp-bridge/index.ts
Comment thread apps/csm-portal/microapp/src/index.css
Comment thread apps/csm-portal/microapp/src/services/apiClient.ts
Comment thread apps/csm-portal/microapp/src/services/apiClient.ts Outdated
Comment thread apps/csm-portal/microapp/src/services/auth.ts
Comment thread apps/csm-portal/microapp/src/services/auth.ts
Comment thread apps/csm-portal/microapp/src/services/cases.ts
…te, token/logging hygiene

- microapp-bridge: saveLocalData/getLocalData/getVersion now invoke their
  callback with a failure result when the native bridge is unavailable,
  instead of leaving the caller hanging forever.
- SupportPage: derive the active tab directly from the ?tab= search param
  every render instead of a useState that only reads it once, so browser
  back/forward navigation is reflected instead of showing a stale tab.
- apiClient: stop logging config.headers on outgoing requests (a retried
  request already carries the prior Authorization/x-user-id-token at that
  point) and stop spreading response.data into the response log (response
  bodies carry user PII and case content) — both logs forward to the native
  bridge. Guard against error.config being undefined in the response
  interceptor, and type the _retry marker instead of relying on implicit any.
- auth: refreshToken() now skips the native-bridge round trip (and the user
  re-init that comes with it) when the current ID token still has useful
  life left, only refreshing when it's missing or near expiry. checkUserGroups
  no longer throws on a malformed token. decodeTokenAndStoreUser now actually
  calls setUser() on success, instead of decoding a user and discarding it.

Two other review findings were checked against the sibling webapp and skipped
as intentional, not oversights: main.tsx's retry policy and cases.ts's
getCaseComments page-size/pagination both match the webapp's documented,
deliberate behavior (webapp only retries 502/503 by design, and caps
comments at the same 50-item single page with the same "switch to an
explicit pagination wrapper" caveat).
@2003dinijay
2003dinijay requested a review from Rashmika998 July 8, 2026 11:42
Comment thread apps/csm-portal/microapp/src/config/endpoints.ts
Comment thread apps/csm-portal/microapp/src/index.css Outdated
Comment thread apps/csm-portal/microapp/src/index.css Outdated
2003dinijay and others added 2 commits July 8, 2026 17:19
Co-authored-by: Anuradha Basnayake <anuradhabsnk@gmail.com>
Co-authored-by: Anuradha Basnayake <anuradhabsnk@gmail.com>

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/csm-portal/microapp/src/index.css (1)

1-16: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Fix the remaining stylelint violations in apps/csm-portal/microapp/src/index.css

  • Replace @import url(...) with plain @import "..." at line 19.
  • Rename @keyframes gentlePulse and @keyframes stepIn to kebab-case at lines 43 and 48.
🤖 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/microapp/src/index.css` around lines 1 - 16, Fix the
remaining stylelint issues in index.css by updating the stylesheet imports and
animation names in the same file: replace the URL-based `@import` with the plain
string form, and rename the `@keyframes` blocks gentlePulse and stepIn to
kebab-case so they match the project’s naming rules. Update any matching
animation references in the CSS to use the new keyframe names consistently.
🤖 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/microapp/src/services/auth.ts`:
- Around line 63-67: The 401 retry path can reuse a stale bearer token because
refreshToken() short-circuits on the ID token alone while the request
interceptor and retry logic still read getAccessToken(). Update the auth flow so
a 401 forces an access-token refresh, or make refreshToken()’s fast path depend
on both the ID token and access token being valid. Use the existing refreshToken
and getAccessToken helpers in auth.ts so the interceptor and retry always pick
up a fresh bearer token.

---

Outside diff comments:
In `@apps/csm-portal/microapp/src/index.css`:
- Around line 1-16: Fix the remaining stylelint issues in index.css by updating
the stylesheet imports and animation names in the same file: replace the
URL-based `@import` with the plain string form, and rename the `@keyframes` blocks
gentlePulse and stepIn to kebab-case so they match the project’s naming rules.
Update any matching animation references in the CSS to use the new keyframe
names consistently.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 985c7bfb-d3ec-4181-a394-9f6d4a04e157

📥 Commits

Reviewing files that changed from the base of the PR and between 71af982 and f2298d7.

📒 Files selected for processing (22)
  • apps/csm-portal/microapp/src/App.tsx
  • apps/csm-portal/microapp/src/components/common/ErrorBoundary.tsx
  • apps/csm-portal/microapp/src/components/layout/MainLayout.tsx
  • apps/csm-portal/microapp/src/components/layout/TabBar.tsx
  • apps/csm-portal/microapp/src/components/microapp-bridge/index.ts
  • apps/csm-portal/microapp/src/components/support/CaseCard.tsx
  • apps/csm-portal/microapp/src/components/support/Chips.tsx
  • apps/csm-portal/microapp/src/components/support/EmptyState.tsx
  • apps/csm-portal/microapp/src/components/support/ErrorState.tsx
  • apps/csm-portal/microapp/src/components/support/config.tsx
  • apps/csm-portal/microapp/src/config/endpoints.ts
  • apps/csm-portal/microapp/src/index.css
  • apps/csm-portal/microapp/src/main.tsx
  • apps/csm-portal/microapp/src/pages/CaseDetailPage.tsx
  • apps/csm-portal/microapp/src/pages/SupportPage.tsx
  • apps/csm-portal/microapp/src/services/apiClient.ts
  • apps/csm-portal/microapp/src/services/auth.ts
  • apps/csm-portal/microapp/src/services/cases.ts
  • apps/csm-portal/microapp/src/types/case.dto.ts
  • apps/csm-portal/microapp/src/types/case.model.ts
  • apps/csm-portal/microapp/src/types/index.ts
  • apps/csm-portal/microapp/src/utils/dateTime.ts
✅ Files skipped from review due to trivial changes (2)
  • apps/csm-portal/microapp/src/components/support/ErrorState.tsx
  • apps/csm-portal/microapp/src/types/index.ts
🚧 Files skipped from review as they are similar to previous changes (17)
  • apps/csm-portal/microapp/src/components/common/ErrorBoundary.tsx
  • apps/csm-portal/microapp/src/components/support/EmptyState.tsx
  • apps/csm-portal/microapp/src/config/endpoints.ts
  • apps/csm-portal/microapp/src/components/support/Chips.tsx
  • apps/csm-portal/microapp/src/main.tsx
  • apps/csm-portal/microapp/src/components/support/CaseCard.tsx
  • apps/csm-portal/microapp/src/components/layout/TabBar.tsx
  • apps/csm-portal/microapp/src/components/layout/MainLayout.tsx
  • apps/csm-portal/microapp/src/components/support/config.tsx
  • apps/csm-portal/microapp/src/pages/SupportPage.tsx
  • apps/csm-portal/microapp/src/types/case.dto.ts
  • apps/csm-portal/microapp/src/pages/CaseDetailPage.tsx
  • apps/csm-portal/microapp/src/types/case.model.ts
  • apps/csm-portal/microapp/src/services/cases.ts
  • apps/csm-portal/microapp/src/components/microapp-bridge/index.ts
  • apps/csm-portal/microapp/src/services/apiClient.ts
  • apps/csm-portal/microapp/src/App.tsx

Comment thread apps/csm-portal/microapp/src/services/auth.ts
@cloby99
cloby99 merged commit a1ccc62 into wso2-open-operations:dev-app-csm-portal Jul 8, 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.

3 participants