Skip to content

Brain activity heatmap#2118

Merged
ragnep merged 8 commits intomainfrom
brain-activity-graph
Mar 17, 2026
Merged

Brain activity heatmap#2118
ragnep merged 8 commits intomainfrom
brain-activity-graph

Conversation

@ragnep
Copy link
Copy Markdown
Contributor

@ragnep ragnep commented Mar 16, 2026

Summary by CodeRabbit

  • New Features

    • Added a brain activity card to user profiles: calendar-style heatmap with month/day labels, total-drops header, accessible tooltips, loading/error/no-data states, automatic viewport snapping, and conditional display when a profile exists.
  • Tests

    • Added comprehensive tests covering loading, error, success, fallback identity, view-model logic, and viewport behavior.
  • Style

    • Minor background/opacity tweaks and spacing adjustments for improved contrast and layout.

Signed-off-by: ragnep <ragneinfo@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 16, 2026

📝 Walkthrough

Walkthrough

Adds a brain-activity feature: new React component and heatmap UI, view-model and heatmap-layout helpers, a viewport hook, a React Query hook for identity activity, integration into the user page, and comprehensive unit tests covering loading, error, success, and helper logic.

Changes

Cohort / File(s) Summary
Tests
__tests__/components/user/brain/UserPageBrainActivity.test.tsx, __tests__/components/user/brain/UserPageDrops.test.tsx, __tests__/components/user/brain/userPageBrainActivity.helpers.test.ts, __tests__/components/user/brain/userPageBrainActivityHeatmap.viewport.test.tsx
Added unit and integration tests for the brain activity component, integration on the user page, view-model helpers, and viewport snapping. Covers loading/error/success states, identity fallback, heatmap cells/intensities, month labels, and viewport snap behavior.
Query Key & Hook
components/react-query-wrapper/ReactQueryWrapper.tsx, hooks/useIdentityActivity.ts
Added IDENTITY_ACTIVITY query key and new useIdentityActivity hook that normalizes identity, fetches /identities/{identity}/activity, and configures React Query (staleTime 15m, gcTime 1h, refetchOnWindowFocus=false).
View Model Helpers
components/user/brain/userPageBrainActivity.helpers.ts
New view-model builder and exported types: validates/parses dates, normalizes counts, computes quantile-based intensity thresholds, pads weeks for calendar alignment, builds per-day cells and month labels, returns view model or null for invalid input.
Heatmap Layout Helpers
components/user/brain/userPageBrainActivityHeatmap.helpers.ts
New heatmap layout/tooltip utilities, layout constants (cell sizing, gaps, offsets), color/class mappings, tooltip formatting, cell class helpers, and month-label placement logic exported for heatmap rendering.
Heatmap Viewport Hook
components/user/brain/userPageBrainActivityHeatmap.viewport.ts
Added useHeatmapViewport(resetKey?) hook exposing viewportRef and viewportMetrics with RAF/ResizeObserver subscription, snapping to latest content, and sync-external-store integration.
Heatmap UI
components/user/brain/UserPageBrainActivityHeatmap.tsx
New interactive heatmap component and loading skeleton: weekday/month labels, scrollable 7-row grid, per-cell ARIA/tooltip anchors, tooltip content, and viewport integration. Exports loading and main heatmap components.
Component & Integration
components/user/brain/UserPageBrainActivity.tsx, components/user/brain/UserPageDrops.tsx
New default-exported UserPageBrainActivity component deriving identity, invoking useIdentityActivity, building the view model, and rendering loading/error/no-data/data states; integrated into UserPageDrops.
Minor Styling Tweaks
components/user/brain/UserPageBrainSidebarWaveItem.tsx, components/waves/drops/WaveDrop.tsx
Adjusted background/border opacity CSS classes (e.g., tw-bg-iron-950tw-bg-iron-950/80, border opacity change).

Sequence Diagram(s)

