Skip to content

Enhance analytics components with error handling and loading states - #3522

Merged
Baalmart merged 10 commits into
stagingfrom
OAuth-upgrades
May 21, 2026
Merged

Enhance analytics components with error handling and loading states#3522
Baalmart merged 10 commits into
stagingfrom
OAuth-upgrades

Conversation

@OchiengPaul442

@OchiengPaul442 OchiengPaul442 commented May 20, 2026

Copy link
Copy Markdown
Contributor

Status of maturity (all need to be checked before merging):

  • I've tested this locally
  • I consider this code done

Screenshots (optional)

Summary by CodeRabbit

  • New Features

    • Email selection card in the login flow for clearer account display and easy change.
    • Built-in payment integration and provider to support secure overlay checkout.
  • Bug Fixes

    • Prevented stale state updates and racey fetches in billing, subscription, and usage polling.
    • Dialog sidebar now reliably closes when dialogs close.
  • Improvements

    • Unified site/location display names across analytics, charts, dialogs, and favorites.
    • Richer analytics/dashboard states: better error messages, placeholders, refresh indicators, and loading UX.
    • Cleaner chart legends, tooltips, and download naming.
  • Chores

    • Added payment-related env examples.

Review Change Stack

…date analytics components for improved error handling
…nhance subscription management with mounted state checks; update chart components for better responsiveness and error handling; streamline user actions and service responses.
…; introduce getSiteDisplayName utility for consistent site name resolution
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR centralizes site/location display-name resolution, refactors analytics site-card construction to use chart points, enhances QuickAccess error/loading UX, adds async abort/mounted guards in billing and polling flows, integrates Paddle (provider, types, layout wiring), and polishes chart/layout behavior.

Changes

Site Naming Normalization and Analytics Refactoring

