Skip to content

Returning users#736

Merged
goldflag merged 15 commits intomasterfrom
returning-users
Nov 29, 2025
Merged

Returning users#736
goldflag merged 15 commits intomasterfrom
returning-users

Conversation

@goldflag
Copy link
Copy Markdown
Collaborator

@goldflag goldflag commented Nov 29, 2025

Summary by CodeRabbit

  • New Features

    • Toggle to view total users or a breakdown of new vs returning users.
    • Charts render separate series for new and returning users with per-series colors, stacking, dashed overlays, and enhanced per-series tooltips showing diffs.
  • UI Improvements

    • Header toggle added (desktop & mobile) with tooltips and layout adjustments.
    • Chart tooltips reworked for single- and two-series displays; improved loader clipping and interaction.
  • API

    • Analytics overview responses now include new_users and returning_users.

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

- Implements a toggle to display user stats split into "New" and "Returning" users.
- Updates charts and tooltips to reflect new breakdown dynamically.
- Adjusts server query to calculate and return new user segmentation.
- Modify `getMaxValue` logic to correctly calculate totals when splitting "New" and "Returning" users.
- Enable stacked y-axis for charts when breakdown is active.
- Replace "pink" color variants with "accent" palette for consistency.
- Adjust chart and tooltip styles to reflect updated colors.
@vercel
Copy link
Copy Markdown

vercel bot commented Nov 29, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
rybbit Ready Ready Preview Comment Nov 29, 2025 5:34am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 29, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds server-side first-visit bucketing and per-bucket new_users/returning_users, extends the bucketed overview API shape, exposes a showUsersSplit store flag, and updates charts, previous-period chart, tooltip, and main UI to support optional new-vs-returning multi-series rendering and controls.

Changes

Cohort / File(s) Summary
Backend analytics query
server/src/api/analytics/getOverviewBucketed.ts
Adds UserFirstVisits and EventsWithFirstVisit subqueries; computes new_users and returning_users per bucket via uniqExactIf; groups on bucket_time; expands returned row shape to include sessions, pages_per_session, bounce_rate, session_duration, users, new_users, returning_users.
Client API response type
client/src/api/analytics/useGetOverviewBucketed.ts
Extends GetOverviewBucketedResponse element to include new_users: number and returning_users: number.
Store state
client/src/lib/store.ts
Adds showUsersSplit: boolean and setter setShowUsersSplit; initializes to false; includes in default/initial state and resets it in resetStore.
Main chart rendering
client/src/app/[site]/main/components/MainSection/Chart.tsx
Replaces single-series logic with per-series construction and SeriesConfig/SERIES_LABELS; introduces colorMap, stacked lines and optional dashed overlays, per-series tooltips, bucket-minute offsets, and conditional y-axis stacking when breakdown enabled.
Previous period chart
client/src/app/[site]/main/components/MainSection/PreviousChart.tsx
Builds seriesConfig and per-series chartData (supports new_users/returning_users when breakdown active), constructs theme-aware colorMap/previousColors, and enables y-axis stacking for breakdown.
Main section UI & controls
client/src/app/[site]/main/components/MainSection/MainSection.tsx
Adds toggle control (icons + tooltip) to flip showUsersSplit when selectedStat === "users"; adds getMaxValue helper to compute maxima across single stat or user breakdown; adjusts header layout and control placement.
Chart tooltip component
client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx
Adds new ChartSliceTooltip component and exported types (SeriesConfig, SeriesDataPoint, SeriesData); supports single- and two-series tooltip rendering, per-series color logic, formatted diffs, and handles dashed-series normalization.
Card loader styling
client/src/components/ui/card.tsx
Adds overflow-hidden and pointer-events-none to CardLoader container element.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant Store
    participant APIClient
    participant Server
    participant DB
    Browser->>Store: toggle showUsersSplit
    Store-->>Browser: updated flag
    Browser->>APIClient: request getOverviewBucketed()
    APIClient->>Server: HTTP request
    Server->>DB: run bucketed query (UserFirstVisits & EventsWithFirstVisit)
    DB-->>Server: bucketed rows (users, new_users, returning_users, ...)
    Server-->>APIClient: JSON response
    APIClient-->>Browser: typed response
    Browser->>Chart: build per-series data (based on showUsersSplit)
    Chart-->>Browser: render stacked/multi-series lines and tooltip
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay extra attention to:
    • server/src/api/analytics/getOverviewBucketed.ts — first-visit bucketing, join correctness, uniqExactIf conditions, and grouping by bucket_time.
    • client/src/app/[site]/main/components/MainSection/Chart.tsx and PreviousChart.tsx — per-series data assembly, stacking behavior, custom render layers, and color/gradient mapping.
    • Type alignment between server response and client/src/api/analytics/useGetOverviewBucketed.ts.
    • client/src/lib/store.ts — initialization and reset of showUsersSplit.
    • client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx — tooltip matching logic, formatting, and color handling.

Possibly related PRs

  • Returning users #736 — Modifies analytics pipeline and UI surfaces; directly related to adding new_users/returning_users and chart changes.
  • New vs Returning users toggle #722 — Implements the same new-vs-returning split and related backend/client/store/chart updates; strongly connected.

Poem

🐰 I hop through buckets, count the new and returning paws,

Two tiny lines trace welcomes and familiar applause—
A toggle, a tooltip, a curve in the sun,
New hops that begin, old hops that run—
I nibble the data, and the meadow is fun.

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 'Returning users' directly aligns with the PR's primary objective to add user breakdown functionality (new vs returning users) across the analytics and charting components.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 948bebd and 910e637.

📒 Files selected for processing (2)
  • client/src/app/[site]/main/components/MainSection/Chart.tsx (7 hunks)
  • client/src/app/[site]/main/components/MainSection/PreviousChart.tsx (4 hunks)

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
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: 4