sequenceDiagram
    participant UI as UserPageBrainActivity
    participant HK as useIdentityActivity Hook
    participant API as Backend API
    participant VM as buildViewModel
    participant HM as Heatmap UI

    UI->>UI: derive identity from profile (handle or primary_wallet)
    UI->>HK: call hook with identity & enabled=true
    HK->>HK: normalize & validate identity
    HK->>API: GET /identities/{identity}/activity
    API-->>HK: return activity response
    HK-->>UI: deliver query state (loading | error | success)
    alt loading
        UI->>HM: render loading skeleton
    else error
        UI->>HM: render error message
    else success
        UI->>VM: build view model from response
        VM-->>UI: return view model (cells, totals, labels)
        UI->>HM: render heatmap with cells and tooltips
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • GelatoGenesis
  • analyticsflowee
  • prxt6529

Poem

🐰 Hops and pixels, cells align,
Weeks and months in tidy rows,
Queries fetch the little drops,
Heatmap hums where data flows,
A rabbit cheers — the UI glows!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Brain activity heatmap' directly and clearly summarizes the main feature being added across the changeset—a new heatmap component for visualizing user brain activity.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch brain-activity-graph
📝 Coding Plan
  • Generate coding plan for human review comments

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
Copy Markdown

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

🧹 Nitpick comments (2)
components/user/brain/UserPageBrainActivity.tsx (1)

157-165: Redundant identity check after hook call.

The identity variable is derived from getProfileWaveIdentity(profile) and is used to gate the useIdentityActivity hook via enabled: identity.length > 0. However, Line 163 checks if (!identity) which can never be true since getProfileWaveIdentity always returns a non-null string (from profile.handle ?? profile.query ?? profile.primary_wallet).

If the intention is to handle edge cases where all three properties are empty strings, the check should be if (identity.length === 0) for consistency with the enabled condition.