Layer / File(s) Summary
Shared site display-name utility and chart location naming
src/platform/src/shared/utils/siteUtils.ts, src/platform/src/shared/components/charts/utils/index.ts, src/platform/src/shared/components/charts/types/index.ts
Introduces getSiteDisplayName and getChartLocationDisplayName, extends chart point types with additional name fields, and updates normalization to populate unified name fields.
SelectedEmailCard component for auth pages
src/platform/src/shared/components/auth/SelectedEmailCard.tsx, src/platform/src/app/(dashboard)/(individual)/user/(auth)/login/page.tsx, src/platform/src/app/(dashboard)/(organization)/org/[org_slug]/(auth)/login/page.tsx
Adds SelectedEmailCard and replaces inline account/email preview UI in both individual and organization login flows, wiring change action to existing go-back handler.
Analytics hooks refactoring: site cards from chart data
src/platform/src/modules/analytics/hooks/index.ts
Builds SiteData[] from chart points over a recent lookback window (group-by-site, latest+previous points), includes user?.id in react-query keys, and refines loading/refresh/transition semantics.
Analytics card and dashboard display with getSiteDisplayName
src/platform/src/modules/analytics/components/AnalyticsCard.tsx, src/platform/src/modules/analytics/components/DownloadDialog.tsx, src/platform/src/modules/analytics/components/AnalyticsDashboard.tsx
Switches display-name derivation to getSiteDisplayName, updates site-object construction and wiring of siteCardsError, placeholderCount, isRefreshing/isLoading into QuickAccessCard.
QuickAccessCard error/loading states and skeleton rendering
src/platform/src/modules/analytics/components/QuickAccessCard.tsx, src/platform/src/modules/analytics/types/index.ts
Adds error-message processing, EmptyState rendering, skeleton placeholders (configurable placeholderCount), inline "Refreshing latest readings..." indicator, and conditional "+ Add Favorite" behavior; extends props with placeholderCount?: number and `errorMessage?: string
Location Insights site naming and chart context refactoring
src/platform/src/modules/location-insights/add-location.tsx, src/platform/src/modules/location-insights/add-favorites.tsx, src/platform/src/modules/location-insights/more-insights.tsx
Uses unified display names, introduces dialogContextKey to reset/init dialog state, and replaces chart-data callback pattern with an isActive-guarded effect that clears/resets chart state on dependency changes.
Billing components: mounted-state guards for async operations
src/platform/src/modules/billing/components/SubscriptionManagement.tsx, src/platform/src/modules/billing/components/SubscriptionSection.tsx, src/platform/src/modules/billing/components/UsageStats.tsx, src/platform/src/shared/services/subscriptionService.ts
Adds mounted/active guards and abort-signal support, prevents stale state updates after unmount, improves polling/abort handling, expands getUsage to accept an optional signal, and validates/normalizes checkout payloads (requires trimmed userId, default USD currency).
Service layer payload handling and cleanup
src/platform/src/shared/services/deviceService.ts, src/platform/src/shared/services/subscriptionService.ts
Introduces envelope-unwrapping helpers in deviceService and adapts response normalization to prefer envelope metadata; subscriptionService narrows profile endpoint candidates, expands fallback rate-limits handling, and normalizes structured error messages.
Paddle integration and provider
src/platform/src/shared/lib/paddle.ts, src/platform/src/shared/providers/paddle-provider.tsx, src/platform/src/shared/types/paddle.d.ts, src/platform/src/app/layout.tsx, src/platform/src/modules/billing/components/SubscriptionSection.tsx, src/platform/src/modules/billing/components/CheckoutDialog.tsx
Adds Paddle constants and helpers, a client-side PaddleProvider that injects/initializes the SDK and dispatches lifecycle DOM events, Paddle typings, wires the provider into RootLayout, and adapts checkout flow to open the Paddle overlay with returned sessionId.
Chart component layout and display refinements
src/platform/src/shared/components/charts/components/ChartContainer.tsx, src/platform/src/shared/components/charts/components/charts/DynamicChart.tsx, src/platform/src/shared/components/charts/components/ui/CustomTooltip.tsx, src/platform/src/shared/components/ui/wide-dialog.tsx
Adjusts ChartContainer sizing/padding, computes resolved margins and x-axis padding in DynamicChart, simplifies legend/label formatting, updates tooltip location display, and resets WideDialog sidebar when closed.
Build scripts and Dockerfile
src/platform/package.json, src/platform/Dockerfile, src/platform/scripts/copy-standalone-assets.mjs, src/platform/.env.example
Replaces postbuild script with an inline node copy command and removes the standalone script file; updates runner Dockerfile to include public and .next/static in final image; adds payment env example vars.

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels: ready for review

Suggested reviewers:

  • Baalmart

Site names aligned, the charts now sing,
QuickAccess shows errors and placeholders in spring,
Billing waits safely, Paddle wakes and calls,
Service envelopes unwrapped, no more surprise falls. ✨

🚥 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 directly aligns with the primary objective: enhancing analytics components with error handling and loading states, which is reflected across multiple file changes (QuickAccessCard, AnalyticsDashboard, AnalyticsCard).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch OAuth-upgrades

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.

@github-actions

Copy link
Copy Markdown
Contributor

New azure analytics_platform changes available for preview here

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

Caution

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

⚠️ Outside diff range comments (2)
src/platform/src/shared/services/subscriptionService.ts (1)

753-765: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fallback usage can return null limits during live-usage failures.

On Line 764, fallback uses only profile?.apiRateLimits. If that optional payload is absent, the catch path (Lines 796-803) returns usage limits as null despite the message saying plan limits are being shown.

Suggested fix
-    const getFallbackRateLimits = async (): Promise<
-      ApiRateLimitsPayload | undefined
-    > => {
+    const getFallbackRateLimits = async (): Promise<ApiRateLimitsPayload> => {
       if (!profile) {
         try {
           profile = await this.getUsersProfilePayload();
         } catch {
           profile = null;
         }
       }
 
-      return profile?.apiRateLimits ?? undefined;
+      return mergeRateLimitsWithDefaults(
+        normalizeTier(profile?.subscriptionTier),
+        profile?.apiRateLimits
+      );
     };

Also applies to: 778-783, 796-803

🤖 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/platform/src/shared/services/subscriptionService.ts` around lines 753 -
765, getFallbackRateLimits currently returns profile?.apiRateLimits which can be
null, causing "plan limits" to actually be null during failures; change the flow
so when profile exists but apiRateLimits is missing (or profile fetch fails) you
return a non-null safe ApiRateLimitsPayload (either derive sensible limits from
profile.plan if available or fall back to a new/shared DEFAULT_API_RATE_LIMITS
constant). Update getFallbackRateLimits to: attempt
this.getUsersProfilePayload(), then if profile.apiRateLimits is falsy build and
return a default ApiRateLimitsPayload (do not return null); apply the same fix
to the other similar fallback blocks referenced (the blocks around the other
getUsersProfilePayload usages) so all branches return a concrete
ApiRateLimitsPayload instead of null.
src/platform/src/modules/billing/components/SubscriptionSection.tsx (1)

86-129: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Prefer SWR or React Query over manual fetch-and-guard pattern.

While the isMountedRef guard correctly prevents state updates after unmount, this manual fetch pattern violates the guideline to use SWR/React Query. Since refreshData is invoked both on mount and after user actions (enable/disable auto-renew, cancellation), using SWR with mutation would provide automatic revalidation, request deduplication, and built-in cancellation.

Consider migrating to SWR:

import useSWR from 'swr';

const { data: subscription, mutate: refreshSubscription } = useSWR(
  'user-subscription',
  () => subscriptionService.getSubscription()
);

const { data: plans } = useSWR(
  'subscription-plans',
  () => subscriptionService.getPlans()
);

// After actions:
await refreshSubscription();

This would eliminate manual mount guards, simplify the async flow, and provide automatic cancellation via SWR's internal AbortController. As per coding guidelines, client fetches should prefer SWR/React Query with stable keys instead of custom fetch loops.

🤖 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/platform/src/modules/billing/components/SubscriptionSection.tsx` around
lines 86 - 129, The current manual fetch pattern in refreshData (used with
isMountedRef, setSubscription, setPlans and invoked in useEffect) should be
replaced with SWR/React Query: create useSWR hooks for
subscriptionService.getSubscription and subscriptionService.getPlans (e.g.,
useSWR('user-subscription', () => subscriptionService.getSubscription()) and
useSWR('subscription-plans', () => subscriptionService.getPlans())), remove the
isMountedRef guard and the refreshData function and its useEffect, and wire
post-action refreshes (enable/disable auto-renew, cancellation) to the returned
mutate or refresh functions from useSWR to revalidate; ensure error handling and
loading state use SWR’s error and isLoading values instead of manual try/catch
and setLoading.
🧹 Nitpick comments (2)
src/platform/src/modules/location-insights/more-insights.tsx (2)

202-273: ⚡ Quick win

Consider adding user-facing error state for failed chart loads.

Currently, when chart data fetching fails, the error is only logged to console and the chart shows empty data. Users have no indication that a fetch error occurred versus legitimately empty data.

💡 Example error state handling

Add error state:

 const [chartData, setChartData] = useState<ChartData[]>([]);
+const [chartError, setChartError] = useState<string | null>(null);

Update the effect:

   setChartData([]);
+  setChartError(null);

   const run = async () => {
     try {
       // ... existing fetch logic
     } catch (error) {
       if (!isActive) {
         return;
       }

+      if (error?.name !== 'AbortError' && error?.name !== 'CanceledError') {
+        setChartError('Failed to load chart data. Please try again.');
+      }
       console.error('Error fetching chart data:', error);
       setChartData([]);
     }
   };

Pass error to ChartContainer:

 <ChartContainer
   title={...}
   loading={isChartLoading}
+  error={chartError}
   className="h-full flex flex-col border"
🤖 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/platform/src/modules/location-insights/more-insights.tsx` around lines
202 - 273, Add a user-facing error state: introduce an error state (e.g.,
chartError) alongside setChartData and clear it whenever you start a new fetch
or when the dialog closes; in the useEffect surrounding getChartData (the async
run function), set chartError to null before calling getChartData, set
chartError to the caught error message inside the catch block (only if
isActive), and ensure you also clear chartError when you setChartData([]) for
legitimately empty responses; finally pass chartError into the ChartContainer
(or whatever component consumes chart data) so it can render an error UI instead
of an empty chart—refer to useEffect, run, getChartData,
normalizeAirQualityData, setChartData, and isActive to locate where to update
state and where to pass the new prop.

202-273: ⚡ Quick win

Add AbortController to cancel in-flight chart requests.

The chart data fetch can be re-fired when dependencies change (filters, date range, visible sites), but uses an isActive flag instead of AbortController. While the flag prevents stale state updates, it doesn't cancel the in-flight HTTP request, wasting resources and potentially causing issues with rapid filter changes.

As per coding guidelines, "Use AbortController for cohort endpoints and any query that can be re-fired on re-render."

♻️ Refactor to add request cancellation
 useEffect(() => {
   let isActive = true;
+  const abortController = new AbortController();

   if (
     !isOpen ||
     visibleSiteIds.length === 0 ||
     !dateRange?.from ||
     !dateRange?.to
   ) {
     setChartData([]);
     return () => {
       isActive = false;
+      abortController.abort();
     };
   }

   setChartData([]);

   const run = async () => {
     try {
       const response = await getChartData({
         sites: visibleSiteIds,
         startDate: dateRange.from.toISOString().split('T')[0],
         endDate: dateRange.to.toISOString().split('T')[0],
         chartType: chartType,
         frequency: frequency,
         pollutant: pollutant.toLowerCase().replace('.', '_'),
         organisation_name: '',
+        signal: abortController.signal,
       });

       if (!isActive) {
         return;
       }

       if (
         response?.data &&
         Array.isArray(response.data) &&
         response.data.length > 0
       ) {
         const transformed = normalizeAirQualityData(
           response.data as ChartDataPoint[]
         );
         setChartData(transformed);
         return;
       }

       setChartData([]);
     } catch (error) {
       if (!isActive) {
         return;
       }

+      // Don't log cancellation as error
+      if (error?.name === 'AbortError' || error?.name === 'CanceledError') {
+        return;
+      }

       console.error('Error fetching chart data:', error);
       setChartData([]);
     }
   };

   run();

   return () => {
     isActive = false;
+    abortController.abort();
   };
 }, [

Note: Verify that getChartData accepts a signal parameter. If the underlying implementation doesn't support it, you may need to update the hook.

🤖 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/platform/src/modules/location-insights/more-insights.tsx` around lines
202 - 273, The effect currently uses an isActive flag to avoid updating state
from stale responses but doesn't cancel the in-flight request; update the
useEffect to create an AbortController, pass its signal into getChartData
(verify getChartData accepts a signal or update it accordingly), and call
controller.abort() in the cleanup; inside the run try/catch handle aborts by
checking error.name === 'AbortError' (or early-return on signal.aborted) so you
avoid setChartData after abort, and keep existing isActive logic only if needed
for extra safety (references: useEffect, run, getChartData, setChartData,
isActive).
🤖 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/platform/src/modules/analytics/components/QuickAccessCard.tsx`:
- Around line 31-35: The empty/error state logic hides usable site cards when an
error occurs and prints errorMessage verbatim; update the conditions so the
empty/error UI is only shown when sites.length === 0 (e.g., compute
shouldShowErrorState as !isLoading && sites.length === 0 &&
Boolean(errorMessage)) and adjust shouldShowAddFavorite/shouldShowSkeleton
accordingly, and sanitize/map errorMessage before rendering (handle cancellation
errors by treating them as non-user-facing, strip or replace tokens/session
data, and map backend/network/internal error texts to a user-friendly message
variable like displayErrorMessage used in the rendering code).

In `@src/platform/src/modules/analytics/hooks/index.ts`:
- Around line 111-130: The card currently computes percentageDifference and
trend from the immediately previous 24h point (using latestValue and
previousValue) while the UI copy describes a "last week" comparison; update this
code to compute week-over-week delta instead: locate where percentageDifference
and trend are produced (symbols: percentageDifference, generateTrend,
latestValue, previousValue, selectedSite) and replace previousValue with a value
sampled from ~7 days earlier (previousWeekValue) when available, compute
percentageDifference = Number.isFinite(previousWeekValue) && previousWeekValue
!== 0 ? ((latestValue - previousWeekValue)/Math.abs(previousWeekValue))*100 : 0
and call generateTrend(latestValue, previousWeekValue); preserve the existing
fallback to 0/trend-neutral if a week-ago value is missing so behavior remains
safe.

In `@src/platform/src/modules/billing/components/SubscriptionManagement.tsx`:
- Around line 19-66: Replace the custom useEffect/fetchSubscription pattern with
SWR: remove the isMounted guard and the fetchSubscription async function, call
useSWR with a stable key (e.g., 'user-subscription') and a fetcher that invokes
subscriptionService.getSubscription(), map the returned data to the same
normalizedSubscription shape (preserve the automaticRenewal/autoRenewal logic)
and update local state via setSubscription; use SWR's isLoading/error instead of
setLoading and the try/catch/toast block (or show toast on error when error from
useSWR exists). Ensure you import useSWR and keep references to
subscriptionService.getSubscription, setSubscription, and the normalization
logic so behavior remains identical.

In `@src/platform/src/modules/billing/components/UsageStats.tsx`:
- Around line 31-69: Replace the manual polling useEffect/fetchUsage/interval
pattern with SWR: remove the useEffect, isActive flag, fetchUsage function, and
clearInterval logic and instead call useSWR with a stable key (e.g.,
'api-usage-stats') and a fetcher that calls subscriptionService.getUsage(), set
refreshInterval to 5*60*1000 and revalidateOnFocus per your preference; then map
the returned data/error/isLoading from useSWR to your existing state handlers
(setUsage, setLiveUsage, setUsageMessage, setLoading) or derive UI state
directly from the SWR response so you no longer manually manage polling, aborts,
or deduplication.
- Around line 31-69: The polling logic in the UsageStats component creates
overlapping requests because fetchUsage has no cancellation; modify fetchUsage
to use an AbortController (create a new controller each invocation), pass its
signal into subscriptionService.getUsage (e.g., getUsage({ signal })) and store
the current controller in a ref so you can abort the previous controller before
starting a new fetch; also abort the active controller in the cleanup return and
ignore AbortError in the catch block (only log non-abort errors) and ensure
setLoading(false) still runs only when not aborted.

---

Outside diff comments:
In `@src/platform/src/modules/billing/components/SubscriptionSection.tsx`:
- Around line 86-129: The current manual fetch pattern in refreshData (used with
isMountedRef, setSubscription, setPlans and invoked in useEffect) should be
replaced with SWR/React Query: create useSWR hooks for
subscriptionService.getSubscription and subscriptionService.getPlans (e.g.,
useSWR('user-subscription', () => subscriptionService.getSubscription()) and
useSWR('subscription-plans', () => subscriptionService.getPlans())), remove the
isMountedRef guard and the refreshData function and its useEffect, and wire
post-action refreshes (enable/disable auto-renew, cancellation) to the returned
mutate or refresh functions from useSWR to revalidate; ensure error handling and
loading state use SWR’s error and isLoading values instead of manual try/catch
and setLoading.

In `@src/platform/src/shared/services/subscriptionService.ts`:
- Around line 753-765: getFallbackRateLimits currently returns
profile?.apiRateLimits which can be null, causing "plan limits" to actually be
null during failures; change the flow so when profile exists but apiRateLimits
is missing (or profile fetch fails) you return a non-null safe
ApiRateLimitsPayload (either derive sensible limits from profile.plan if
available or fall back to a new/shared DEFAULT_API_RATE_LIMITS constant). Update
getFallbackRateLimits to: attempt this.getUsersProfilePayload(), then if
profile.apiRateLimits is falsy build and return a default ApiRateLimitsPayload
(do not return null); apply the same fix to the other similar fallback blocks
referenced (the blocks around the other getUsersProfilePayload usages) so all
branches return a concrete ApiRateLimitsPayload instead of null.

---

Nitpick comments:
In `@src/platform/src/modules/location-insights/more-insights.tsx`:
- Around line 202-273: Add a user-facing error state: introduce an error state
(e.g., chartError) alongside setChartData and clear it whenever you start a new
fetch or when the dialog closes; in the useEffect surrounding getChartData (the
async run function), set chartError to null before calling getChartData, set
chartError to the caught error message inside the catch block (only if
isActive), and ensure you also clear chartError when you setChartData([]) for
legitimately empty responses; finally pass chartError into the ChartContainer
(or whatever component consumes chart data) so it can render an error UI instead
of an empty chart—refer to useEffect, run, getChartData,
normalizeAirQualityData, setChartData, and isActive to locate where to update
state and where to pass the new prop.
- Around line 202-273: The effect currently uses an isActive flag to avoid
updating state from stale responses but doesn't cancel the in-flight request;
update the useEffect to create an AbortController, pass its signal into
getChartData (verify getChartData accepts a signal or update it accordingly),
and call controller.abort() in the cleanup; inside the run try/catch handle
aborts by checking error.name === 'AbortError' (or early-return on
signal.aborted) so you avoid setChartData after abort, and keep existing
isActive logic only if needed for extra safety (references: useEffect, run,
getChartData, setChartData, isActive).
🪄 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: 29d5dec1-bf2a-47d8-b493-a8e3213a65c8

📥 Commits

Reviewing files that changed from the base of the PR and between 5c278b1 and bcdb3e9.

📒 Files selected for processing (27)
  • src/platform/src/app/(dashboard)/(individual)/user/(auth)/login/page.tsx
  • src/platform/src/app/(dashboard)/(organization)/org/[org_slug]/(auth)/login/page.tsx
  • src/platform/src/modules/analytics/components/AnalyticsCard.tsx
  • src/platform/src/modules/analytics/components/AnalyticsDashboard.tsx
  • src/platform/src/modules/analytics/components/DownloadDialog.tsx
  • src/platform/src/modules/analytics/components/QuickAccessCard.tsx
  • src/platform/src/modules/analytics/hooks/index.ts
  • src/platform/src/modules/analytics/types/index.ts
  • src/platform/src/modules/analytics/utils/index.ts
  • src/platform/src/modules/billing/components/SubscriptionManagement.tsx
  • src/platform/src/modules/billing/components/SubscriptionSection.tsx
  • src/platform/src/modules/billing/components/UsageStats.tsx
  • src/platform/src/modules/location-insights/add-favorites.tsx
  • src/platform/src/modules/location-insights/add-location.tsx
  • src/platform/src/modules/location-insights/more-insights.tsx
  • src/platform/src/shared/components/auth/SelectedEmailCard.tsx
  • src/platform/src/shared/components/charts/components/ChartContainer.tsx
  • src/platform/src/shared/components/charts/components/charts/DynamicChart.tsx
  • src/platform/src/shared/components/charts/components/ui/CustomTooltip.tsx
  • src/platform/src/shared/components/charts/types/index.ts
  • src/platform/src/shared/components/charts/utils/index.ts
  • src/platform/src/shared/components/ui/wide-dialog.tsx
  • src/platform/src/shared/hooks/usePreferences.ts
  • src/platform/src/shared/hooks/useUserActions.ts
  • src/platform/src/shared/services/deviceService.ts
  • src/platform/src/shared/services/subscriptionService.ts
  • src/platform/src/shared/utils/siteUtils.ts

Comment thread src/platform/src/modules/analytics/components/QuickAccessCard.tsx
Comment thread src/platform/src/modules/analytics/hooks/index.ts
Comment thread src/platform/src/modules/billing/components/UsageStats.tsx
… abort error detection and improve user feedback for loading states
@github-actions

Copy link
Copy Markdown
Contributor

New azure analytics_platform changes available for preview here

@github-actions

Copy link
Copy Markdown
Contributor

New azure analytics_platform changes available for preview here

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

Caution

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

⚠️ Outside diff range comments (1)
src/platform/src/modules/analytics/hooks/index.ts (1)

51-51: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Minor inconsistency in percentageDifference default values.

buildNoValueSiteCard (line 51) sets percentageDifference: 0, while buildSiteCardsFromChartPoints (line 114) returns undefined when no valid previous value exists. This inconsistency means "no value" cards will show "worsened by 0.0%" in the tooltip, whereas cards with a single reading will show the generic trend message.

Consider aligning both to undefined for consistent UX:

Suggested fix
 const buildNoValueSiteCard = (
   selectedSite: Site,
   pollutant: string
 ): SiteData => ({
   _id: selectedSite._id,
   name: getSiteDisplayName(selectedSite),
   location: selectedSite.country || 'Unknown Country',
   value: 0,
   status: 'no-value',
   pollutant,
   unit: 'μg/m³',
   trend: 'stable',
-  percentageDifference: 0,
+  percentageDifference: undefined,
 });

Also applies to: 111-114

🤖 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/platform/src/modules/analytics/hooks/index.ts` at line 51,
buildNoValueSiteCard currently sets percentageDifference: 0 which conflicts with
buildSiteCardsFromChartPoints returning undefined for "no previous value";
change buildNoValueSiteCard to set percentageDifference to undefined (or omit
the property) so both paths use undefined for missing comparisons, and if the
SiteCard type disallows undefined update the type signature (or make
percentageDifference optional) accordingly; update any tooltip/consumer logic
that checks for 0 vs undefined to rely on undefined as the "no value" sentinel.
🧹 Nitpick comments (1)
src/platform/src/shared/services/subscriptionService.ts (1)

105-122: 💤 Low value

Consider extracting isAbortError to a shared utility.

This helper duplicates the one in UsageStats.tsx (lines 14-31). Both check the same error signatures (AbortError, CanceledError, ERR_CANCELED, canceled). Extracting to a shared module (e.g., @/shared/utils/errorUtils.ts) would reduce duplication and ensure consistent abort detection across the codebase.

Not blocking, but worth consolidating when convenient.

🤖 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/platform/src/shared/services/subscriptionService.ts` around lines 105 -
122, The isAbortError helper is duplicated (e.g., in subscriptionService.ts and
UsageStats.tsx); extract it into a single shared utility (e.g., export function
isAbortError(...) in a new module like errorUtils.ts) and replace the local
implementations with imports of that function; ensure the exported utility
preserves the same checks (name === 'AbortError'|'CanceledError', code ===
'ERR_CANCELED', message === 'canceled'), update imports in SubscriptionService
(isAbortError) and UsageStats to use the shared function, and remove the
duplicate local definitions.
🤖 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.

Outside diff comments:
In `@src/platform/src/modules/analytics/hooks/index.ts`:
- Line 51: buildNoValueSiteCard currently sets percentageDifference: 0 which
conflicts with buildSiteCardsFromChartPoints returning undefined for "no
previous value"; change buildNoValueSiteCard to set percentageDifference to
undefined (or omit the property) so both paths use undefined for missing
comparisons, and if the SiteCard type disallows undefined update the type
signature (or make percentageDifference optional) accordingly; update any
tooltip/consumer logic that checks for 0 vs undefined to rely on undefined as
the "no value" sentinel.

---

Nitpick comments:
In `@src/platform/src/shared/services/subscriptionService.ts`:
- Around line 105-122: The isAbortError helper is duplicated (e.g., in
subscriptionService.ts and UsageStats.tsx); extract it into a single shared
utility (e.g., export function isAbortError(...) in a new module like
errorUtils.ts) and replace the local implementations with imports of that
function; ensure the exported utility preserves the same checks (name ===
'AbortError'|'CanceledError', code === 'ERR_CANCELED', message === 'canceled'),
update imports in SubscriptionService (isAbortError) and UsageStats to use the
shared function, and remove the duplicate local definitions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a344446a-0f94-470d-b856-483eecf498b9

📥 Commits

Reviewing files that changed from the base of the PR and between bcdb3e9 and b964ff7.

📒 Files selected for processing (9)
  • src/platform/Dockerfile
  • src/platform/package.json
  • src/platform/scripts/copy-standalone-assets.mjs
  • src/platform/src/modules/analytics/components/AnalyticsCard.tsx
  • src/platform/src/modules/analytics/components/QuickAccessCard.tsx
  • src/platform/src/modules/analytics/hooks/index.ts
  • src/platform/src/modules/billing/components/SubscriptionSection.tsx
  • src/platform/src/modules/billing/components/UsageStats.tsx
  • src/platform/src/shared/services/subscriptionService.ts
💤 Files with no reviewable changes (1)
  • src/platform/scripts/copy-standalone-assets.mjs

@OchiengPaul442
OchiengPaul442 requested a review from Baalmart May 21, 2026 18:06
@github-actions

Copy link
Copy Markdown
Contributor

New azure analytics_platform changes available for preview here

@Baalmart
Baalmart merged commit cf2167b into staging May 21, 2026
19 of 20 checks passed
@Baalmart
Baalmart deleted the OAuth-upgrades branch May 21, 2026 18:11
@Baalmart Baalmart mentioned this pull request May 21, 2026
4 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.

2 participants