Skip to content

Conversation

@Klakurka
Copy link
Member

@Klakurka Klakurka commented Dec 21, 2025

Test plan

Go to /dashboard and change the current button filter to see the loading spinner appear.

Summary by CodeRabbit

  • Bug Fixes

    • Dashboard now shows a reliable loading indicator during data fetches and avoids user-email rendering errors while loading.
  • Style

    • Loading indicator updated to a fullscreen, centered overlay with semi-transparent backdrop to prevent interaction while loading.

✏️ Tip: You can customize this high-level summary in your review settings.

@Klakurka Klakurka added this to the Phase 3 milestone Dec 21, 2025
@Klakurka Klakurka requested a review from lissavxo December 21, 2025 03:14
@Klakurka Klakurka self-assigned this Dec 21, 2025
@Klakurka Klakurka added the enhancement (UI/UX/feature) New feature or request label Dec 21, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 21, 2025

Walkthrough

Adds an internal isLoading flag to the dashboard and conditionally renders the Loading component during fetch. Renames Loading wrapper CSS class to .loading_overlay and changes its styles to a fullscreen, centered overlay with a high z-index.

Changes

Cohort / File(s) Summary
Dashboard loading state
pages/dashboard/index.tsx
Adds isLoading state; sets isLoading = true before fetching and false after data is set; conditionally renders Loading when true; updates TopBar user prop access to user?.stUser?.email via optional chaining.
Loading component
components/Loading/index.tsx
Replaced root wrapper CSS class usage from loading_container to loading_overlay; no logic or prop changes.
Loading styles
components/Loading/loading.module.css
Renamed class to .loading_overlay; changed to fixed fullscreen overlay (top/left/right/bottom: 0), added semi-transparent background, high z-index (--z-overlay-loading: 999999), and centered flex layout; removed previous min-height/width rules.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify isLoading is set/reset in all code paths (including errors/early returns).
  • Confirm Loading component uses the renamed class everywhere and no residual loading_container references remain.
  • Check visual/UX implications of a fixed fullscreen overlay (focus, scroll, stacking context).

Possibly related PRs

Suggested reviewers

  • lissavxo
  • chedieck

Poem

🐰 I hopped in swift with gentle paws,
A spinning dot that dims the pause,
True when fetching, calm when done,
Overlay hugs the rising sun,
Small rabbit dance — the load is won. ✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete; it only includes a test plan but is missing the required Description section and Related to # issue link from the template. Add the missing Description section explaining what was changed and why, and include the Related to # issue reference at the top of the description.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a loading spinner on the dashboard when filtering, which matches the code changes.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/dashboard-filter-loading-spinner

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
pages/dashboard/index.tsx (1)

120-141: Critical: Loading state not reset on error; Major: Race condition with rapid filter changes.

Two issues need addressing:

  1. Error handling gap: If the fetch fails, setIsLoading(false) is never called (line 136 only logs the error), leaving the loading spinner visible indefinitely and blocking the user.

  2. Race condition: Rapid filter changes trigger multiple concurrent requests without cancellation. If an earlier request completes after a later one, stale data overwrites current filtered results.

🔎 Proposed fix using AbortController
  useEffect(() => {
+   const controller = new AbortController()
    const fetchData = async (): Promise<void> => {
-     setIsLoading(true)
-     let url = 'api/dashboard'
-     if (selectedButtonIds.length > 0) {
-       url += `?buttonIds=${selectedButtonIds.join(',')}`
-     }
-     const res = await fetch(url, {
-       headers: {
-         Timezone: moment.tz.guess()
-       }
-     })
-     const json = await res.json()
-     setDashboardData(json)
-     setIsLoading(false)
+     try {
+       setIsLoading(true)
+       let url = 'api/dashboard'
+       if (selectedButtonIds.length > 0) {
+         url += `?buttonIds=${selectedButtonIds.join(',')}`
+       }
+       const res = await fetch(url, {
+         headers: {
+           Timezone: moment.tz.guess()
+         },
+         signal: controller.signal
+       })
+       const json = await res.json()
+       setDashboardData(json)
+     } catch (error: any) {
+       if (error.name !== 'AbortError') {
+         console.error(error)
+       }
+     } finally {
+       setIsLoading(false)
+     }
    }
-   fetchData().catch(console.error)
+   void fetchData()
    const savedActivePeriodString = loadStateFromCookie(COOKIE_NAMES.DASHBOARD_FILTER, undefined) as (PeriodString | undefined)
    if (savedActivePeriodString !== undefined) {
      setActivePeriodString(savedActivePeriodString)
    }
+   return () => {
+     controller.abort()
+   }
  }, [selectedButtonIds])
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1a64e6 and d5d5a44.