♻️ Suggested fix for consistency
-  if (!identity) {
+  if (identity.length === 0) {
     return null;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/user/brain/UserPageBrainActivity.tsx` around lines 157 - 165, The
`identity` value comes from getProfileWaveIdentity(profile) and is already used
to enable useIdentityActivity via enabled: identity.length > 0, so the
subsequent truthy check if (!identity) is incorrect; replace that check with if
(identity.length === 0) (or remove it and rely on the hook’s enabled flag) so
the condition matches the same empty-string semantics used for
useIdentityActivity; update the check near where identity is defined and where
useIdentityActivity is called to reference identity consistently.
__tests__/components/user/brain/UserPageDrops.test.tsx (1)

27-31: Test assertion may not match implementation behavior.

The test asserts that brain-activity-card is null when profile is null. This is correct since the component returns null when profile is falsy. However, the test name "hides Drops when no profile handle" is slightly misleading as it now also tests the BrainActivity component. Consider updating the test name to reflect the broader scope.

♻️ Suggested name update
-  it("hides Drops when no profile handle", () => {
+  it("renders nothing when profile is null", () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/components/user/brain/UserPageDrops.test.tsx` around lines 27 - 31,
The test name is misleading because it asserts that multiple elements
(brain-activity-card, drops, brain-sidebar) are absent when rendering
UserPageDrops with profile={null}; rename the test to reflect the broader
behavior (e.g., change the it(...) string to "hides Drops and BrainActivity
components when no profile handle" or similar) so it matches the assertions
against UserPageDrops, brain-activity-card, drops, and brain-sidebar.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hooks/useIdentityActivity.ts`:
- Around line 19-29: The query key lowercases normalizedIdentity but the API
call in commonApiFetch uses the original normalizedIdentity, causing a case
mismatch; update the endpoint construction in the useQuery queryFn so that
commonApiFetch's endpoint uses the lowercased normalizedIdentity (i.e., call
toLowerCase() on normalizedIdentity before encodeURIComponent) to match the
queryKey behavior used in useQuery/UserPageBrainActivityResponse and align with
the pattern in useIdentity.ts.

---

Nitpick comments:
In `@__tests__/components/user/brain/UserPageDrops.test.tsx`:
- Around line 27-31: The test name is misleading because it asserts that
multiple elements (brain-activity-card, drops, brain-sidebar) are absent when
rendering UserPageDrops with profile={null}; rename the test to reflect the
broader behavior (e.g., change the it(...) string to "hides Drops and
BrainActivity components when no profile handle" or similar) so it matches the
assertions against UserPageDrops, brain-activity-card, drops, and brain-sidebar.

In `@components/user/brain/UserPageBrainActivity.tsx`:
- Around line 157-165: The `identity` value comes from
getProfileWaveIdentity(profile) and is already used to enable
useIdentityActivity via enabled: identity.length > 0, so the subsequent truthy
check if (!identity) is incorrect; replace that check with if (identity.length
=== 0) (or remove it and rely on the hook’s enabled flag) so the condition
matches the same empty-string semantics used for useIdentityActivity; update the
check near where identity is defined and where useIdentityActivity is called to
reference identity consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8337c020-08aa-408b-b186-2fa887aaa9b6

📥 Commits

Reviewing files that changed from the base of the PR and between a10a937 and 147a68f.

📒 Files selected for processing (8)
  • __tests__/components/user/brain/UserPageBrainActivity.test.tsx
  • __tests__/components/user/brain/UserPageDrops.test.tsx
  • __tests__/components/user/brain/userPageBrainActivity.helpers.test.ts
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/user/brain/UserPageBrainActivity.tsx
  • components/user/brain/UserPageDrops.tsx
  • components/user/brain/userPageBrainActivity.helpers.ts
  • hooks/useIdentityActivity.ts

Comment thread hooks/useIdentityActivity.ts
ragnep added 2 commits March 16, 2026 11:44
Signed-off-by: ragnep <ragneinfo@gmail.com>
Copy link
Copy Markdown

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

🧹 Nitpick comments (3)
components/user/brain/UserPageBrainActivityHeatmap.tsx (2)

145-152: Simplify conditional expression.

The ternary can be simplified using the logical OR operator.

♻️ Proposed fix
       {DAY_LABELS.map((label, index) => (
         <div
           key={`${label || "empty"}-${index}`}
           className="tw-flex tw-items-center tw-justify-end"
         >
-          {label ? label : <span className="tw-opacity-0">Sun</span>}
+          {label || <span className="tw-opacity-0">Sun</span>}
         </div>
       ))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/user/brain/UserPageBrainActivityHeatmap.tsx` around lines 145 -
152, The map rendering of DAY_LABELS uses a ternary to show label or a
placeholder span; simplify by replacing the ternary with a logical OR so the
element renders label || <span className="tw-opacity-0">Sun</span>. Update the
map callback in UserPageBrainActivityHeatmap (the DAY_LABELS.map(...) block and
its key generation) to use the logical OR expression for the content while
keeping the same wrapper div and key.

430-454: Use .dataset instead of getAttribute() and add intensity upper bound check.

Per static analysis, prefer .dataset for data attribute access. Additionally, the intensity value is cast without verifying it's within the valid range (0-4).

♻️ Proposed fix
             render={({ activeAnchor }) => {
-              const countValue = Number(
-                activeAnchor?.getAttribute("data-tooltip-count")
-              );
-              const isoDate = activeAnchor?.getAttribute("data-tooltip-date");
+              const countValue = Number(activeAnchor?.dataset.tooltipCount);
+              const isoDate = activeAnchor?.dataset.tooltipDate;

               if (!isoDate || Number.isNaN(countValue)) {
                 return null;
               }

-              const intensityValue = Number(
-                activeAnchor?.getAttribute("data-tooltip-intensity")
-              );
+              const intensityValue = Number(activeAnchor?.dataset.tooltipIntensity);

               const indicatorCell: UserPageBrainActivityCell = {
                 key: "tooltip",
                 isoDate: isoDate,
                 count: countValue,
                 ariaLabel: null,
                 state: countValue > 0 ? "active" : "empty",
                 intensity:
-                  Number.isNaN(intensityValue) || intensityValue < 0
+                  Number.isNaN(intensityValue) || intensityValue < 0 || intensityValue > 4
                     ? 0
                     : (intensityValue as UserPageBrainActivityCell["intensity"]),
               };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/user/brain/UserPageBrainActivityHeatmap.tsx` around lines 430 -
454, In the render function where activeAnchor is read to build indicatorCell in
UserPageBrainActivityHeatmap.tsx, replace getAttribute(...) calls with the DOM
dataset access (e.g., activeAnchor?.dataset.tooltipCount, .tooltipDate,
.tooltipIntensity), parse the numeric values safely (Number or parseInt) and
guard against NaN, and enforce the intensity upper and lower bounds so intensity
becomes 0 if NaN or <0, and is clamped to 4 if >4 before casting to
UserPageBrainActivityCell["intensity"]; keep the same key/aria/state logic but
use the dataset-derived isoDate and countValue.
components/user/brain/userPageBrainActivity.helpers.ts (1)

236-242: Hardcoded period label may not reflect actual data range.

The periodLabel is hardcoded to "the last 12 months", but the actual period depends on date_samples.length. If the API returns a different range (e.g., 6 months or 18 months), this label would be misleading.

Consider deriving the label from the actual date range or documenting the API contract that guarantees 12 months of data.

🔧 Proposed fix to derive period label from actual data
+  const dayCount = normalizedSamples.length;
+  const monthCount = Math.round(dayCount / 30);
+  const periodLabel = monthCount <= 1 
+    ? "the last month" 
+    : `the last ${monthCount} months`;
+
   return {
-    periodLabel: "the last 12 months",
+    periodLabel,
     totalDrops,
     weekCount: cells.length / WEEKDAY_COUNT,
     monthLabels,
     cells,
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/user/brain/userPageBrainActivity.helpers.ts` around lines 236 -
242, The periodLabel is hardcoded to "the last 12 months" but should reflect the
actual range of the data; update the return to compute periodLabel dynamically
(using the date_samples array if available, or deriving months from cells and
WEEKDAY_COUNT) — e.g., build a human-readable label like "the last N months" or
a date range using the first/last dates from date_samples; modify the code
around periodLabel in the same function (referencing periodLabel, date_samples,
cells, WEEKDAY_COUNT, monthLabels) so the label is generated from the real data
instead of being hardcoded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@components/user/brain/userPageBrainActivity.helpers.ts`:
- Around line 236-242: The periodLabel is hardcoded to "the last 12 months" but
should reflect the actual range of the data; update the return to compute
periodLabel dynamically (using the date_samples array if available, or deriving
months from cells and WEEKDAY_COUNT) — e.g., build a human-readable label like
"the last N months" or a date range using the first/last dates from
date_samples; modify the code around periodLabel in the same function
(referencing periodLabel, date_samples, cells, WEEKDAY_COUNT, monthLabels) so
the label is generated from the real data instead of being hardcoded.

In `@components/user/brain/UserPageBrainActivityHeatmap.tsx`:
- Around line 145-152: The map rendering of DAY_LABELS uses a ternary to show
label or a placeholder span; simplify by replacing the ternary with a logical OR
so the element renders label || <span className="tw-opacity-0">Sun</span>.
Update the map callback in UserPageBrainActivityHeatmap (the DAY_LABELS.map(...)
block and its key generation) to use the logical OR expression for the content
while keeping the same wrapper div and key.
- Around line 430-454: In the render function where activeAnchor is read to
build indicatorCell in UserPageBrainActivityHeatmap.tsx, replace
getAttribute(...) calls with the DOM dataset access (e.g.,
activeAnchor?.dataset.tooltipCount, .tooltipDate, .tooltipIntensity), parse the
numeric values safely (Number or parseInt) and guard against NaN, and enforce
the intensity upper and lower bounds so intensity becomes 0 if NaN or <0, and is
clamped to 4 if >4 before casting to UserPageBrainActivityCell["intensity"];
keep the same key/aria/state logic but use the dataset-derived isoDate and
countValue.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 663e532a-6405-48cf-bd72-795115db8541

📥 Commits

Reviewing files that changed from the base of the PR and between 147a68f and e0761d0.

📒 Files selected for processing (7)
  • __tests__/components/user/brain/UserPageBrainActivity.test.tsx
  • __tests__/components/user/brain/userPageBrainActivity.helpers.test.ts
  • components/user/brain/UserPageBrainActivity.tsx
  • components/user/brain/UserPageBrainActivityHeatmap.tsx
  • components/user/brain/UserPageBrainSidebarWaveItem.tsx
  • components/user/brain/userPageBrainActivity.helpers.ts
  • components/waves/drops/WaveDrop.tsx
✅ Files skipped from review due to trivial changes (1)
  • components/waves/drops/WaveDrop.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/components/user/brain/UserPageBrainActivity.test.tsx
  • tests/components/user/brain/userPageBrainActivity.helpers.test.ts

ragnep added 3 commits March 17, 2026 09:45
Signed-off-by: ragnep <ragneinfo@gmail.com>
Signed-off-by: ragnep <ragneinfo@gmail.com>
Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
components/user/brain/userPageBrainActivityHeatmap.viewport.ts (1)

126-134: Consider potential stale closure in viewport subscription.

The subscribeToViewport callback reads viewportRef.current at subscription time, but since it has an empty dependency array, useSyncExternalStore won't resubscribe if the viewport element changes after initial mount (e.g., due to conditional rendering without a resetKey change).

If this component is always mounted when needed, this is likely fine. Otherwise, consider adding the viewport ref to the subscription logic or using a dependency that triggers resubscription when the element changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/user/brain/userPageBrainActivityHeatmap.viewport.ts` around lines
126 - 134, The subscription callback subscribeToViewport captures
viewportRef.current once because its dependency array is empty, so
useSyncExternalStore won't resubscribe if the actual DOM element changes; fix
this by making the subscription recreate when the element changes—either include
the current element in the callback deps (e.g., useCallback((onStoreChange)=>
subscribeToViewportMetrics(viewportRef.current, onStoreChange),
[viewportRef.current])) or introduce a resetKey that you update when the
viewport element mounts and include that key in the deps so useSyncExternalStore
(and subscribeToViewport) resubscribes; keep getViewportSnapshot and
EMPTY_VIEWPORT_METRICS the same.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@components/user/brain/userPageBrainActivityHeatmap.viewport.ts`:
- Around line 126-134: The subscription callback subscribeToViewport captures
viewportRef.current once because its dependency array is empty, so
useSyncExternalStore won't resubscribe if the actual DOM element changes; fix
this by making the subscription recreate when the element changes—either include
the current element in the callback deps (e.g., useCallback((onStoreChange)=>
subscribeToViewportMetrics(viewportRef.current, onStoreChange),
[viewportRef.current])) or introduce a resetKey that you update when the
viewport element mounts and include that key in the deps so useSyncExternalStore
(and subscribeToViewport) resubscribes; keep getViewportSnapshot and
EMPTY_VIEWPORT_METRICS the same.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5524375e-3da7-4c44-bae4-5c4c6fb310ca

📥 Commits

Reviewing files that changed from the base of the PR and between e0761d0 and 0644dcf.

📒 Files selected for processing (6)
  • __tests__/components/user/brain/UserPageBrainActivity.test.tsx
  • components/user/brain/UserPageBrainActivityHeatmap.tsx
  • components/user/brain/userPageBrainActivity.helpers.ts
  • components/user/brain/userPageBrainActivityHeatmap.helpers.ts
  • components/user/brain/userPageBrainActivityHeatmap.viewport.ts
  • hooks/useIdentityActivity.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/components/user/brain/UserPageBrainActivity.test.tsx

ragnep added 2 commits March 17, 2026 10:04
Signed-off-by: ragnep <ragneinfo@gmail.com>
Signed-off-by: ragnep <ragneinfo@gmail.com>
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

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

🧹 Nitpick comments (2)
__tests__/components/user/brain/userPageBrainActivityHeatmap.viewport.test.tsx (1)

4-34: LGTM! Consider expanding test coverage.

The test correctly validates the snap-to-latest behavior. Consider adding tests for:

  • Initial render without a viewport ref (returns EMPTY_VIEWPORT_METRICS)
  • Viewport metrics updates on scroll events
  • Cleanup behavior when component unmounts

These would increase confidence in edge cases but are optional given the current test validates the primary use case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@__tests__/components/user/brain/userPageBrainActivityHeatmap.viewport.test.tsx`
around lines 4 - 34, Add additional unit tests around useHeatmapViewport to
cover the suggested edge cases: add a test that calls the hook without assigning
viewportRef and asserts it returns EMPTY_VIEWPORT_METRICS; add a test that
simulates scroll events on the viewport element and asserts the viewport metrics
(scrollLeft/scrollTop/clientWidth/scrollHeight) update accordingly via the hook;
and add a test that unmounts the hook after assigning viewportRef and verifies
event listeners are cleaned up (e.g., no updates after unmount). Use the
existing test patterns around useHeatmapViewport and viewportRef to locate where
to add these cases.
components/user/brain/UserPageBrainActivity.tsx (1)

74-98: Consider logging when view model construction fails for debugging.

When buildUserPageBrainActivityViewModel returns null (due to invalid date_samples or unparseable last_date), the component silently displays "No activity in last 12 months." This is a reasonable fallback, but adding a debug log when activityQuery.data exists but activity is null would aid troubleshooting malformed API responses.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/user/brain/UserPageBrainActivity.tsx` around lines 74 - 98, When
activityQuery.data is present but
buildUserPageBrainActivityViewModel(activityQuery.data) returns null, add a
debug log to surface the malformed API response; check for activityQuery.data &&
activity == null and log the raw activityQuery.data (or specific problematic
fields like date_samples/last_date) along with a clear message. Insert this
check after the activity variable is constructed in UserPageBrainActivity.tsx
(referencing buildUserPageBrainActivityViewModel, activityQuery.data, and
activity) and use the existing logging mechanism (or console.debug/console.warn)
so developers can inspect the invalid payload during debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@__tests__/components/user/brain/userPageBrainActivityHeatmap.viewport.test.tsx`:
- Around line 4-34: Add additional unit tests around useHeatmapViewport to cover
the suggested edge cases: add a test that calls the hook without assigning
viewportRef and asserts it returns EMPTY_VIEWPORT_METRICS; add a test that
simulates scroll events on the viewport element and asserts the viewport metrics
(scrollLeft/scrollTop/clientWidth/scrollHeight) update accordingly via the hook;
and add a test that unmounts the hook after assigning viewportRef and verifies
event listeners are cleaned up (e.g., no updates after unmount). Use the
existing test patterns around useHeatmapViewport and viewportRef to locate where
to add these cases.

In `@components/user/brain/UserPageBrainActivity.tsx`:
- Around line 74-98: When activityQuery.data is present but
buildUserPageBrainActivityViewModel(activityQuery.data) returns null, add a
debug log to surface the malformed API response; check for activityQuery.data &&
activity == null and log the raw activityQuery.data (or specific problematic
fields like date_samples/last_date) along with a clear message. Insert this
check after the activity variable is constructed in UserPageBrainActivity.tsx
(referencing buildUserPageBrainActivityViewModel, activityQuery.data, and
activity) and use the existing logging mechanism (or console.debug/console.warn)
so developers can inspect the invalid payload during debugging.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f8f2ff2e-7a67-4425-90ce-a8c97ce3acf4

📥 Commits

Reviewing files that changed from the base of the PR and between 0644dcf and 017cb07.

📒 Files selected for processing (9)
  • __tests__/components/user/brain/UserPageBrainActivity.test.tsx
  • __tests__/components/user/brain/userPageBrainActivity.helpers.test.ts
  • __tests__/components/user/brain/userPageBrainActivityHeatmap.viewport.test.tsx
  • components/user/brain/UserPageBrainActivity.tsx
  • components/user/brain/UserPageBrainActivityHeatmap.tsx
  • components/user/brain/UserPageBrainSidebarWaveItem.tsx
  • components/user/brain/userPageBrainActivity.helpers.ts
  • components/user/brain/userPageBrainActivityHeatmap.helpers.ts
  • components/user/brain/userPageBrainActivityHeatmap.viewport.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/components/user/brain/userPageBrainActivity.helpers.test.ts
  • tests/components/user/brain/UserPageBrainActivity.test.tsx
  • components/user/brain/UserPageBrainActivityHeatmap.tsx

@ragnep ragnep merged commit bc3cd32 into main Mar 17, 2026
7 checks passed
@ragnep ragnep deleted the brain-activity-graph branch March 17, 2026 08:46
@coderabbitai coderabbitai Bot mentioned this pull request Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants