-
Notifications
You must be signed in to change notification settings - Fork 7
feat(dashboard): enhance client dashboard with new analytics charts and design improvements #447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add PeriodComparison: weekly performance comparison (this week vs last week) - Add CategoryPerformance: horizontal bar chart of top categories by avg engagement - Add ApprovalTrend: area chart showing 6-month approval rate trend - Add SubmissionCalendar: GitHub-style heatmap for last 90 days activity - Add EngagementDistribution: horizontal bar chart of top items by engagement - Add EngagementRateChart: line chart showing engagement rate over time
… accessibility
Changes:
- New file: components/dashboard/styles.ts
- Centralized design system constants (CARD_BASE_STYLES, TITLE_STYLES, etc.)
- Added TOOLTIP_STYLES, CHART_COLORS, SEMANTIC_COLORS
- Modified: components/dashboard/period-comparison.tsx
- Import shared styles from styles.ts (DRY principle)
- Use SEMANTIC_COLORS instead of hardcoded color values
- Replace hardcoded "vs" with translation tCommon("VS")
- Add accessibility attributes (aria-label, aria-labelledby, role)
- Change container to <section> with proper landmarks
- Modified: 14 locale files (ar, de, en, es, fr, id, it, ja, ko, nl, pl, pt, ru, zh)
- Add client.dashboard.COMMON.VS translation
- Add client.dashboard.COMMON.AVG translation
- Replaced local style constants with imports from ./styles
- Changed container from <div> to <section> with aria-labelledby
- Added id to heading for accessibility linking
… and add accessibility
- Replaced local style constants with imports from ./styles
- Added useLocale() and passed locale to formatDate() for proper i18n
- Changed container from <div> to <section> with aria-labelledby
- Added id to heading for accessibility linking
…ility
- Replaced local style constants with imports from ./styles
- Changed container from <div> to <section> with aria-labelledby
- Added id to heading for accessibility linking
… add accessibility
- Replaced local style constants with imports from ./styles
- Replaced hardcoded "Avg:" with tCommon("AVG") translation
- Changed container from <div> to <section> with aria-labelledby
- Added id to heading for accessibility linking
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a dashboard analytics feature: six new client React components, shared dashboard styles and exports, extended repository computations and types for new chart data, removal of a mock fixture, and comprehensive i18n dashboard strings across many locales. Changes
Sequence Diagram(s)(Skipped — changes are multiple new components and data calculations but do not introduce a single new cross-component runtime control flow that meets the diagram criteria.) Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4 issues found across 28 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="components/dashboard/approval-trend.tsx">
<violation number="1" location="components/dashboard/approval-trend.tsx:89">
P2: Hardcoded SVG gradient ID can cause collisions if this component is rendered multiple times. Use React's `useId()` hook to generate a unique ID, consistent with how `stats-card.tsx` handles this.</violation>
</file>
<file name="components/dashboard/category-performance.tsx">
<violation number="1" location="components/dashboard/category-performance.tsx:106">
P2: The `labelFormatter` lookup may return the wrong category if two categories share the same 15-character prefix. Since `chartData` is in scope and contains both `displayCategory` and original `category`, match by exact `displayCategory` instead.</violation>
</file>
<file name="components/dashboard/status-breakdown.tsx">
<violation number="1" location="components/dashboard/status-breakdown.tsx:51">
P2: The condition checks for exact zero but the display rounds to 0%. Values like 0.004 (0.4%) would pass the check but still render as '0%' after rounding, potentially causing the same label overlap issue. Consider checking if the rounded percentage equals 0 instead.</violation>
</file>
<file name="components/dashboard/period-comparison.tsx">
<violation number="1" location="components/dashboard/period-comparison.tsx:125">
P2: ARIA role mismatch: `role="list"` requires children with `role="listitem"`, but `MetricCard` uses `role="group"`. Remove `role="list"` since the `MetricCard` components already have proper `role="group"` with `aria-label` attributes for accessibility.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this 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 (16)
messages/pt.json (1)
2839-2930: Client dashboard (pt) translations look correct and aligned with new componentsThe new
client.dashboardblock is syntactically valid, preserves all interpolation placeholders, and the Portuguese wording is consistent with the rest of the locale. Only minor nit: if layout allows, you might consider expanding abbreviations like"Engaj. Médio"/"Méd."to full words for clarity.messages/nl.json (1)
2839-2930: Client dashboard (nl) translations are consistent and implementation-readyThe added
client.dashboardnamespace matches the expected key structure, keeps all placeholders intact, and the Dutch phrasing fits the existing tone. Optionally, you could expand abbreviations like"Gem. Betrokk."/"Gem."if the chart layout has room for slightly longer labels.messages/fr.json (1)
2939-3029: Client dashboard FR translations look correct; consider clarifying abbreviations.The new
client.submissions.dashboardblock is structurally aligned with the EN locale (same keys, same placeholders) and should work fine with the new dashboard components.One optional polish point:
"AVG_ENGAGEMENT": "Engag. Moy."and"AVG": "Moy."use fairly terse abbreviations that may be unclear out of context. If UI space allows, consider expanding slightly for readability, e.g.:Optional wording tweak
- "AVG_ENGAGEMENT": "Engag. Moy.", + "AVG_ENGAGEMENT": "Engagement moy.", ... - "AVG": "Moy." + "AVG": "Moyenne"Purely stylistic; functionally this block is good to ship.
components/dashboard/engagement-chart.tsx (1)
20-35: Consider reusing shared dashboard style tokens to avoid duplicationYou now have
CARD_BASE_STYLES,TITLE_STYLES, andTOOLTIP_STYLESincomponents/dashboard/styles.ts; this component is duplicating those values inline. Reusing the shared tokens will keep the engagement chart visually consistent with the new dashboard widgets and make future style changes easier.Proposed refactor using shared styles
-import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend } from 'recharts'; +import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend } from 'recharts'; +import { CARD_BASE_STYLES, TITLE_STYLES, TOOLTIP_STYLES } from './styles'; @@ - return ( - <div className="bg-white dark:bg-gray-900 rounded-xl shadow-xs border border-gray-200 dark:border-gray-700 p-6"> + return ( + <div className={CARD_BASE_STYLES}> @@ - <div className="bg-white dark:bg-gray-900 rounded-xl shadow-xs border border-gray-200 dark:border-gray-700 p-6"> - <h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4"> + <div className={CARD_BASE_STYLES}> + <h3 className={`${TITLE_STYLES} mb-4`}> @@ - <Tooltip - contentStyle={{ - backgroundColor: '#1F2937', - border: '1px solid #374151', - borderRadius: '8px', - color: '#F9FAFB' - }} - /> + <Tooltip contentStyle={TOOLTIP_STYLES} />Also applies to: 53-60
messages/ru.json (1)
2840-2930: Client dashboard RU translations look consistent; minor optional copy tweakThe new
client.dashboardblock is structurally coherent (all expected subsections and keys are present) and the{name}placeholder inWELCOME_BACKis correctly preserved. As an optional polish, consider localizing"COMMON.VS": "vs"to a Russian equivalent (for example,"против") if you want to avoid English fragments in the Russian UI; everything else looks good to ship.messages/es.json (1)
2862-2952: ES dashboard translations aligned with analytics surface; only tiny optional wording nitThe new
client.dashboardsubtree for Spanish cleanly matches the expected structure (STATS, PERIOD_COMPARISON, CATEGORY_PERFORMANCE, APPROVAL_TREND, ENGAGEMENT_RATE, SUBMISSION_CALENDAR, ENGAGEMENT_DISTRIBUTION, COMMON, CHARTS) and preserves the{name}placeholder correctly inWELCOME_BACK. If you prefer fully localized copy, you might replace"COMMON.VS": "vs"with a Spanish alternative (e.g.,"frente a"), but that’s purely optional—current texts are coherent and usable.components/dashboard/status-breakdown.tsx (1)
50-53: Label guard correctly hides 0% slices; consider a tiny readability refactorThe updated label callback safely skips labels for zero-percent slices and avoids
percentbeingundefinedby usingpercent ?? 0, which matches the intent to hide 0% segments. For readability you could compute a localconst percentage = (percent ?? 0) * 100;and reuse it in both the zero check and the formatted string, but behavior is already correct.components/dashboard/dashboard-content.tsx (1)
18-23: New analytics widgets are wired correctly into the dashboard layoutThe newly imported analytics components are integrated cleanly:
- Each widget receives the appropriate slice from
stats(periodComparison,categoryPerformance,approvalTrend,submissionCalendar,engagementDistribution,engagementOverview,totalSubmissions) and anisLoading={!stats}flag that matches their expected props.- Fallbacks like
|| []and|| 0ensure children never seeundefinedwhere they expect arrays/numbers, while their ownisLoading/ “no data” branches handle empty data gracefully.- Updating
EngagementOverviewto takeisLoadingkeeps behavior consistent with other cards.You might optionally introduce a local
const isLoading = !stats;and reuse it for all these components to reduce repetition, but that’s purely a readability tweak.Please run
pnpm tsc --noEmitto confirm thatuseDashboardStats’s return type includes all the new fields (periodComparison,categoryPerformance,approvalTrend,submissionCalendar,engagementDistribution, updatedengagementOverview, etc.) referenced here.Also applies to: 108-115, 130-134, 136-145, 164-187
components/dashboard/period-comparison.tsx (1)
125-125: Consider removing or fixing therole="list"attribute.The grid has
role="list"but theMetricCardchildren userole="group"instead ofrole="listitem". This creates an accessibility mismatch. Either removerole="list"from the grid or addrole="listitem"wrappers around eachMetricCard.🔎 Proposed fix
- <div className="grid grid-cols-2 md:grid-cols-4 gap-4" role="list"> + <div className="grid grid-cols-2 md:grid-cols-4 gap-4">components/dashboard/submission-calendar.tsx (2)
33-40: Consider reusing the utility function fromutils/date.tsor documenting the difference.There's a
formatDatefunction inutils/date.tswith different options (month: 'long'). This local version usesmonth: 'short'which is appropriate for the compact calendar tooltip, but consider adding a comment to clarify this intentional difference.
69-89: Using-1as a sentinel value for padding days works but is non-obvious.The
count: -1sentinel is used to identify padding cells. Consider using a more explicit approach like aisPaddingboolean flag or a union type for better readability.components/dashboard/engagement-rate-chart.tsx (1)
23-27: Duplicate interface definition.
EngagementOverviewDatais defined locally here but also exists inhooks/use-dashboard-stats.ts(lines 31-35). Consider importing from the hook file to maintain a single source of truth and ensure type consistency.🔎 Proposed fix
Add the type to the re-exports in
hooks/use-dashboard-stats.ts:// In hooks/use-dashboard-stats.ts, add to exports: export type { EngagementOverviewData };Then import in this file:
+import type { EngagementOverviewData } from "@/hooks/use-dashboard-stats"; -interface EngagementOverviewData { - week: string; - votes: number; - comments: number; -}components/dashboard/engagement-distribution.tsx (1)
105-110: Improved but still imperfect tooltip label matching.Using
startsWithis better than exact substring comparison (as incategory-performance.tsx), but could still match incorrectly if multiple items share the same 20-character prefix. Consider using the same Map-based approach suggested for category-performance for consistency.hooks/use-dashboard-stats.ts (1)
93-95: Add Zod validation for the dashboard stats API response.The response is cast to
UserStatswithout runtime validation. Per coding guidelines, create a Zod schema inlib/validations/client-dashboard.tsto validate the API response shape. This would catch mismatches between the API and the expectedUserStatstype at runtime, especially important given the complex nested structure of the response.lib/repositories/client-dashboard.repository.ts (2)
56-112: Consider eliminating duplicate Export type variants.Lines 56-88 define internal chart data types, while lines 89-112 define structurally identical
Exportvariants. This duplication increases maintenance burden without clear benefit. Note thatEngagementDistributionData(line 81) is exported directly without a duplicate, creating an inconsistent pattern.🔎 Proposed consolidation
Export the base types directly and remove the Export variants:
-interface PeriodComparisonData { +export interface PeriodComparisonData { thisWeek: { votes: number; comments: number; submissions: number; views: number }; lastWeek: { votes: number; comments: number; submissions: number; views: number }; change: { votes: number; comments: number; submissions: number; views: number }; } -interface CategoryPerformanceData { +export interface CategoryPerformanceData { category: string; itemCount: number; totalEngagement: number; avgEngagement: number; } -interface ApprovalTrendData { +export interface ApprovalTrendData { month: string; approved: number; total: number; rate: number; } -interface SubmissionCalendarData { +export interface SubmissionCalendarData { date: string; count: number; } -export interface PeriodComparisonDataExport { - thisWeek: { votes: number; comments: number; submissions: number; views: number }; - lastWeek: { votes: number; comments: number; submissions: number; views: number }; - change: { votes: number; comments: number; submissions: number; views: number }; -} - -export interface CategoryPerformanceDataExport { - category: string; - itemCount: number; - totalEngagement: number; - avgEngagement: number; -} - -export interface ApprovalTrendDataExport { - month: string; - approved: number; - total: number; - rate: number; -} - -export interface SubmissionCalendarDataExport { - date: string; - count: number; -}Then update consuming code to reference the base types instead of Export variants.
487-533: Consider extracting magic number for top categories limit.The method correctly calculates category performance with multi-category item support. The top 5 limit (line 532) is reasonable but hardcoded.
🔎 Optional improvement
Extract to a constant at the top of the file for easier adjustment:
+const TOP_CATEGORIES_LIMIT = 5; + // ... in calculateCategoryPerformance return result .sort((a, b) => b.avgEngagement - a.avgEngagement) - .slice(0, 5); + .slice(0, TOP_CATEGORIES_LIMIT);
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (28)
components/dashboard/approval-trend.tsxcomponents/dashboard/category-performance.tsxcomponents/dashboard/dashboard-content.tsxcomponents/dashboard/engagement-chart.tsxcomponents/dashboard/engagement-distribution.tsxcomponents/dashboard/engagement-rate-chart.tsxcomponents/dashboard/index.tscomponents/dashboard/period-comparison.tsxcomponents/dashboard/status-breakdown.tsxcomponents/dashboard/styles.tscomponents/dashboard/submission-calendar.tsxhooks/mock-dashboard-stats.tshooks/use-dashboard-stats.tslib/repositories/client-dashboard.repository.tsmessages/ar.jsonmessages/de.jsonmessages/en.jsonmessages/es.jsonmessages/fr.jsonmessages/id.jsonmessages/it.jsonmessages/ja.jsonmessages/ko.jsonmessages/nl.jsonmessages/pl.jsonmessages/pt.jsonmessages/ru.jsonmessages/zh.json
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use Node.js >= 20.19.0
Files:
components/dashboard/dashboard-content.tsxcomponents/dashboard/status-breakdown.tsxhooks/use-dashboard-stats.tshooks/mock-dashboard-stats.tslib/repositories/client-dashboard.repository.tscomponents/dashboard/engagement-rate-chart.tsxcomponents/dashboard/period-comparison.tsxcomponents/dashboard/submission-calendar.tsxcomponents/dashboard/engagement-distribution.tsxcomponents/dashboard/approval-trend.tsxcomponents/dashboard/styles.tscomponents/dashboard/index.tscomponents/dashboard/category-performance.tsxcomponents/dashboard/engagement-chart.tsx
components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
components/**/*.{ts,tsx}: UI components should be placed in the components/** directory
Keep React components mostly presentational and lean, avoiding complex business logic
Files:
components/dashboard/dashboard-content.tsxcomponents/dashboard/status-breakdown.tsxcomponents/dashboard/engagement-rate-chart.tsxcomponents/dashboard/period-comparison.tsxcomponents/dashboard/submission-calendar.tsxcomponents/dashboard/engagement-distribution.tsxcomponents/dashboard/approval-trend.tsxcomponents/dashboard/styles.tscomponents/dashboard/index.tscomponents/dashboard/category-performance.tsxcomponents/dashboard/engagement-chart.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Prefer TypeScript files over JavaScript
Run pnpm tsc --noEmit for TypeScript type-checking
Files:
components/dashboard/dashboard-content.tsxcomponents/dashboard/status-breakdown.tsxhooks/use-dashboard-stats.tshooks/mock-dashboard-stats.tslib/repositories/client-dashboard.repository.tscomponents/dashboard/engagement-rate-chart.tsxcomponents/dashboard/period-comparison.tsxcomponents/dashboard/submission-calendar.tsxcomponents/dashboard/engagement-distribution.tsxcomponents/dashboard/approval-trend.tsxcomponents/dashboard/styles.tscomponents/dashboard/index.tscomponents/dashboard/category-performance.tsxcomponents/dashboard/engagement-chart.tsx
components/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Place UI and layout components in components/**
Files:
components/dashboard/dashboard-content.tsxcomponents/dashboard/status-breakdown.tsxcomponents/dashboard/engagement-rate-chart.tsxcomponents/dashboard/period-comparison.tsxcomponents/dashboard/submission-calendar.tsxcomponents/dashboard/engagement-distribution.tsxcomponents/dashboard/approval-trend.tsxcomponents/dashboard/styles.tscomponents/dashboard/index.tscomponents/dashboard/category-performance.tsxcomponents/dashboard/engagement-chart.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer TypeScript for all code
Files:
components/dashboard/dashboard-content.tsxcomponents/dashboard/status-breakdown.tsxhooks/use-dashboard-stats.tshooks/mock-dashboard-stats.tslib/repositories/client-dashboard.repository.tscomponents/dashboard/engagement-rate-chart.tsxcomponents/dashboard/period-comparison.tsxcomponents/dashboard/submission-calendar.tsxcomponents/dashboard/engagement-distribution.tsxcomponents/dashboard/approval-trend.tsxcomponents/dashboard/styles.tscomponents/dashboard/index.tscomponents/dashboard/category-performance.tsxcomponents/dashboard/engagement-chart.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Follow Prettier config with tabs, 4-space tabWidth, and 120-char printWidth
Prefer async/await over raw Promise chains in TypeScript/JavaScript code
Validate input with Zod where appropriate; follow existing schemas in lib/validations
Keep i18n-friendly: avoid hard-coded English strings in logic; use next-intl messages where relevant
Files:
components/dashboard/dashboard-content.tsxcomponents/dashboard/status-breakdown.tsxhooks/use-dashboard-stats.tshooks/mock-dashboard-stats.tslib/repositories/client-dashboard.repository.tscomponents/dashboard/engagement-rate-chart.tsxcomponents/dashboard/period-comparison.tsxcomponents/dashboard/submission-calendar.tsxcomponents/dashboard/engagement-distribution.tsxcomponents/dashboard/approval-trend.tsxcomponents/dashboard/styles.tscomponents/dashboard/index.tscomponents/dashboard/category-performance.tsxcomponents/dashboard/engagement-chart.tsx
**/components/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/components/**/*.{jsx,tsx}: For forms, prefer react-hook-form + Zod; follow patterns in existing auth/profile forms
Place business logic in lib/services or lib/repositories, not in components; keep components mostly presentational and data-fetching
Files:
components/dashboard/dashboard-content.tsxcomponents/dashboard/status-breakdown.tsxcomponents/dashboard/engagement-rate-chart.tsxcomponents/dashboard/period-comparison.tsxcomponents/dashboard/submission-calendar.tsxcomponents/dashboard/engagement-distribution.tsxcomponents/dashboard/approval-trend.tsxcomponents/dashboard/category-performance.tsxcomponents/dashboard/engagement-chart.tsx
lib/repositories/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Data access and repositories should be placed in lib/repositories/** directory
Files:
lib/repositories/client-dashboard.repository.ts
lib/{services,repositories}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Place shared business logic in lib/services/** and lib/repositories/**
Files:
lib/repositories/client-dashboard.repository.ts
🧬 Code graph analysis (5)
components/dashboard/dashboard-content.tsx (8)
components/dashboard/period-comparison.tsx (1)
PeriodComparison(79-165)components/dashboard/index.ts (7)
PeriodComparison(10-10)EngagementOverview(6-6)CategoryPerformance(11-11)ApprovalTrend(12-12)SubmissionCalendar(13-13)EngagementDistribution(14-14)EngagementRateChart(15-15)components/dashboard/engagement-overview.tsx (1)
EngagementOverview(16-72)components/dashboard/category-performance.tsx (1)
CategoryPerformance(28-142)components/dashboard/approval-trend.tsx (1)
ApprovalTrend(27-145)components/dashboard/submission-calendar.tsx (1)
SubmissionCalendar(42-170)components/dashboard/engagement-distribution.tsx (1)
EngagementDistribution(29-140)components/dashboard/engagement-rate-chart.tsx (1)
EngagementRateChart(35-172)
hooks/use-dashboard-stats.ts (1)
lib/repositories/client-dashboard.repository.ts (5)
PeriodComparisonDataExport(89-93)CategoryPerformanceDataExport(95-100)ApprovalTrendDataExport(102-107)SubmissionCalendarDataExport(109-112)EngagementDistributionData(81-87)
components/dashboard/engagement-rate-chart.tsx (3)
components/dashboard/styles.ts (5)
CARD_BASE_STYLES(4-5)TITLE_STYLES(7-7)SUBTITLE_STYLES(9-9)VALUE_STYLES(11-11)TOOLTIP_STYLES(21-26)components/ui/responsive-container.tsx (1)
ResponsiveContainer(14-51)lib/editor/components/primitive/tooltip/tooltip.tsx (1)
Tooltip(132-153)
components/dashboard/submission-calendar.tsx (4)
hooks/use-dashboard-stats.ts (1)
SubmissionCalendarDataExport(15-15)utils/date.ts (1)
formatDate(1-7)components/dashboard/index.ts (1)
SubmissionCalendar(13-13)components/dashboard/styles.ts (4)
CARD_BASE_STYLES(4-5)TITLE_STYLES(7-7)SUBTITLE_STYLES(9-9)VALUE_STYLES(11-11)
components/dashboard/approval-trend.tsx (5)
hooks/use-dashboard-stats.ts (1)
ApprovalTrendDataExport(14-14)components/dashboard/index.ts (1)
ApprovalTrend(12-12)components/dashboard/styles.ts (5)
CARD_BASE_STYLES(4-5)TITLE_STYLES(7-7)SUBTITLE_STYLES(9-9)VALUE_STYLES(11-11)TOOLTIP_STYLES(21-26)components/ui/responsive-container.tsx (1)
ResponsiveContainer(14-51)lib/editor/components/primitive/tooltip/tooltip.tsx (1)
Tooltip(132-153)
⏰ 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). (5)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Greptile Review
- GitHub Check: Lint and Build
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (33)
messages/it.json (1)
2839-2930: LGTM! Dashboard translations properly structured.The Italian dashboard translations are well-organized with:
- Valid JSON syntax and proper nesting
- Consistent key naming conventions
- Correct placeholder usage ({name}, {count}, etc.)
- Complete coverage of all dashboard sections (STATS, PERIOD_COMPARISON, CATEGORY_PERFORMANCE, APPROVAL_TREND, ENGAGEMENT_RATE, SUBMISSION_CALENDAR, ENGAGEMENT_DISTRIBUTION, COMMON, CHARTS)
The structure aligns well with the new dashboard analytics components described in the PR objectives.
messages/zh.json (1)
2871-2962: LGTM! Chinese dashboard translations mirror Italian file perfectly.The Chinese dashboard translations maintain excellent consistency with the Italian locale file:
- Identical key structure and nesting
- Matching placeholder usage
- Valid JSON syntax throughout
- Complete coverage of all dashboard sections
- Proper formatting and organization
The consistency between locale files will ensure the dashboard components render correctly across all supported languages.
messages/de.json (1)
2872-2963: Client dashboard de-translations look structurally and semantically alignedThe new
client.dashboardblock mirrors the expected key structure (STATS, PERIOD_COMPARISON, CATEGORY_PERFORMANCE, APPROVAL_TREND, ENGAGEMENT_RATE, SUBMISSION_CALENDAR, ENGAGEMENT_DISTRIBUTION, COMMON, CHARTS), uses the correct{name}placeholder, and the German phrasing is consistent and clear for a dashboard context. No JSON or i18n placeholder issues spotted.messages/id.json (1)
2840-2930: Client dashboard id-translations are consistent and correctly structuredThe added
client.dashboardnamespace matches the other locales’ key shape, keeps the{name}placeholder intact, and provides natural Indonesian labels and descriptions for all charts and empty states. No issues with JSON validity or obvious i18n mismatches.messages/en.json (1)
2955-3045: New client dashboard EN strings are consistent and ready to use.The added
client.submissions.dashboardnamespace cleanly matches the described widgets (stats, weekly comparison, category performance, approval trend, engagement charts), with:
- Key structure parallel to other locales and internally consistent.
- Correct use of the
{name}placeholder inWELCOME_BACK.- Reusable
COMMON.VSandCOMMON.AVGlabels, which will help avoid hardcoded “vs” / “Avg” in chart components.- Clear, user-friendly copy for loading and no‑data states.
No issues from an i18n or structural perspective.
messages/ja.json (1)
2839-2930: Client dashboard ja translations look structurally soundThe new
client.dashboardblock is well‑structured (correct nesting, commas, and ICU placeholder{name}) and aligns with the described dashboard analytics widgets. No issues from a code/i18n‑keys perspective.components/dashboard/styles.ts (1)
1-51: Shared dashboard style tokens are clean and reusableCentralizing card, text, metric, tooltip, and color constants here is straightforward and consistent. The exports are well‑named, immutable, and ready to be reused across dashboard components.
components/dashboard/engagement-chart.tsx (1)
41-44: Zero‑percent slice labels now handled safelyThe updated
labelcallback correctly skips labels for 0% slices and defends against missingname/percentvalues. This should clean up the chart without affecting non‑zero segments.messages/ar.json (1)
2860-2950: Arabic client dashboard block is consistent and correctly structuredThe new
client.dashboardkeys are complete, structurally valid, and consistent with the other locales (including correct{name}placeholder usage). No code‑level issues spotted.messages/pl.json (1)
2839-2930: Client dashboard PL translations align with new componentsThe new
client.dashboardblock (plusEDIT_SUBMISSION_DESC) looks structurally consistent with the component usages: all expected keys (STATS, PERIOD_COMPARISON, CATEGORY_PERFORMANCE, APPROVAL_TREND, ENGAGEMENT_RATE, SUBMISSION_CALENDAR, ENGAGEMENT_DISTRIBUTION, COMMON, CHARTS) are present, naming matches the code, and placeholders (e.g.{name}) are well-formed. JSON syntax also looks correct.messages/ko.json (1)
2839-2930: Client dashboard KO translations are complete and consistentThe added
client.dashboardnamespace (andEDIT_SUBMISSION_DESC) cleanly mirrors the expected structure from the dashboard components: all section and field keys are present, COMMON.VS/AVG exist for shared labels, and the JSON remains valid. This should keep the new analytics widgets fully localized in Korean.components/dashboard/index.ts (1)
10-15: Barrel exports for new dashboard widgets look correctThe additional exports for
PeriodComparison,CategoryPerformance,ApprovalTrend,SubmissionCalendar,EngagementDistribution, andEngagementRateChartcorrectly point at their component modules and align with how they’re imported elsewhere.components/dashboard/period-comparison.tsx (3)
32-53: LGTM!The
ChangeIndicatorcomponent is well-implemented with clear conditional logic for positive/negative/neutral states and appropriate color classes for both light and dark modes.
55-77: LGTM!The
MetricCardcomponent is clean and accessible with properrole="group"andaria-labelattributes. Good use ofaria-hidden="true"on the decorative color dot.
79-165: Well-structured component with good separation of concerns.The component follows best practices: proper loading/empty states, i18n compliance, shared styles usage, and accessibility with
aria-labelledby. The use of<section>for the data-present state is appropriate.components/dashboard/submission-calendar.tsx (2)
24-31: LGTM!The intensity class function correctly maps count ratios to appropriate color classes with both light and dark mode support.
42-170: Well-implemented calendar heatmap with good UX.The component handles loading, empty states, and data rendering well. Good use of
useMemofor performance optimization and locale-aware date formatting. The legend at the bottom provides helpful context.hooks/mock-dashboard-stats.ts (1)
64-97: Mock data structure aligns well with the new UserStats interface.The new mock data fields (
periodComparison,categoryPerformance,approvalTrend,submissionCalendar,engagementDistribution) correctly match the type definitions and provide reasonable test values.hooks/use-dashboard-stats.ts (2)
2-17: Good pattern for re-exporting types.Re-exporting the data types from the repository layer provides a clean API for component consumers and maintains proper module boundaries.
70-75: Type extension aligns with repository exports.The new fields in
UserStatscorrectly reference the exported types from the repository layer.components/dashboard/approval-trend.tsx (3)
55-59: Data aggregation logic is correct.The calculations for
latestRate,firstRate,rateChange,totalSubmissions, andtotalApprovedcorrectly handle edge cases with optional chaining and default values.
87-106: Well-defined gradient for the area chart.The gradient
approvalGradientprovides a nice visual effect with proper opacity transitions.
27-145: Clean implementation following established patterns.The component properly handles loading, empty, and data states. Good use of shared styles, i18n, and accessibility attributes. The chart configuration is appropriate for displaying trend data.
components/dashboard/engagement-rate-chart.tsx (2)
43-54: Verify the rate calculation semantics.The rate formula
((week.votes + week.comments) / totalSubmissions) * 100divides weekly engagement by total submissions (a constant across all weeks). This produces a "percentage of total submissions that received engagement this week" rather than a typical engagement rate. Ensure this matches the intended metric, or consider documenting this in the UI/tooltip.
35-172: Component structure and patterns are consistent with other dashboard components.Good implementation with proper loading/empty states, accessibility, and i18n. The ReferenceLine for average is a nice addition for context.
components/dashboard/category-performance.tsx (1)
28-142: Well-structured component with good accessibility and i18n support.The component follows established patterns for loading/empty states, uses shared styles consistently, and provides a compact legend for the top categories.
components/dashboard/engagement-distribution.tsx (2)
122-137: Nice progress bar visualization for the top item.The summary section with progress bar provides good context for the #1 item's share of total engagement.
29-140: Component follows established dashboard patterns.Good implementation with consistent loading/empty states, accessibility, i18n, and shared styles. The visual summary at the bottom adds value.
lib/repositories/client-dashboard.repository.ts (5)
214-218: LGTM! Clear justification for increased data limits.The increased limits for
topItemsEngagement(5→10) anddailyViewsData(7→14) are well-documented with inline comments explaining their purpose for the new engagement distribution and period comparison charts.
538-580: LGTM! Approval trend calculation is correct.The method properly handles:
- 6-month initialization with zeros
- Approved vs total counting by month
- Zero-division protection in rate calculation (line 575)
Note: This uses the same local date logic as
calculatePeriodComparison. If timezone handling is addressed there, apply the same approach here for consistency.
585-616: LGTM! Submission calendar logic is sound.The 90-day heatmap calculation correctly:
- Initializes all days with zeros
- Uses ISO date format (YYYY-MM-DD) consistently
- Sorts chronologically for proper display
621-658: LGTM! Engagement distribution calculation is accurate.The method correctly:
- Combines multiple engagement signals (votes, comments, views)
- Handles missing items gracefully (line 644)
- Calculates percentage share with zero-division protection
- Returns top 10 items sorted by engagement
708-745: LGTM! Empty state helpers are consistent.The helper methods
getEmptyApprovalTrendandgetEmptySubmissionCalendarcorrectly mirror the structure of their calculation counterparts, ensuring proper initialization when no data exists.
Greptile Summary
Important Files Changed
Confidence score: 4/5
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
28 files reviewed, 3 comments
…p labelFormatter
There was a problem hiding this 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)
components/dashboard/category-performance.tsx (1)
103-108: Past tooltip matching issue has been resolved.The
labelFormatternow correctly uses exactdisplayCategorymatching instead of substring comparison, eliminating the risk of returning the wrong category when multiple categories share the same 15-character prefix. This matches the fix described in commit964ef9b.
🧹 Nitpick comments (2)
components/dashboard/category-performance.tsx (2)
133-133: Avoid programmatic case conversion on translated strings.Applying
.toLowerCase()tot("ITEMS")is not i18n-friendly. Case conversion rules vary by locale (e.g., TurkishI→ı, German noun capitalization), and some languages lack case distinctions. Per coding guidelines, translators should control text presentation.Recommendation: Add a separate translation key (e.g.,
ITEMS_LOWERCASE) or remove the transformation and let translators provide the appropriate case in each locale.Alternative approaches
Option 1: Separate translation key
- {item.itemCount} {t("ITEMS").toLowerCase()} + {item.itemCount} {t("ITEMS_LOWER")}Option 2: Let translator control case in the original key
Update all locale files to provide lowercase "items" if that's the desired presentation, then remove.toLowerCase().
84-84: Consider dark-mode adaptive axis colors.The XAxis and YAxis use fixed
stroke="#6B7280"(gray-500), which doesn't adapt to dark mode unlike other UI elements in the component. While fixed axis colors are common in charts for consistency, you may want to use a CSS variable or conditional color for better dark-mode integration.Also applies to: 91-91
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
components/dashboard/approval-trend.tsxcomponents/dashboard/category-performance.tsxcomponents/dashboard/period-comparison.tsxhooks/index.tshooks/mock-dashboard-stats.ts
💤 Files with no reviewable changes (2)
- hooks/index.ts
- hooks/mock-dashboard-stats.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- components/dashboard/approval-trend.tsx
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use Node.js >= 20.19.0
Files:
components/dashboard/category-performance.tsxcomponents/dashboard/period-comparison.tsx
components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
components/**/*.{ts,tsx}: UI components should be placed in the components/** directory
Keep React components mostly presentational and lean, avoiding complex business logic
Files:
components/dashboard/category-performance.tsxcomponents/dashboard/period-comparison.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Prefer TypeScript files over JavaScript
Run pnpm tsc --noEmit for TypeScript type-checking
Files:
components/dashboard/category-performance.tsxcomponents/dashboard/period-comparison.tsx
components/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Place UI and layout components in components/**
Files:
components/dashboard/category-performance.tsxcomponents/dashboard/period-comparison.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer TypeScript for all code
Files:
components/dashboard/category-performance.tsxcomponents/dashboard/period-comparison.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Follow Prettier config with tabs, 4-space tabWidth, and 120-char printWidth
Prefer async/await over raw Promise chains in TypeScript/JavaScript code
Validate input with Zod where appropriate; follow existing schemas in lib/validations
Keep i18n-friendly: avoid hard-coded English strings in logic; use next-intl messages where relevant
Files:
components/dashboard/category-performance.tsxcomponents/dashboard/period-comparison.tsx
**/components/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/components/**/*.{jsx,tsx}: For forms, prefer react-hook-form + Zod; follow patterns in existing auth/profile forms
Place business logic in lib/services or lib/repositories, not in components; keep components mostly presentational and data-fetching
Files:
components/dashboard/category-performance.tsxcomponents/dashboard/period-comparison.tsx
🧬 Code graph analysis (2)
components/dashboard/category-performance.tsx (3)
hooks/use-dashboard-stats.ts (1)
CategoryPerformanceDataExport(13-13)components/dashboard/index.ts (1)
CategoryPerformance(11-11)components/dashboard/styles.ts (5)
CARD_BASE_STYLES(4-5)TITLE_STYLES(7-7)SUBTITLE_STYLES(9-9)TOOLTIP_STYLES(21-26)CHART_COLORS(28-39)
components/dashboard/period-comparison.tsx (4)
hooks/use-dashboard-stats.ts (1)
PeriodComparisonDataExport(12-12)env-config.ts (1)
value(28-39)components/dashboard/styles.ts (8)
METRIC_CARD_STYLES(13-13)METRIC_LABEL_STYLES(15-15)METRIC_VALUE_STYLES(17-17)METRIC_COMPARE_STYLES(19-19)CARD_BASE_STYLES(4-5)TITLE_STYLES(7-7)SUBTITLE_STYLES(9-9)SEMANTIC_COLORS(42-51)components/dashboard/index.ts (1)
PeriodComparison(10-10)
⏰ 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). (4)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Greptile Review
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Lint and Build
🔇 Additional comments (1)
components/dashboard/period-comparison.tsx (1)
1-165: LGTM! Well-structured component with excellent accessibility.This component is well-implemented with:
- Proper semantic HTML (
<section>,<h3>) and ARIA attributes (aria-labelledby,role="group")- Complete internationalization using next-intl
- Three well-defined states (loading, no-data, data-present)
- Appropriate use of centralized styles and semantic colors
- Clear component composition with focused sub-components
- Locale-aware number formatting via
toLocaleString()The past review concern about
role="list"has been correctly resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4 issues found across 29 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="components/dashboard/submission-calendar.tsx">
<violation number="1" location="components/dashboard/submission-calendar.tsx:54">
P2: Using `toISOString().split("T")[0]` can cause timezone issues where the displayed date differs from the user's local date. For users in timezones ahead of UTC, 'today' may show as yesterday during early morning hours. Use local date formatting instead to ensure consistency with user expectations.</violation>
</file>
<file name="components/dashboard/engagement-distribution.tsx">
<violation number="1" location="components/dashboard/engagement-distribution.tsx:105">
P2: The `labelFormatter` uses `startsWith` to reverse-lookup the original title, which can match the wrong item when multiple titles share the same 20-character prefix. Unlike `category-performance.tsx` which uses exact equality matching against `chartData`, this searches `data` with a partial match.
Consider matching by exact `displayTitle` against `chartData`:</violation>
</file>
<file name="components/dashboard/category-performance.tsx">
<violation number="1" location="components/dashboard/category-performance.tsx:133">
P2: Using `.toLowerCase()` on translated text breaks i18n for some languages (Turkish, German). Consider using ICU message format for proper pluralization: `{count, plural, one {# item} other {# items}}`.</violation>
</file>
<file name="components/dashboard/approval-trend.tsx">
<violation number="1" location="components/dashboard/approval-trend.tsx:99">
P2: Hardcoded color `#10B981` should use `SEMANTIC_COLORS.success` from the shared styles file to maintain consistency with the PR's goal of centralizing styles. This color appears in 4 places in this file. Consider importing and using the centralized constant for easier theme updates.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Summary
This PR enhances the client dashboard with 6 new analytics chart components, improves code maintainability through shared styles, adds accessibility features, and fixes UI bugs in existing charts.
New Features
1. Period Comparison Card
2. Category Performance Chart
3. Approval Rate Trend Chart
4. Submission Calendar Heatmap
5. Engagement Distribution Chart
6. Engagement Rate Chart
Refactoring Changes
1. Created Shared Styles File
Problem: Each dashboard chart component had duplicated style constants (card styles, title styles, tooltip styles, colors), violating the DRY principle and making theme updates difficult.
Impact: Centralized style management, easier theme updates, reduced code duplication across 6 components.
What we did:
components/dashboard/styles.tsDeleted: Local style constant definitions in each component (replaced with imports from styles.ts)
2. Added Accessibility Attributes
Problem: Chart components lacked proper accessibility attributes, making them difficult to navigate for screen reader users.
Impact: Improved accessibility compliance, better screen reader support, proper semantic HTML structure.
What we did:
<div>to<section>witharia-labelledbyattributeidattributes to heading elements for accessibility linking3. Replaced Hardcoded Text with Translations
Problem: Some text like "vs" and "Avg:" were hardcoded in English, breaking internationalization support.
Impact: Full i18n support for all dashboard text, consistent translation across 14 supported languages.
What we did:
COMMON.VSandCOMMON.AVGtranslations to all 14 locale filesperiod-comparison.tsxto usetCommon("VS")instead of hardcoded "vs"engagement-rate-chart.tsxto usetCommon("AVG")instead of hardcoded "Avg:"4. Fixed Locale-Aware Date Formatting
Problem:
submission-calendar.tsxused hardcoded "en-US" locale for date formatting, ignoring user's locale preference.Impact: Dates now display in user's preferred locale format.
What we did:
useLocale()hook from next-intlformatDatefunction to accept locale parameterBug Fixes
1. Fixed Pie Chart Label Overlap for 0% Values
Problem: In StatusBreakdown and EngagementChart pie charts, labels for 0% values (e.g., "Rejected", "Shares") would render as "Rejected0%" or "Shares0%", overlapping with adjacent labels.
Impact: Clean pie chart display without overlapping text when some categories have zero values.
What we did:
status-breakdown.tsxlabel function to return empty string for 0% valuesengagement-chart.tsxlabel function to return empty string for 0% values2. Fixed Submission Calendar Layout
Problem: Submission calendar was constrained to 50% width in a 2-column grid, causing excessive whitespace on the right side.
Impact: Better visual balance and use of available space.
What we did:
dashboard-content.tsxBackend Changes
Repository Layer Updates
File:
lib/repositories/client-dashboard.repository.tsTranslation Updates
Added new translation keys for all 6 chart components across 14 locale files:
Files Changed
New Files (7):
components/dashboard/styles.tscomponents/dashboard/period-comparison.tsxcomponents/dashboard/category-performance.tsxcomponents/dashboard/approval-trend.tsxcomponents/dashboard/submission-calendar.tsxcomponents/dashboard/engagement-distribution.tsxcomponents/dashboard/engagement-rate-chart.tsxModified Files (21):
components/dashboard/dashboard-content.tsxcomponents/dashboard/engagement-chart.tsxcomponents/dashboard/status-breakdown.tsxcomponents/dashboard/index.tshooks/use-dashboard-stats.tshooks/mock-dashboard-stats.tslib/repositories/client-dashboard.repository.tsmessages/ar.jsonmessages/de.jsonmessages/en.jsonmessages/es.jsonmessages/fr.jsonmessages/id.jsonmessages/it.jsonmessages/ja.jsonmessages/ko.jsonmessages/nl.jsonmessages/pl.jsonmessages/pt.jsonmessages/ru.jsonmessages/zh.jsonSummary by cubic
Enhances the client dashboard with six new analytics charts and improves design, accessibility, and i18n. Also adds backend data support and fixes chart label and layout issues.
New Features
Refactors
Written for commit 7aa7b95. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes
Internationalization
✏️ Tip: You can customize this high-level summary in your review settings.