📒 Files selected for processing (1)
  • pages/dashboard/index.tsx (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
pages/dashboard/index.tsx (1)
redis/index.ts (1)
  • dashboardData (111-115)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run Tests
🔇 Additional comments (2)
pages/dashboard/index.tsx (2)

80-80: LGTM! Clean loading state addition.

The isLoading state is properly typed and initialized, following React best practices.


155-155: Loading condition logic is correct.

The conditional properly includes isLoading to show the loading spinner during data fetches. The logic correctly handles the initial mount (undefined data) and subsequent filter changes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
pages/dashboard/index.tsx (1)

121-136: Critical: Loading state not reset on fetch error.

If the fetch fails or JSON parsing throws an error, the catch handler on line 136 logs the error but doesn't call setIsLoading(false). This leaves the loading spinner visible indefinitely, blocking the user from interacting with the dashboard.

🔎 Recommended fix using try-finally
   useEffect(() => {
     const fetchData = async (): Promise<void> => {
       setIsLoading(true)
-      let url = 'api/dashboard'
-      if (selectedButtonIds.length > 0) {
-        url += `?buttonIds=${selectedButtonIds.join(',')}`
+      try {
+        let url = 'api/dashboard'
+        if (selectedButtonIds.length > 0) {
+          url += `?buttonIds=${selectedButtonIds.join(',')}`
+        }
+        const res = await fetch(url, {
+          headers: {
+            Timezone: moment.tz.guess()
+          }
+        })
+        const json = await res.json()
+        setDashboardData(json)
+      } catch (error) {
+        console.error(error)
+      } finally {
+        setIsLoading(false)
       }
-      const res = await fetch(url, {
-        headers: {
-          Timezone: moment.tz.guess()
-        }
-      })
-      const json = await res.json()
-      setDashboardData(json)
-      setIsLoading(false)
     }
-    fetchData().catch(console.error)
+    void fetchData()
     const savedActivePeriodString = loadStateFromCookie(COOKIE_NAMES.DASHBOARD_FILTER, undefined) as (PeriodString | undefined)
     if (savedActivePeriodString !== undefined) {
       setActivePeriodString(savedActivePeriodString)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5d5a44 and feb8793.

📒 Files selected for processing (3)
  • components/Loading/index.tsx (1 hunks)
  • components/Loading/loading.module.css (1 hunks)
  • pages/dashboard/index.tsx (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
pages/dashboard/index.tsx (2)
components/TopBar/index.tsx (1)
  • TopBar (10-36)
components/Loading/index.tsx (1)
  • Loading (10-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run Tests
🔇 Additional comments (4)
components/Loading/index.tsx (1)

23-23: LGTM! CSS class rename aligns with the new overlay approach.

The change from loading_container to loading_overlay correctly reflects the updated CSS implementation.

pages/dashboard/index.tsx (3)

80-80: LGTM! Appropriate state for tracking loading status.

The isLoading state is properly initialized and will control the Loading component visibility.


158-158: Good defensive coding with optional chaining.

Adding optional chaining for user?.stUser?.email prevents potential runtime errors if the user object structure is incomplete.

Also applies to: 166-166


167-167: LGTM! Loading spinner correctly shown during filtering.

The conditional rendering of the Loading component when isLoading is true properly implements the PR objective to show a loading spinner during filter operations.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
components/Loading/loading.module.css (1)

1-13: LGTM! Previous issues resolved.

Both major concerns from the previous review have been properly addressed:

  1. The semi-transparent backdrop (rgba(0, 0, 0, 0.4)) now prevents interaction with underlying content and improves spinner visibility
  2. The z-index conflict is resolved using a CSS custom property set to 999999, ensuring proper stacking above other components

The implementation follows best practices for fullscreen loading overlays.

Optional enhancement: cursor feedback

Consider adding cursor: wait; to provide visual feedback that the UI is processing:

 .loading_overlay {
   --z-overlay-loading: 999999;
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
   display: flex;
   justify-content: center;
   align-items: center;
   background-color: rgba(0, 0, 0, 0.4);
   z-index: var(--z-overlay-loading);
+  cursor: wait;
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between feb8793 and 1c79b5d.

📒 Files selected for processing (1)
  • components/Loading/loading.module.css (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run Tests

@Klakurka Klakurka merged commit e5b8973 into master Dec 23, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement (UI/UX/feature) New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants