Conversation
|
@Sendi0011 is attempting to deploy a commit to the Threadflow Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR implements a full-featured analytics dashboard page with client-side authentication guards, displaying user metrics in a bento-grid layout with trend indicators and an interactive time-series chart component, all drawing data from the existing session without additional API calls. Changes
Sequence DiagramsequenceDiagram
participant User
participant AnalyticsPage as AnalyticsPage<br/>(Wrapper)
participant AuthGuard
participant AnalyticsContent
participant BentoGrid as AnalyticsBentoGrid
participant Chart as AnalyticsChart
participant Session
User->>AnalyticsPage: Visit /me/analytics
AnalyticsPage->>AuthGuard: Render with children
AuthGuard->>Session: Check authentication status
alt Not Authenticated
AuthGuard->>User: Redirect to /sign-in
else Authenticated
AuthGuard->>AnalyticsContent: Render content
AnalyticsContent->>Session: Extract meData (stats & chart)
AnalyticsContent->>AnalyticsContent: useAuthStatus for loading state
alt Loading
AnalyticsContent->>User: Show loading spinner
else Data Available
AnalyticsContent->>BentoGrid: Pass stats & chart
AnalyticsContent->>Chart: Pass chart data
BentoGrid->>BentoGrid: Calculate trends (calculateChartTrend)
BentoGrid->>BentoGrid: Memoize tile configs
BentoGrid->>User: Render animated tile grid
Chart->>Chart: Transform & memoize chart data
Chart->>Chart: Apply range filter (7D/30D/90D/ALL)
Chart->>User: Render interactive line chart
else No Analytics Data
AnalyticsContent->>User: Show missing data message
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (5)
app/me/analytics/page.tsx (1)
11-12: Consider using const arrow functions per coding guidelines.Both
AnalyticsContentandAnalyticsPageare function declarations. The coding guidelines prefer const arrow functions with explicit type annotations.♻️ Suggested refactor
-function AnalyticsContent() { +const AnalyticsContent = (): JSX.Element => { ... -export default function AnalyticsPage() { +const AnalyticsPage = (): JSX.Element => { ... +export default AnalyticsPage;As per coding guidelines: "Prefer const arrow functions with explicit type annotations over function declarations".
Also applies to: 54-54
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/me/analytics/page.tsx` around lines 11 - 12, Convert the function declarations AnalyticsContent and AnalyticsPage into const arrow functions with explicit React/Next type annotations; replace "function AnalyticsContent() { ... }" and "function AnalyticsPage() { ... }" with "const AnalyticsContent: React.FC = () => { ... }" (or the appropriate NextPage/JSX.Element return type for AnalyticsPage) and ensure any props or return types are explicitly annotated to satisfy the coding guidelines.components/analytics/AnalyticsChart.tsx (1)
82-95: Prefer clsx for conditional classes.The conditional className uses a ternary operator within the template literal. The coding guidelines recommend using clsx or similar helper functions for cleaner conditional class composition.
♻️ Suggested refactor using clsx
+import clsx from 'clsx'; ... <button key={r} onClick={() => setRange(r)} aria-pressed={range === r} - className={`rounded-lg px-3 py-1.5 text-xs font-medium transition-all duration-150 ${ - range === r - ? 'bg-gradient-to-r from-[`#06b6d4`] to-[`#4f46e5`] text-white shadow' - : 'text-zinc-400 hover:text-white' - }`} + className={clsx( + 'rounded-lg px-3 py-1.5 text-xs font-medium transition-all duration-150', + range === r + ? 'bg-gradient-to-r from-[`#06b6d4`] to-[`#4f46e5`] text-white shadow' + : 'text-zinc-400 hover:text-white' + )} >As per coding guidelines: "For conditional classes, prefer clsx or similar helper functions over ternary operators in JSX".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/analytics/AnalyticsChart.tsx` around lines 82 - 95, Replace the inline template-literal conditional classes in the AnalyticsChart component (the ranges.map button render that calls setRange and reads range) with clsx: import clsx at the top, then build the button className using clsx to always include the shared classes ('rounded-lg px-3 py-1.5 text-xs font-medium transition-all duration-150') and conditionally include the active classes ('bg-gradient-to-r from-[`#06b6d4`] to-[`#4f46e5`] text-white shadow') when range === r, otherwise include the inactive classes ('text-zinc-400 hover:text-white'); keep key, onClick, aria-pressed and button children unchanged.components/analytics/AnalyticsBentoGrid.tsx (1)
225-226: Consider using clsx for the conditional text size.The ternary operator in the className could be replaced with clsx for consistency with coding guidelines.
♻️ Suggested refactor
+import clsx from 'clsx'; ... <p - className={`font-bold tracking-tight text-white ${tile.large ? 'text-4xl' : 'text-2xl'}`} + className={clsx( + 'font-bold tracking-tight text-white', + tile.large ? 'text-4xl' : 'text-2xl' + )} >As per coding guidelines: "For conditional classes, prefer clsx or similar helper functions over ternary operators in JSX".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/analytics/AnalyticsBentoGrid.tsx` around lines 225 - 226, Replace the inline ternary in the JSX className on the <p> element inside the AnalyticsBentoGrid component with clsx: import clsx if not already imported, then use clsx to combine the static classes "font-bold tracking-tight text-white" with a conditional mapping for tile.large to choose "text-4xl" or "text-2xl" (refer to the variable tile and the <p> element where className is set) so the conditional class follows the project's clsx convention.lib/utils/calculateTrend.ts (2)
56-65: Consider the fallback behavior when filtering yields no results.When
filteredis empty, the fallbackdata.slice(-days)returns the last N data points. This is reasonable, but ifdaysexceedsdata.length, it returns the entire array. Ensure this behavior aligns with UX expectations (e.g., showing "No data" vs. showing older data outside the requested range).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/utils/calculateTrend.ts` around lines 56 - 65, In filterChartByRange, the current fallback returns data.slice(-days) which can return the entire array when days > data.length; change the fallback so it returns an empty array when the requested days exceeds data.length (so the caller can show "No data") and otherwise return the last days entries. Concretely, replace the final return so it uses filtered.length > 0 ? filtered : (days > data.length ? [] : data.slice(-days)), keeping the function name filterChartByRange and variables filtered, days and data as references.
6-9: Consider using const arrow functions per coding guidelines.The coding guidelines prefer const arrow functions with explicit type annotations over function declarations. This is a stylistic preference and can be deferred.
♻️ Example refactor for one function
-export function calculateTrend( - current: number | null | undefined, - previous: number | null | undefined -): TrendResult { +export const calculateTrend = ( + current: number | null | undefined, + previous: number | null | undefined +): TrendResult => {As per coding guidelines: "Prefer const arrow functions with explicit type annotations over function declarations".
Also applies to: 26-28, 39-41, 56-58
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/utils/calculateTrend.ts` around lines 6 - 9, Convert the function declaration calculateTrend to an exported const arrow function with explicit parameter and return type annotations (keep the same parameter names and TrendResult return type) and replace the other function declarations in this file with the same pattern; ensure you export the consts and preserve original logic and symbol names (e.g., calculateTrend and the same parameter identifiers and TrendResult) so tooling and callers remain unaffected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@app/me/analytics/page.tsx`:
- Around line 11-12: Convert the function declarations AnalyticsContent and
AnalyticsPage into const arrow functions with explicit React/Next type
annotations; replace "function AnalyticsContent() { ... }" and "function
AnalyticsPage() { ... }" with "const AnalyticsContent: React.FC = () => { ... }"
(or the appropriate NextPage/JSX.Element return type for AnalyticsPage) and
ensure any props or return types are explicitly annotated to satisfy the coding
guidelines.
In `@components/analytics/AnalyticsBentoGrid.tsx`:
- Around line 225-226: Replace the inline ternary in the JSX className on the
<p> element inside the AnalyticsBentoGrid component with clsx: import clsx if
not already imported, then use clsx to combine the static classes "font-bold
tracking-tight text-white" with a conditional mapping for tile.large to choose
"text-4xl" or "text-2xl" (refer to the variable tile and the <p> element where
className is set) so the conditional class follows the project's clsx
convention.
In `@components/analytics/AnalyticsChart.tsx`:
- Around line 82-95: Replace the inline template-literal conditional classes in
the AnalyticsChart component (the ranges.map button render that calls setRange
and reads range) with clsx: import clsx at the top, then build the button
className using clsx to always include the shared classes ('rounded-lg px-3
py-1.5 text-xs font-medium transition-all duration-150') and conditionally
include the active classes ('bg-gradient-to-r from-[`#06b6d4`] to-[`#4f46e5`]
text-white shadow') when range === r, otherwise include the inactive classes
('text-zinc-400 hover:text-white'); keep key, onClick, aria-pressed and button
children unchanged.
In `@lib/utils/calculateTrend.ts`:
- Around line 56-65: In filterChartByRange, the current fallback returns
data.slice(-days) which can return the entire array when days > data.length;
change the fallback so it returns an empty array when the requested days exceeds
data.length (so the caller can show "No data") and otherwise return the last
days entries. Concretely, replace the final return so it uses filtered.length >
0 ? filtered : (days > data.length ? [] : data.slice(-days)), keeping the
function name filterChartByRange and variables filtered, days and data as
references.
- Around line 6-9: Convert the function declaration calculateTrend to an
exported const arrow function with explicit parameter and return type
annotations (keep the same parameter names and TrendResult return type) and
replace the other function declarations in this file with the same pattern;
ensure you export the consts and preserve original logic and symbol names (e.g.,
calculateTrend and the same parameter identifiers and TrendResult) so tooling
and callers remain unaffected.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
app/me/analytics/page.tsxcomponents/analytics/AnalyticsBentoGrid.tsxcomponents/analytics/AnalyticsChart.tsxlib/utils/calculateTrend.ts
feat(analytics): implement
/me/analyticspersonal growth dashboard — closes #397Overview
This PR implements the personal analytics dashboard at
app/me/analyticsas specified in issue #397. The page is a full-width, interactive growth console that surfaces the authenticated user's platform stats and activity trends using data sourced exclusively from the existing session — no additional API calls are made on page load.Changes
New files:
app/me/analytics/page.tsx— Route entry point, protected byAuthGuard, reads fromuser.profilepopulated by the existinggetMe()call inuseAuthStatuscomponents/analytics/AnalyticsBentoGrid.tsx— Asymmetric bento-style grid with staggered Framer Motion mount animation, surfacing all stats fromGetMeResponse.statswith trend badgescomponents/analytics/AnalyticsChart.tsx— RechartsLineChartofGetMeResponse.chartwith 7D / 30D / 90D / ALL range toggle, custom tooltip, and cyan→indigo gradient linelib/utils/calculateTrend.ts— Pure utility functions for trend percentage calculation, chart data transformation, and range filtering. Independently testable with no React dependenciesHow It Works
All data flows from
user.profilewhich is set byuseAuthStatus()via the already-existinggetMe()fetch inhooks/use-auth.ts. The analytics page consumes this cached value — it introduces zero new network requests.Trend percentages are calculated client-side by splitting the chart time series into two equal halves (previous period vs current period) and computing the percentage change. The
calculateTrendutility handles all edge cases: zero previous-period values, null/undefined fields, and flat periods.Chart data transformation (sorting by date, formatting labels) lives in
transformChartData()andfilterChartByRange()in the utility file — not inline in JSX.Design
col-span-2 row-span-2hero tile, with supporting metrics in smaller tiles around it#06b6d4) to Deep Indigo (#4f46e5) gradient applied to the chart line and active range toggle button, consistent with the Boundless palettebg-white/[0.03],border-white/[0.06],backdrop-blur-smAccessibility
<table>with proper column headers for screen readerssr-onlydata table fallback listing every date and activity count in the active rangearia-pressedandrole="group"with a descriptive labelaria-labeldescribing direction and percentage valueA Note on Chart Data
GetMeResponse.chartexposes a single{ date, count }activity time series. The issue referenced a "Projects vs Earnings" multi-line chart, however noearningsfield exists in the API response. Fabricating a second series would violate the issue's explicit requirement of no hardcoded or mock data. The chart renders the real activity series. If the backend adds additional series in future,AnalyticsChartis structured to accommodate extra<Line>entries with minimal changes.Testing Checklist
npm run lintpasses with no errorsnpm run buildpasses with no breaking changesScreenshots
Desktop view
Mobile view
Summary by CodeRabbit