🧹 Nitpick comments (5)
client/src/components/ui/card.tsx (1)

22-33: Unused ref and className props in CardLoader.

The component uses forwardRef and destructures className from props, but neither ref nor className is applied to the rendered element. This is inconsistent with other Card components in the file.

Consider applying the forwarded props:

 const CardLoader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
   ({ className, ...props }, ref) => {
     const { theme } = useTheme();
     const loaderColor = theme === "dark" ? "hsl(var(--neutral-400))" : "hsl(var(--neutral-200))";

     return (
-      <div className="mt-[-15px] absolute top-0 left-0 w-full overflow-hidden pointer-events-none">
+      <div ref={ref} className={cn("mt-[-15px] absolute top-0 left-0 w-full overflow-hidden pointer-events-none", className)} {...props}>
         <Zoomies size={1400} stroke="3" bg-opacity="0.1" speed="1.4" color={loaderColor} />
       </div>
     );
   }
 );
client/src/app/[site]/main/components/MainSection/PreviousChart.tsx (1)

77-80: Type assertion could be improved.

Using (e as any) loses type safety. Consider using a type-safe accessor or improving the typing.

-            return {
-              x: timestamp.toFormat("yyyy-MM-dd HH:mm:ss"),
-              y: (e as any)[series.dataKey] ?? 0,
-            };
+            return {
+              x: timestamp.toFormat("yyyy-MM-dd HH:mm:ss"),
+              y: e[series.dataKey] ?? 0,
+            };

This should work since dataKey is typed as keyof GetOverviewBucketedResponse[number] and e is from data?.data which has that type.

client/src/app/[site]/main/components/MainSection/MainSection.tsx (1)

102-120: Consider extracting duplicate toggle markup.

The Switch + label combination is duplicated for desktop (hidden on mobile) and mobile (hidden on desktop) breakpoints. This could be extracted into a small component.

const UserBreakdownToggle = ({ className }: { className?: string }) => (
  <div className={cn("flex items-center gap-2 text-xs text-muted-foreground", className)}>
    <Switch id="toggle-user-breakdown" checked={showUsersSplit} onCheckedChange={setShowUsersSplit} />
    <label className="cursor-pointer" htmlFor="toggle-user-breakdown">
      New vs returning
    </label>
  </div>
);

Then use <UserBreakdownToggle className="hidden md:flex" /> and <UserBreakdownToggle className="flex md:hidden justify-end" />.

client/src/app/[site]/main/components/MainSection/Chart.tsx (2)

211-214: Avoid as any casts for type-safe data access.

The as any casts bypass TypeScript's type checking. Since config.dataKey is typed as keyof GetOverviewBucketedResponse[number], direct indexing should work without casts.

Apply this diff to improve type safety:

           return {
             x: timestamp.toFormat("yyyy-MM-dd HH:mm:ss"),
-            y: (e as any)[config.dataKey] ?? 0,
-            previousY: previousEntry ? (previousEntry as any)[config.dataKey] : undefined,
+            y: e[config.dataKey] ?? 0,
+            previousY: previousEntry ? previousEntry[config.dataKey] : undefined,
             currentTime: timestamp,
             previousTime: previousTimestamp,
           };

226-228: Consider explicit typing for chart configuration arrays.

The arrays chartPropsData, chartPropsDefs, and chartPropsFill use any[] which loses type safety. While this is common with Nivo's flexible API, explicit types would improve maintainability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5997087 and fc2c1da.

📒 Files selected for processing (7)
  • client/src/api/analytics/useGetOverviewBucketed.ts (1 hunks)
  • client/src/app/[site]/main/components/MainSection/Chart.tsx (7 hunks)
  • client/src/app/[site]/main/components/MainSection/MainSection.tsx (4 hunks)
  • client/src/app/[site]/main/components/MainSection/PreviousChart.tsx (4 hunks)
  • client/src/components/ui/card.tsx (1 hunks)
  • client/src/lib/store.ts (3 hunks)
  • server/src/api/analytics/getOverviewBucketed.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript with strict typing throughout both client and server
Use camelCase for variables and functions
Use PascalCase for components and types
Group imports by external, then internal (alphabetical within groups)
Error handling: Use try/catch blocks with specific error types

Files:

  • client/src/api/analytics/useGetOverviewBucketed.ts
  • client/src/lib/store.ts
  • client/src/app/[site]/main/components/MainSection/PreviousChart.tsx
  • client/src/components/ui/card.tsx
  • client/src/app/[site]/main/components/MainSection/Chart.tsx
  • server/src/api/analytics/getOverviewBucketed.ts
  • client/src/app/[site]/main/components/MainSection/MainSection.tsx
client/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Client: React functional components with minimal useEffect and inline functions

Files:

  • client/src/api/analytics/useGetOverviewBucketed.ts
  • client/src/lib/store.ts
  • client/src/app/[site]/main/components/MainSection/PreviousChart.tsx
  • client/src/components/ui/card.tsx
  • client/src/app/[site]/main/components/MainSection/Chart.tsx
  • client/src/app/[site]/main/components/MainSection/MainSection.tsx
client/**/*.{css,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Dark mode is default theme

Files:

  • client/src/app/[site]/main/components/MainSection/PreviousChart.tsx
  • client/src/components/ui/card.tsx
  • client/src/app/[site]/main/components/MainSection/Chart.tsx
  • client/src/app/[site]/main/components/MainSection/MainSection.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-25T02:34:42.365Z
Learnt from: CR
Repo: rybbit-io/rybbit PR: 0
File: docs/CLAUDE.md:0-0
Timestamp: 2025-11-25T02:34:42.365Z
Learning: Applies to docs/src/components/Cards/**/*.{ts,tsx} : Place feature demonstration components in `src/components/Cards/` directory

Applied to files:

  • client/src/components/ui/card.tsx
📚 Learning: 2025-11-25T02:34:24.979Z
Learnt from: CR
Repo: rybbit-io/rybbit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T02:34:24.979Z
Learning: Frontend: Use Next.js with Turbopack, Tailwind CSS, Shadcn UI, Tanstack Query, Zustand, Luxon, Nivo, and react-hook-form

Applied to files:

  • client/src/app/[site]/main/components/MainSection/Chart.tsx
📚 Learning: 2025-11-25T02:34:42.365Z
Learnt from: CR
Repo: rybbit-io/rybbit PR: 0
File: docs/CLAUDE.md:0-0
Timestamp: 2025-11-25T02:34:42.365Z
Learning: Applies to docs/src/lib/trackAdEvent.ts : Track analytics events via `src/lib/trackAdEvent.ts` for monitoring tool usage and analytics

Applied to files:

  • server/src/api/analytics/getOverviewBucketed.ts
🧬 Code graph analysis (2)
client/src/app/[site]/main/components/MainSection/Chart.tsx (5)
client/src/lib/store.ts (2)
  • StatType (6-6)
  • useStore (26-153)
client/src/api/analytics/useGetOverviewBucketed.ts (1)
  • GetOverviewBucketedResponse (9-19)
client/src/lib/nivo.ts (1)
  • useNivoTheme (53-56)
client/src/components/charts/ChartTooltip.tsx (1)
  • ChartTooltip (7-13)
client/src/lib/dateTimeUtils.ts (1)
  • formatChartDateTime (157-178)
server/src/api/analytics/getOverviewBucketed.ts (1)
server/src/api/analytics/utils.ts (2)
  • TimeBucketToFn (315-325)
  • getTimeStatement (7-65)
⏰ 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). (2)
  • GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)
  • GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)
🔇 Additional comments (11)
client/src/api/analytics/useGetOverviewBucketed.ts (1)

9-19: LGTM!

The type extension correctly adds new_users and returning_users fields, aligning with the server-side API response shape.

client/src/lib/store.ts (1)

20-21: LGTM!

The showUsersSplit state and setter follow the existing store patterns, with proper initialization and reset handling.

Also applies to: 149-150

server/src/api/analytics/getOverviewBucketed.ts (2)

163-168: LGTM - User counting logic is correct.

The uniqExactIf conditions properly distinguish:

  • new_users: users whose first-ever visit falls in the current bucket
  • returning_users: users who visited before the current bucket

Note: users may slightly exceed new_users + returning_users if a user has no matching events in UserFirstVisits (due to the LEFT JOIN), but this edge case should be rare.


117-124: The performance concern is partially valid but the suggested solutions are not appropriate for ClickHouse's architecture.

The UserFirstVisits CTE intentionally scans all historical events for the site (as clarified by the comment "site-wide, no filters") to compute each user's actual first visit. This design is correct—the per-user first visit timestamp must be calculated across all time to be accurate.

However, the original suggestion to add an index or materialized view reflects a SQL Server/PostgreSQL perspective and doesn't align with ClickHouse optimization:

  1. Primary key optimization: The events table uses ORDER BY (site_id, timestamp), allowing efficient filtering by site_id. The site_id filter in UserFirstVisits leverages this automatically; no secondary index is needed.

  2. Partition pruning: Events are partitioned by month (PARTITION BY toYYYYMM(timestamp)), further optimizing full-table scans.

  3. Materialized view caching: Unsuitable here because first-visit data changes continuously as new users arrive. A materialized view would require constant invalidation, adding complexity without benefit. (The codebase does use a materialized view for hourly_events_by_site_mv in cloud mode, but that's for time-series aggregation, not user tracking.)

  4. Query structure is sound: The CTE is computed once and reused efficiently. Time filters are applied in EventsWithFirstVisit, limiting the join to relevant events.

The query design is already well-suited to ClickHouse. No action is required unless query execution logs indicate this specific CTE is a bottleneck.

client/src/app/[site]/main/components/MainSection/Chart.tsx (7)

122-138: LGTM!

The SERIES_LABELS mapping and SeriesConfig type are well-structured. The type correctly extends StatType with the new user breakdown keys, and SeriesConfig properly types the series metadata.


149-178: LGTM!

The conditional logic for showUserBreakdown is clear, and the seriesConfig array construction properly handles both single-series and dual-series modes with appropriate color assignments.


257-273: LGTM!

The StackedLines layer correctly handles both stacked and non-stacked rendering by falling back from yStacked to y. The conditional slicing when displayDashed is true properly avoids double-rendering the final segment.


275-300: LGTM!

The DashedOverlay layer correctly renders dashed segments for only the last two points of each series. The slightly thicker stroke (3 vs 2) helps distinguish the dashed projection segment visually.


318-318: LGTM!

Correctly enables stacking only when showUserBreakdown is true, which ensures the new/returning users areas stack properly while maintaining single-series behavior otherwise.


442-470: LGTM!

The dual-series tooltip correctly matches data points by timestamp across both series and gracefully handles missing matches with the filter(Boolean) cleanup.


515-527: LGTM!

The layers array correctly positions the custom StackedLines and conditional DashedOverlay layers in the rendering order. Nivo handles null values in the layers array gracefully.

Comment on lines +398 to +403
color: diffPercentage > 0 ? "hsl(var(--green-400))" : "hsl(var(--red-400))",
}}
>
{diffPercentage > 0 ? "+" : ""}
{diffPercentage.toFixed(2)}%
</div>
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.

⚠️ Potential issue | 🟡 Minor

Zero percentage change displays as red (negative).

When diffPercentage is exactly 0, the condition diffPercentage > 0 is false, causing zero change to display with the red "negative" color. This may confuse users since zero change is neutral.

Apply this diff to handle zero change with a neutral or green color:

                   style={{
-                    color: diffPercentage > 0 ? "hsl(var(--green-400))" : "hsl(var(--red-400))",
+                    color: diffPercentage >= 0 ? "hsl(var(--green-400))" : "hsl(var(--red-400))",
                   }}

The same fix should be applied at line 483 for consistency in the dual-series tooltip.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
color: diffPercentage > 0 ? "hsl(var(--green-400))" : "hsl(var(--red-400))",
}}
>
{diffPercentage > 0 ? "+" : ""}
{diffPercentage.toFixed(2)}%
</div>
color: diffPercentage >= 0 ? "hsl(var(--green-400))" : "hsl(var(--red-400))",
}}
>
{diffPercentage > 0 ? "+" : ""}
{diffPercentage.toFixed(2)}%
</div>
🤖 Prompt for AI Agents
In client/src/app/[site]/main/components/MainSection/Chart.tsx around lines 398
to 403 (and similarly at line 483 for the dual-series tooltip), the color logic
uses diffPercentage > 0 which treats 0 as negative; change the condition to
treat zero as neutral/positive by using diffPercentage >= 0 (or explicitly check
for === 0 and map to a neutral/green color) so that 0% renders with the
neutral/green color; update both tooltip render sites to use the same comparison
and keep the displayed sign logic unchanged (only show "+" for >0).

Comment on lines +122 to +133
{showUserBreakdown && (
<div className="flex items-center justify-center gap-4 text-xs text-muted-foreground px-2 md:px-0 mt-2">
<div className="flex items-center gap-2">
<span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: "hsl(var(--dataviz))" }} />
<span>New users</span>
</div>
<div className="flex items-center gap-2">
<span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: "hsl(var(--accent-200))" }} />
<span>Returning users</span>
</div>
</div>
)}
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.

⚠️ Potential issue | 🟡 Minor

Legend color may not match chart in dark mode.

The returning users legend uses hsl(var(--accent-200)), but PreviousChart.tsx uses hsl(var(--accent-800) / 0.35) in dark mode. This could cause a visual mismatch between the legend indicator and the actual chart series.

Consider using CSS variables or a shared constant to ensure consistency:

-                <span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: "hsl(var(--accent-200))" }} />
+                <span className="h-2.5 w-2.5 rounded-full bg-accent-200 dark:bg-accent-800" />

Or define the colors in a shared location to maintain consistency across components.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{showUserBreakdown && (
<div className="flex items-center justify-center gap-4 text-xs text-muted-foreground px-2 md:px-0 mt-2">
<div className="flex items-center gap-2">
<span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: "hsl(var(--dataviz))" }} />
<span>New users</span>
</div>
<div className="flex items-center gap-2">
<span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: "hsl(var(--accent-200))" }} />
<span>Returning users</span>
</div>
</div>
)}
{showUserBreakdown && (
<div className="flex items-center justify-center gap-4 text-xs text-muted-foreground px-2 md:px-0 mt-2">
<div className="flex items-center gap-2">
<span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: "hsl(var(--dataviz))" }} />
<span>New users</span>
</div>
<div className="flex items-center gap-2">
<span className="h-2.5 w-2.5 rounded-full bg-accent-200 dark:bg-accent-800" />
<span>Returning users</span>
</div>
</div>
)}
🤖 Prompt for AI Agents
In client/src/app/[site]/main/components/MainSection/MainSection.tsx around
lines 122 to 133, the "Returning users" legend uses a hardcoded
hsl(var(--accent-200)) which can differ from the chart's dark-mode color
(PreviousChart.tsx uses hsl(var(--accent-800) / 0.35)); update the legend to use
the same shared color source as the chart (either the same CSS variable the
chart uses or a new shared CSS variable/constant used by both components) so the
legend color mirrors the chart in all themes — replace the hardcoded hsl(...)
with that shared variable/constant and ensure the variable accounts for opacity
or theme-specific variants as the chart does.

UserFirstVisits AS (
SELECT
user_id,
toDateTime(${TimeBucketToFn[bucket]}(toTimeZone(min(timestamp), ${SqlString.escape(time_zone)}))) AS first_visit_bucket
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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, check file size and read the getOverviewBucketed.ts file
wc -l server/src/api/analytics/getOverviewBucketed.ts

Repository: rybbit-io/rybbit

Length of output: 111


🏁 Script executed:

#!/bin/bash
# Read the entire file to understand time_zone handling and past_minutes logic
cat -n server/src/api/analytics/getOverviewBucketed.ts

Repository: rybbit-io/rybbit

Length of output: 9477


🏁 Script executed:

#!/bin/bash
# Check the query-validation.js file to understand time_zone validation
cat -n server/src/api/analytics/query-validation.js | head -150

Repository: rybbit-io/rybbit

Length of output: 136


🏁 Script executed:

#!/bin/bash
# Find query-validation file
fd -t f "query-validation" server/src/api/analytics/

Repository: rybbit-io/rybbit

Length of output: 104


🏁 Script executed:

#!/bin/bash
# Read query-validation.ts to check time_zone validation
wc -l server/src/api/analytics/query-validation.ts
cat -n server/src/api/analytics/query-validation.ts

Repository: rybbit-io/rybbit

Length of output: 9780


🏁 Script executed:

#!/bin/bash
# Check how sqlstring handles undefined
npm list sqlstring 2>/dev/null | head -5
# Let's also check what getQuery does with time_zone in past_minutes mode

Repository: rybbit-io/rybbit

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Check the flow: req.query -> getQuery -> getOverviewBucketed function
# Look at how time_zone is passed and used in the main getQuery function
rg -n "time_zone" server/src/api/analytics/getOverviewBucketed.ts -B2 -A2

Repository: rybbit-io/rybbit

Length of output: 2236


🏁 Script executed:

#!/bin/bash
# Check if there's any validation or error handling in the ClickHouse query execution
rg -n "query_params\|time_zone" server/src/api/analytics/getOverviewBucketed.ts

Repository: rybbit-io/rybbit

Length of output: 42


Based on my verification, I've confirmed the issue. Let me create the rewritten review comment:


Fix time_zone handling in past-minutes mode.

When past_minutes_start/past_minutes_end are used without date range parameters, time_zone is optional per the validation schema (line 136-138 in query-validation.ts). However, the query always uses time_zone at lines 120, 128, and 152, where SqlString.escape(time_zone) will produce 'NULL' if undefined. This causes toTimeZone(timestamp, NULL) to fail in ClickHouse. Either make time_zone required for all query paths or conditionally exclude timezone-dependent calculations when in past-minutes mode.

Also applies to: 128-128, 152-152

- Simplify Chart component by removing unused imports and adjusting type definitions.
- Update StackedLines and DashedOverlay functions for better type handling.
- Enhance MainSection layout by replacing the toggle switch with a button for user breakdown filtering.
- Improve max value calculation in getMaxValue function to ensure proper number handling.
- Reverse the order of rows in the Chart tooltip for better data presentation.
- Replace the filter icon with a user icon toggle in MainSection, adding a tooltip for clarity on user breakdown options.
Copy link
Copy Markdown
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

♻️ Duplicate comments (1)
client/src/app/[site]/main/components/MainSection/Chart.tsx (1)

382-394: Zero percentage change displays as red (negative).

When diffPercentage is exactly 0, the condition diffPercentage > 0 evaluates to false, causing zero change to render with the red negative color. Zero change is neutral and should not appear negative.

Apply this diff to treat zero as neutral (green):

                 style={{
-                  color: diffPercentage > 0 ? "hsl(var(--green-400))" : "hsl(var(--red-400))",
+                  color: diffPercentage >= 0 ? "hsl(var(--green-400))" : "hsl(var(--red-400))",
                 }}

Note: The same issue exists in the dual-series tooltip at line 473 and should be fixed consistently.

🧹 Nitpick comments (1)
client/src/app/[site]/main/components/MainSection/Chart.tsx (1)

196-222: Type safety: as any bypasses TypeScript checks.

The data point construction uses as any to access dynamic properties (lines 213-214). While this works, it bypasses type safety.

Consider using a type guard or refining the typing of GetOverviewBucketedResponse to ensure type safety.

Otherwise, the series data construction logic is correct, properly handling timestamp parsing, future date filtering, and previous period alignment.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b42e885 and 5d748bf.

📒 Files selected for processing (2)
  • client/src/app/[site]/main/components/MainSection/Chart.tsx (7 hunks)
  • client/src/app/[site]/main/components/MainSection/MainSection.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript with strict typing throughout both client and server
Use camelCase for variables and functions
Use PascalCase for components and types
Group imports by external, then internal (alphabetical within groups)
Error handling: Use try/catch blocks with specific error types

Files:

  • client/src/app/[site]/main/components/MainSection/MainSection.tsx
  • client/src/app/[site]/main/components/MainSection/Chart.tsx
client/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Client: React functional components with minimal useEffect and inline functions

Files:

  • client/src/app/[site]/main/components/MainSection/MainSection.tsx
  • client/src/app/[site]/main/components/MainSection/Chart.tsx
client/**/*.{css,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Dark mode is default theme

Files:

  • client/src/app/[site]/main/components/MainSection/MainSection.tsx
  • client/src/app/[site]/main/components/MainSection/Chart.tsx
🧠 Learnings (4)
📚 Learning: 2025-11-25T02:34:59.895Z
Learnt from: CR
Repo: rybbit-io/rybbit PR: 0
File: docs/src/app/(home)/tools/CLAUDE.md:0-0
Timestamp: 2025-11-25T02:34:59.895Z
Learning: Applies to docs/src/app/(home)/tools/**/tools/**/*.{tsx,ts} : Use primary color `bg-emerald-600 hover:bg-emerald-500`, success `bg-emerald-50 dark:bg-emerald-900/20 border-emerald-200 dark:border-emerald-800`, error `bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800` for tool components

Applied to files:

  • client/src/app/[site]/main/components/MainSection/MainSection.tsx
  • client/src/app/[site]/main/components/MainSection/Chart.tsx
📚 Learning: 2025-11-25T02:34:24.979Z
Learnt from: CR
Repo: rybbit-io/rybbit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T02:34:24.979Z
Learning: Frontend: Use Next.js with Turbopack, Tailwind CSS, Shadcn UI, Tanstack Query, Zustand, Luxon, Nivo, and react-hook-form

Applied to files:

  • client/src/app/[site]/main/components/MainSection/MainSection.tsx
📚 Learning: 2025-11-25T02:34:24.979Z
Learnt from: CR
Repo: rybbit-io/rybbit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T02:34:24.979Z
Learning: Applies to client/**/*.{ts,tsx} : Client: React functional components with minimal useEffect and inline functions

Applied to files:

  • client/src/app/[site]/main/components/MainSection/MainSection.tsx
📚 Learning: 2025-11-25T02:34:59.895Z
Learnt from: CR
Repo: rybbit-io/rybbit PR: 0
File: docs/src/app/(home)/tools/CLAUDE.md:0-0
Timestamp: 2025-11-25T02:34:59.895Z
Learning: Applies to docs/src/app/(home)/tools/**/tools/**/*.tsx : Use `useState<string | null>(null)` for error state and display error in `bg-red-50` container when present

Applied to files:

  • client/src/app/[site]/main/components/MainSection/Chart.tsx
🧬 Code graph analysis (1)
client/src/app/[site]/main/components/MainSection/MainSection.tsx (3)
client/src/lib/store.ts (1)
  • useStore (26-153)
client/src/components/RybbitLogo.tsx (1)
  • RybbitLogo (3-5)
client/src/components/BucketSelection.tsx (1)
  • BucketSelection (155-166)
⏰ 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). (2)
  • GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)
  • GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)
🔇 Additional comments (11)
client/src/app/[site]/main/components/MainSection/MainSection.tsx (4)

3-3: LGTM!

The new imports are correctly structured and all are properly utilized in the component.

Also applies to: 8-11


36-37: LGTM!

The showUserBreakdown logic correctly derives from the store's showUsersSplit flag and the selected stat, ensuring the breakdown is only displayed when appropriate.


62-73: LGTM!

The activeKeys and getMaxValue logic correctly handles both single-series and dual-series (user breakdown) modes. The max calculation properly accounts for stacked values when showing the user breakdown.


83-117: LGTM!

The UI updates provide a clean layout with:

  • Logo/link on the left
  • Centered stat label for visual balance
  • Toggle and bucket selection on the right

The toggle button correctly shows/hides the user breakdown with appropriate icons and tooltip text.

client/src/app/[site]/main/components/MainSection/Chart.tsx (7)

8-8: LGTM!

The theme awareness via useTheme, the comprehensive SERIES_LABELS constant, the SeriesConfig type definition, and the showUserBreakdown logic are all correctly implemented.

Also applies to: 122-154


156-178: LGTM!

The seriesConfig correctly switches between single-series and dual-series modes, with appropriate colors and labels for each series.


224-255: LGTM!

The chart configuration correctly builds per-series data, color mappings, and gradient definitions for both single and multi-series modes.


257-290: LGTM!

The custom StackedLines and DashedOverlay layers correctly implement stacked line rendering with optional dashed segments for incomplete data.


308-308: LGTM!

The yScale.stacked property correctly enables stacking for user breakdown mode, and the colors function properly resolves series colors from the color map.

Also applies to: 356-356


419-504: Dual-series tooltip implementation is comprehensive.

The tooltip correctly:

  • Handles per-series data matching by timestamp
  • Uses theme-aware colors for previous period indicators
  • Renders rows in reversed order for visual hierarchy
  • Formats values appropriately

However, the same zero percentage issue exists at line 473 (as noted in the previous comment about line 388).


505-517: LGTM!

The layers configuration correctly positions custom rendering layers and conditionally includes the dashed overlay based on display conditions.

…set calculations

- Introduce utility functions for bucket minute offsets to simplify time calculations.
- Replace inline tooltip rendering with a dedicated ChartSliceTooltip component for better code organization.
- Remove redundant code and streamline the getMax and getMin functions for clarity.
Copy link
Copy Markdown
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: 4

♻️ Duplicate comments (1)
client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx (1)

78-90: Zero percentage change displays as red (negative).

When diffPercentage is exactly 0, the condition diffPercentage > 0 evaluates to false, causing zero change to display with red (negative) color. Zero change should be treated as neutral or positive.

Apply this diff to handle zero change with green/neutral color:

           style={{
-            color: diffPercentage > 0 ? "hsl(var(--green-400))" : "hsl(var(--red-400))",
+            color: diffPercentage >= 0 ? "hsl(var(--green-400))" : "hsl(var(--red-400))",
           }}

The same fix should be applied at lines 166-171 for consistency in the two-series tooltip.

🧹 Nitpick comments (2)
client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx (1)

118-126: Extract inline helper outside component.

The previousColorFor function is defined inside the component body. Per coding guidelines preferring minimal inline functions, consider extracting it as a module-level constant or helper.

const getPreviousColorFor = (seriesId: string, resolvedTheme: string | undefined): string => {
  if (seriesId === "new_users") {
    return "hsl(var(--dataviz) / 0.28)";
  }
  if (seriesId === "returning_users") {
    return resolvedTheme === "dark" ? "hsl(var(--accent-800) / 0.35)" : "hsl(var(--accent-200) / 0.38)";
  }
  return resolvedTheme === "dark" ? "hsl(var(--neutral-700))" : "hsl(var(--neutral-200))";
};

Then use: previousColor: getPreviousColorFor(series.id, resolvedTheme) at line 153.

client/src/app/[site]/main/components/MainSection/Chart.tsx (1)

158-180: Consider centralizing series colors.

Series colors are hardcoded in the configuration. For maintainability and theme consistency, consider defining a SERIES_COLORS constant or deriving colors from the theme.

Example:

const SERIES_COLORS = {
  new_users: "hsl(var(--dataviz))",
  returning_users: "hsl(var(--accent-200))",
  default: "hsl(var(--dataviz))",
} as const;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d748bf and 948bebd.

📒 Files selected for processing (2)
  • client/src/app/[site]/main/components/MainSection/Chart.tsx (7 hunks)
  • client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript with strict typing throughout both client and server
Use camelCase for variables and functions
Use PascalCase for components and types
Group imports by external, then internal (alphabetical within groups)
Error handling: Use try/catch blocks with specific error types

Files:

  • client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx
  • client/src/app/[site]/main/components/MainSection/Chart.tsx
client/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Client: React functional components with minimal useEffect and inline functions

Files:

  • client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx
  • client/src/app/[site]/main/components/MainSection/Chart.tsx
client/**/*.{css,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Dark mode is default theme

Files:

  • client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx
  • client/src/app/[site]/main/components/MainSection/Chart.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-25T02:34:24.979Z
Learnt from: CR
Repo: rybbit-io/rybbit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T02:34:24.979Z
Learning: Applies to client/**/*.{ts,tsx} : Client: React functional components with minimal useEffect and inline functions

Applied to files:

  • client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx
📚 Learning: 2025-11-25T02:34:59.895Z
Learnt from: CR
Repo: rybbit-io/rybbit PR: 0
File: docs/src/app/(home)/tools/CLAUDE.md:0-0
Timestamp: 2025-11-25T02:34:59.895Z
Learning: Applies to docs/src/app/(home)/tools/**/tools/**/*.{tsx,ts} : Use primary color `bg-emerald-600 hover:bg-emerald-500`, success `bg-emerald-50 dark:bg-emerald-900/20 border-emerald-200 dark:border-emerald-800`, error `bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800` for tool components

Applied to files:

  • client/src/app/[site]/main/components/MainSection/Chart.tsx
📚 Learning: 2025-11-25T02:34:59.895Z
Learnt from: CR
Repo: rybbit-io/rybbit PR: 0
File: docs/src/app/(home)/tools/CLAUDE.md:0-0
Timestamp: 2025-11-25T02:34:59.895Z
Learning: Applies to docs/src/app/(home)/tools/**/tools/**/*.tsx : Use `useState<string | null>(null)` for error state and display error in `bg-red-50` container when present

Applied to files:

  • client/src/app/[site]/main/components/MainSection/Chart.tsx
🧬 Code graph analysis (2)
client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx (4)
client/src/lib/store.ts (1)
  • StatType (6-6)
client/src/lib/utils.ts (1)
  • formatSecondsAsMinutesAndSeconds (14-27)
client/src/components/charts/ChartTooltip.tsx (1)
  • ChartTooltip (7-13)
client/src/lib/dateTimeUtils.ts (1)
  • formatChartDateTime (157-178)
client/src/app/[site]/main/components/MainSection/Chart.tsx (4)
shared/src/time.ts (1)
  • TimeBucket (1-10)
client/src/lib/store.ts (1)
  • StatType (6-6)
client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx (3)
  • SeriesConfig (19-24)
  • ChartSliceTooltip (49-200)
  • SeriesData (34-36)
client/src/api/analytics/useGetOverviewBucketed.ts (1)
  • GetOverviewBucketedResponse (9-19)
⏰ 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). (2)
  • GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)
  • GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)
🔇 Additional comments (4)
client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx (1)

1-17: LGTM!

Import organization follows guidelines (external, then internal), and the formatTooltipValue helper correctly handles the three stat formatting cases with proper typing.

client/src/app/[site]/main/components/MainSection/Chart.tsx (3)

5-23: LGTM!

New imports are properly organized, and the BUCKET_MINUTES_OFFSET constant with its helper function provide clear, type-safe bucket offset logic. The SERIES_LABELS constant appropriately extends stat labels to include user breakdown types.

Also applies to: 85-94


198-224: LGTM!

Series data construction properly handles timestamp parsing, filters future dates, and aligns current/previous data with correct offset calculation. The logic is sound and type-safe within the constraints of the response type.


228-260: LGTM!

Chart rendering updates are well-structured:

  • Color map provides consistent series-to-color resolution
  • Stacking is correctly enabled for user breakdown mode
  • Layer factories cleanly separate rendering concerns
  • ChartSliceTooltip integration passes all necessary props including theme context
  • Conditional dashed overlay rendering maintains clean composition

Also applies to: 278-278, 326-326, 332-351

Comment on lines +96 to +101
type SeriesConfig = {
id: string;
dataKey: keyof GetOverviewBucketedResponse[number];
label: string;
color: string;
};
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.

🛠️ Refactor suggestion | 🟠 Major

Remove duplicate type definition.

SeriesConfig is already exported from ChartSliceTooltip.tsx (lines 19-24). Redefining it here violates DRY principles and creates maintenance burden.

Apply this diff:

-type SeriesConfig = {
-  id: string;
-  dataKey: keyof GetOverviewBucketedResponse[number];
-  label: string;
-  color: string;
-};
+import { SeriesConfig as BaseSeriesConfig } from "./ChartSliceTooltip";
+
+type SeriesConfig = BaseSeriesConfig & {
+  dataKey: keyof GetOverviewBucketedResponse[number];
+};

Or define SeriesConfig once in a shared types file and import it in both components.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In client/src/app/[site]/main/components/MainSection/Chart.tsx around lines 96
to 101, the local SeriesConfig type duplicates the exported SeriesConfig from
ChartSliceTooltip.tsx; remove this local definition and instead import the
shared SeriesConfig type (either directly from ChartSliceTooltip.tsx or from a
new shared types file you create under the same directory), update the import
statement at the top of Chart.tsx to reference the shared type, and ensure any
export/usage sites are adjusted to use the single shared type so there are no
remaining duplicate definitions.

Comment on lines +103 to +140
const createStackedLines =
(displayDashed: boolean) =>
({ series, lineGenerator, xScale, yScale }: any) => {
return series.map(({ id, data, color }: any) => {
const usableData = displayDashed && data.length >= 2 ? data.slice(0, -1) : data;
const coords = usableData.map((d: any) => {
const stackedY = d.data.yStacked ?? d.data.y;
return { x: xScale(d.data.x), y: yScale(stackedY) };
});
const path = lineGenerator(coords);
if (!path) return null;
return <path key={`${id}-solid`} d={path} fill="none" stroke={color} style={{ strokeWidth: 2 }} />;
});
};

const createDashedOverlay =
(displayDashed: boolean) =>
({ series, lineGenerator, xScale, yScale }: any) => {
return series.map(({ id, data, color }: any) => {
if (!displayDashed || data.length < 2) return null;
const lastTwo = data.slice(-2);
const coords = lastTwo.map((d: any) => {
const stackedY = d.data.yStacked ?? d.data.y;
return { x: xScale(d.data.x), y: yScale(stackedY) };
});
const path = lineGenerator(coords);
if (!path) return null;
return (
<path
key={`${id}-dashed`}
d={path}
fill="none"
stroke={color}
style={{ strokeDasharray: "3, 6", strokeWidth: 3 }}
/>
);
});
};
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.

⚠️ Potential issue | 🟡 Minor

Add proper types for layer factory parameters.

Both factory functions use any for their parameters, violating strict TypeScript guidelines. Nivo provides CustomLayerProps type for custom layers.

Apply this diff:

+import { CustomLayerProps } from "@nivo/line";
+
 const createStackedLines =
   (displayDashed: boolean) =>
-  ({ series, lineGenerator, xScale, yScale }: any) => {
+  ({ series, lineGenerator, xScale, yScale }: CustomLayerProps) => {
     return series.map(({ id, data, color }: any) => {

Similar fix for createDashedOverlay at line 120. Consider also typing the destructured properties within map.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In client/src/app/[site]/main/components/MainSection/Chart.tsx around lines 103
to 140, the two layer factory functions use `any` for their parameters and map
destructured items, violating strict TypeScript rules; change the function
parameter types from `any` to Nivo's `CustomLayerProps<any>` (or
`CustomLayerProps<MyDatum>` if you have a datum type) for both
`createStackedLines` and `createDashedOverlay`, and type the mapped series items
(e.g., `{ id: string; data: Array<{ data: MyDatum }>; color: string }`) and
inner datum (`d: { data: MyDatum }`) so `xScale`, `yScale`, and `lineGenerator`
usages are typed correctly; ensure imports include `CustomLayerProps` from
`@nivo/line` and adjust generics to reflect your chart datum type.

Comment on lines +38 to +47
type ChartSliceTooltipProps = {
slice: any;
showUserBreakdown: boolean;
colorMap: Record<string, string>;
seriesConfig: SeriesConfig[];
seriesData: SeriesData[];
bucket: TimeBucket;
selectedStat: StatType;
resolvedTheme: string | undefined;
};
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.

⚠️ Potential issue | 🟡 Minor

Replace any type with proper Nivo type.

The slice: any violates the strict TypeScript typing guideline. Nivo provides typed props for slice tooltips.

Apply this diff:

+import { SliceSvgProps } from "@nivo/line";
+
 type ChartSliceTooltipProps = {
-  slice: any;
+  slice: SliceSvgProps["slice"];
   showUserBreakdown: boolean;
   colorMap: Record<string, string>;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type ChartSliceTooltipProps = {
slice: any;
showUserBreakdown: boolean;
colorMap: Record<string, string>;
seriesConfig: SeriesConfig[];
seriesData: SeriesData[];
bucket: TimeBucket;
selectedStat: StatType;
resolvedTheme: string | undefined;
};
import { SliceTooltipProps } from "@nivo/line";
type ChartSliceTooltipProps = {
slice: SliceTooltipProps["slice"];
showUserBreakdown: boolean;
colorMap: Record<string, string>;
seriesConfig: SeriesConfig[];
seriesData: SeriesData[];
bucket: TimeBucket;
selectedStat: StatType;
resolvedTheme: string | undefined;
};
🤖 Prompt for AI Agents
In client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx
around lines 38 to 47, replace the loose slice: any with the proper Nivo slice
tooltip type: import the SliceTooltipProps (or equivalent slice tooltip type)
from the Nivo package you use (e.g. '@nivo/line' or '@nivo/area') and type the
slice property using that type (use the generic to match your
SeriesData/SeriesConfig types if needed, e.g.
SliceTooltipProps<SeriesData>['slice']). Update the props type accordingly and
remove the any.


return (
<ChartTooltip>
{rows.toReversed().map((row: any, idx: number) => (
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for Next.js config file
fd -t f 'next\.config' --exec cat {}

Repository: rybbit-io/rybbit

Length of output: 1172


🏁 Script executed:

#!/bin/bash
# Find and read tsconfig.json
fd -t f 'tsconfig\.json' -x cat {}

Repository: rybbit-io/rybbit

Length of output: 3741


🏁 Script executed:

#!/bin/bash
# Find tsconfig in client directory
fd -t f 'tsconfig' client/ 2>/dev/null | head -5

# Get file info
wc -l client/src/app/\[site\]/main/components/MainSection/ChartSliceTooltip.tsx

Repository: rybbit-io/rybbit

Length of output: 156


🏁 Script executed:

#!/bin/bash
# Check client tsconfig
cat client/tsconfig.json

echo "---"

# Read around line 160
sed -n '155,165p' client/src/app/\[site\]/main/components/MainSection/ChartSliceTooltip.tsx

Repository: rybbit-io/rybbit

Length of output: 1058


Replace toReversed() with .reverse() for ES2017 compatibility.

The client TypeScript config targets ES2017, but Array.prototype.toReversed() is an ES2023 feature. While TypeScript may not error due to "lib": ["esnext"], the code will fail at runtime. Use [...rows].reverse() instead, which is compatible with ES2017.

{[...rows].reverse().map((row: any, idx: number) => (
🤖 Prompt for AI Agents
In client/src/app/[site]/main/components/MainSection/ChartSliceTooltip.tsx
around line 160, replace the ES2023 Array.prototype.toReversed() call (which
will fail at runtime under the ES2017 target) with a compatible approach: create
a shallow copy of rows and reverse it before mapping (e.g., use spread +
reverse) so the code runs under ES2017 while preserving the original array.

…improved readability

- Simplify date calculations by formatting multi-line expressions for better clarity.
- Adjust size calculation in PreviousChart for consistent data handling.
@goldflag goldflag merged commit fd1f912 into master Nov 29, 2025
5 of 7 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 20, 2025
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