feat: Add advanced shift statistics with charts and i18n updates#80
feat: Add advanced shift statistics with charts and i18n updates#80
Conversation
Adds richer shift analytics and visualization support. Adds daily breakdown and trend data, total/min/max durations, and averaged metrics (per-shift and per-day) in the stats API while improving date handling and excluding all-day shifts from duration metrics. Enhances the UI with new view modes (overview, pie, bar, radar), prepared datasets and custom tooltips, summary cards and detailed lists for better insights, and integrates interactive charts via Recharts. Also centralizes weekday translations to the common namespace. Includes Recharts in dependencies and updates lockfile to ship the new charting library, enabling better trend visualization and more actionable shift statistics.
|
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. WalkthroughThe PR adds per-shift duration and daily-trend aggregations to the stats API, expands the ShiftStats UI with overview/pie/bar/radar chart views (using Recharts), restructures weekday and stats i18n keys, and adds Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Browser UI (ShiftStats)
participant API as app/api/shifts/stats/route
participant DB as Database
Note over Client,API: User opens ShiftStats (viewMode selected)
Client->>API: GET /api/shifts/stats?range...
API->>DB: Query shifts (include id, date, start, end, allDay, title...)
DB-->>API: shifts[]
API->>API: compute durations, per-title aggregates, dailyStats, totals/averages
API-->>Client: JSON { stats, totalShifts, trendData, ... }
Client->>Client: build pieData/barData/radarData from stats
Client->>Recharts: render selected chart (pie/bar/radar) or Overview
Note over Client,Recharts: Interactive tooltip/legend driven by prepared data
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
Possibly related PRs
Suggested labels
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.
Actionable comments posted: 3
🧹 Nitpick comments (3)
components/shift-stats.tsx (3)
176-206: ExtractCustomTooltipoutside the component to avoid re-creation on each render.Defining
CustomTooltipinside the component creates a new function reference on every render, which can cause unnecessary re-renders of chart components that use it.Move the tooltip component and its type definition outside
ShiftStats:interface TooltipPayloadEntry { name: string; value: number; color: string; dataKey?: string; payload: { fullName?: string; name?: string }; } const CustomTooltip = ({ active, payload, }: { active?: boolean; payload?: TooltipPayloadEntry[]; }) => { if (active && payload && payload.length) { return ( <div className="bg-card/95 backdrop-blur-sm border border-border rounded-lg p-3 shadow-lg"> <p className="font-semibold text-sm mb-1"> {payload[0].payload.fullName || payload[0].name} </p> {payload.map((entry) => ( <p key={entry.name} className="text-xs text-muted-foreground"> <span style={{ color: entry.color }}>{entry.name}:</span>{" "} {entry.value} {entry.name === "hours" || entry.dataKey === "hours" ? "h" : ""} </p> ))} </div> ); } return null; };
501-527: Consider the UX of mixing shifts count and hours on the same axis.The bar chart displays "shifts" (count) and "hours" (duration) on the same Y-axis. These have different units and scales, which may be confusing for users when comparing bars.
This is a minor UX consideration — the current approach works but a dual-axis chart or separate charts could provide clearer comparisons.
573-594: Legend items all display the same color, which doesn't add visual distinction.All radar legend items use
CHART_COLORS[0](blue), making the color indicators redundant. Consider either:
- Assigning different colors per entry (like in the pie chart legend)
- Removing the color indicator since the radar chart distinguishes by series color, not by entry
{radarData.map((entry, index) => ( <div key={entry.fullName} className="flex items-center gap-2 p-2 rounded-lg bg-card/50 border border-border/30" > <div className="w-3 h-3 rounded-sm flex-shrink-0" style={{ - backgroundColor: CHART_COLORS[0], + backgroundColor: CHART_COLORS[index % CHART_COLORS.length], }} />
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (7)
app/api/shifts/stats/route.ts(3 hunks)components/calendar-grid.tsx(1 hunks)components/shift-stats.tsx(6 hunks)messages/de.json(3 hunks)messages/en.json(3 hunks)messages/it.json(3 hunks)package.json(1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: UseformatDateToLocal()utility function for consistent date formatting in YYYY-MM-DD format
Centralize validation messages usingvalidation.*namespace keys (e.g.,validation.passwordMatch,validation.fileTooLarge) instead of creating message-specific keys
When checking calendar lock status, useisLockedfield to determine if read access requires password; when checking write protection, check onlypasswordHashfield
Hash passwords using SHA-256 via utilities inlib/password-utils.ts
Only add comments for complex logic or non-obvious behavior - write self-documenting code with clear variable and function names
Files:
components/calendar-grid.tsxapp/api/shifts/stats/route.tscomponents/shift-stats.tsx
components/**/*.tsx
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
components/**/*.tsx: Store hex colors in format#3b82f6and apply 20% opacity for backgrounds using${color}20pattern
All dialogs must follow the unified design pattern with gradient backgrounds, consistent padding (p-6, px-6, pb-6), border-border/50 styling, backdrop blur, and gradient text for titles
Centralize form field labels and placeholders usingform.*namespace keys (e.g.,form.nameLabel,form.namePlaceholder) instead of duplicating across components
Control dialog state via props (open, onOpenChange), reset local state whenopenchanges to false, and useonSubmitand optionalonDeletecallbacks
Implement calendar interactions with left-click to toggle shift with selected preset, right-click to open note dialog (prevent default context menu), and delete if exists/create if not logic
UsePRESET_COLORSarray for color selection in components
UseshowMobileCalendarDialogto separate calendar selector logic for mobile UI
Files:
components/calendar-grid.tsxcomponents/shift-stats.tsx
**/*.tsx
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use centralized common messages with parametrized
{item}parameter for CRUD operations instead of creating message-specific keys - usecommon.created,common.updated,common.deleted,common.createError,common.updateError,common.deleteError
Files:
components/calendar-grid.tsxcomponents/shift-stats.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
All code, comments, variable names, and user-facing messages must be in English
Files:
components/calendar-grid.tsxapp/api/shifts/stats/route.tscomponents/shift-stats.tsx
app/api/**/route.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
app/api/**/route.ts: Implement password protection in API routes following two-tier model: GET endpoints check bothpasswordHashANDisLocked, mutation endpoints (POST/PUT/PATCH/DELETE) check onlypasswordHash
All API endpoints must handle requests and return NextResponse.json() with appropriate error status codes (400 for bad requests, 401 for authentication failures)
Files:
app/api/shifts/stats/route.ts
messages/{de,en,it}.json
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Add all new translation keys to all three message files:
messages/de.json,messages/en.json, andmessages/it.jsonsimultaneously
Files:
messages/en.jsonmessages/de.jsonmessages/it.json
messages/de.json
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use informal 'du' form (never formal 'Sie' form) in all German translations - examples: 'Möchtest du...' not 'Möchten Sie...', 'Bitte entsperre...' not 'Bitte entsperren Sie...'
Files:
messages/de.json
🧠 Learnings (9)
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to **/*.{ts,tsx} : Use `formatDateToLocal()` utility function for consistent date formatting in YYYY-MM-DD format
Applied to files:
components/calendar-grid.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to **/*.tsx : Use centralized common messages with parametrized `{item}` parameter for CRUD operations instead of creating message-specific keys - use `common.created`, `common.updated`, `common.deleted`, `common.createError`, `common.updateError`, `common.deleteError`
Applied to files:
components/calendar-grid.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to components/**/*.tsx : Use `showMobileCalendarDialog` to separate calendar selector logic for mobile UI
Applied to files:
components/calendar-grid.tsxcomponents/shift-stats.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to app/**/*.tsx : Use `useTranslations()` hook from next-intl for all user-facing text in React components
Applied to files:
components/calendar-grid.tsxcomponents/shift-stats.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to lib/**/*.ts : Use locale-specific date formatting: `de` for German dates, `it` for Italian dates, `enUS` for English dates in date formatting utilities
Applied to files:
components/calendar-grid.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to components/**/*.tsx : Implement calendar interactions with left-click to toggle shift with selected preset, right-click to open note dialog (prevent default context menu), and delete if exists/create if not logic
Applied to files:
components/calendar-grid.tsxcomponents/shift-stats.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to app/page.tsx : Use `useState` for shifts, presets, notes, and calendars; `useEffect` for data fetching on calendar/date changes; `useRouter().replace()` for URL state sync; `statsRefreshTrigger` counter for mutation tracking
Applied to files:
app/api/shifts/stats/route.tscomponents/shift-stats.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to app/**/*.tsx : Listen to Server-Sent Events (SSE) for real-time updates on shift, preset, note, and sync-log changes, using silent refresh patterns with `fetchData(false)` and counter-based refresh triggers
Applied to files:
components/shift-stats.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to messages/{de,en,it}.json : Add all new translation keys to all three message files: `messages/de.json`, `messages/en.json`, and `messages/it.json` simultaneously
Applied to files:
messages/en.jsonmessages/it.json
🧬 Code graph analysis (2)
app/api/shifts/stats/route.ts (2)
lib/db/schema.ts (1)
shifts(49-82)lib/date-utils.ts (1)
calculateShiftDuration(18-56)
components/shift-stats.tsx (3)
components/ui/button.tsx (1)
Button(60-60)components/ui/skeleton.tsx (1)
Skeleton(13-13)lib/date-utils.ts (1)
formatDuration(63-73)
⏰ 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: Agent
- GitHub Check: build-dev
🔇 Additional comments (15)
messages/de.json (2)
27-36: LGTM! Weekday translations centralized correctly.The weekday abbreviations are properly moved to
common.weekdaynamespace with correct German abbreviations (Mo, Di, Mi, Do, Fr, Sa, So), aligning with the other locale files.
187-199: Stats translations look good.The new statistics keys are properly translated to German with appropriate informal tone. The ⌀ symbol usage is consistent across locales.
messages/it.json (2)
27-36: LGTM! Italian weekday translations correctly centralized.The weekday abbreviations are properly placed in
common.weekdaynamespace with correct Italian abbreviations.
186-198: Stats translations are complete and consistent.All new statistics keys are present and properly translated to Italian, maintaining consistency with the other locale files.
components/calendar-grid.tsx (1)
109-117: LGTM! Translation keys correctly updated to centralized namespace.The weekday translation keys are properly migrated from
calendar_grid.*tocommon.weekday.*, aligning with the i18n restructuring in this PR.messages/en.json (2)
27-36: LGTM! Weekday translations properly centralized.The English weekday abbreviations are correctly placed in the
common.weekdaynamespace, providing a single source of truth for weekday labels across the application.
187-199: Stats translations complete with all required keys.All new statistics view keys are present and properly structured to support the enhanced statistics UI with multiple chart views.
app/api/shifts/stats/route.ts (3)
86-88: LGTM! Extended select fields for enhanced statistics.Adding
idanddatefields enables proper shift tracking for trend analysis and min/max calculations.
116-121: Good initialization for tracking daily stats and duration bounds.The data structures are properly initialized for accumulating daily statistics and tracking min/max durations.
182-220: LGTM! Comprehensive statistics calculation and response.The metrics calculations are well-structured:
avgMinutesPerShiftis rounded to whole numbersavgShiftsPerDaypreserves one decimal place for accuracyminDurationcorrectly defaults to 0 when no valid durations existtrendDatais sorted chronologically for visualizationThe response structure provides all necessary data for the enhanced charts UI.
package.json (1)
47-47: Recharts 3.6.0 requires special handling for React 19 compatibility.Recharts with React 19 requires overriding the react-is dependency, and the react-is version must match the version of React 19 being used. Add this to package.json overrides:
"overrides": { "react-is": "^19.2.1" }Alternatively, if using yarn, use resolutions. Recharts currently works with React 17, 18 and 19, but the react-is override is necessary for proper React 19 support.
components/shift-stats.tsx (4)
6-33: LGTM on imports.The imports are well-organized, separating lucide-react icons from Recharts components. The aliasing of
RadartoRadarIconfrom lucide-react avoids conflict with Recharts'Radarcomponent.
59-72: LGTM on color constants.The
CHART_COLORSarray follows the coding guidelines with hex colors in#3b82f6format, providing a good variety for chart elements.
142-174: LGTM on chart data preparation.The data transformations are well-structured. The truncation of long names with
...suffix provides good UX for chart labels. Sorting and limiting radar data to top 6 types keeps the chart readable.
41-48: Several fetched metrics are not displayed in the UI.The interface defines
avgShiftsPerDay,avgMinutesPerDay,daysWithShifts, andtrendData, which are fetched from the API (as shown by the debug log) but not rendered anywhere.Is this intentional for future use, or should these metrics be displayed? Consider either:
- Adding UI elements to show these metrics
- Removing unused fields from the interface if not planned
- Adding a TODO comment if this is planned for a follow-up
There was a problem hiding this comment.
Pull request overview
This pull request adds advanced shift statistics with interactive charts and visualization capabilities to BetterShift. It introduces the Recharts library to enable rich data visualization (pie charts, bar charts, and radar charts) and enhances the stats API to provide comprehensive metrics including daily breakdown, trend data, min/max durations, and averaged metrics per shift and per day. The PR also improves i18n consistency by centralizing weekday translations from the calendar_grid namespace to the common namespace, making them reusable across components.
Key Changes:
- Adds Recharts library (v3.6.0) with all dependencies for data visualization
- Enhances stats API with 7 new metrics: totalShifts, avgMinutesPerShift, avgShiftsPerDay, avgMinutesPerDay, minDuration, maxDuration, daysWithShifts, and trendData array
- Introduces 4 view modes in shift statistics component: overview, pie chart, bar chart, and radar chart for different data perspectives
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Adds recharts dependency (v3.6.0) to enable chart visualization features |
| package-lock.json | Updates lockfile with recharts and all transitive dependencies including d3 libraries, Redux toolkit, and react-redux |
| messages/de.json | Moves weekday translations to common namespace and adds 12 new statistics translation keys for German |
| messages/en.json | Moves weekday translations to common namespace and adds 12 new statistics translation keys for English |
| messages/it.json | Moves weekday translations to common namespace and adds 12 new statistics translation keys for Italian |
| components/shift-stats.tsx | Major refactor: adds 4 visualization modes (overview/pie/bar/radar), integrates Recharts components with custom tooltips, and displays enhanced statistics with summary cards |
| components/calendar-grid.tsx | Updates weekday translation keys from calendar_grid namespace to common.weekday namespace |
| app/api/shifts/stats/route.ts | Enhances stats calculation with daily breakdown tracking, min/max duration computation, and trend data generation for visualization |
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
app/api/shifts/stats/route.ts (1)
141-166: Past issue resolved: daily breakdown now uses correct date field.The code now correctly uses
shift.dateas the sole date source for daily breakdown, addressing the previous concern about incorrectly usingstartTime/endTime(which are HH:MM time strings). The date parsing logic properly handles Date objects, Unix timestamps in seconds, and string values.Optional: Consider adding error logging for invalid dates.
The empty catch block at line 163 silently skips invalid dates. While this is acceptable for the current use case, adding a
console.warnor conditional debug logging could help diagnose data quality issues in production.Apply this diff if you want to add debug logging:
} catch { // Skip invalid dates + if (process.env.NODE_ENV === 'development') { + console.warn('Invalid date encountered for shift:', shift.id, shiftDate); + } }components/shift-stats.tsx (2)
17-39: Excellent SSR-safe implementation!This hook properly addresses the previous concern about direct
window.innerWidthaccess causing SSR hydration issues. The safe default value (120) ensures server-side rendering works correctly, and the resize listener enables responsive behavior on the client.
163-196: LGTM! Safe data preparation with proper division guards.The chart data preparation correctly handles division by zero at lines 192-194 with the guard
data.count ? ... : 0, addressing the previous concern. All three data pipelines (pie, bar, radar) properly transform the stats into chart-friendly formats with appropriate name truncation for UI display.
🧹 Nitpick comments (1)
components/shift-stats.tsx (1)
453-513: Pie chart implementation is functional with legend fallback.The pie chart correctly uses the SSR-safe
outerRadiushook and provides a legend grid below for accessibility. While SVG text labels (lines 468-469) may have limited screen reader support, the legend grid provides a text-based alternative.Optional: Consider ARIA enhancements for improved accessibility.
The legend grid partially addresses screen reader accessibility, but adding explicit ARIA attributes to the chart container could further improve the experience for assistive technology users.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
app/api/shifts/stats/route.ts(3 hunks)components/shift-stats.tsx(5 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
app/api/**/route.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
app/api/**/route.ts: Implement password protection in API routes following two-tier model: GET endpoints check bothpasswordHashANDisLocked, mutation endpoints (POST/PUT/PATCH/DELETE) check onlypasswordHash
All API endpoints must handle requests and return NextResponse.json() with appropriate error status codes (400 for bad requests, 401 for authentication failures)
Files:
app/api/shifts/stats/route.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: UseformatDateToLocal()utility function for consistent date formatting in YYYY-MM-DD format
Centralize validation messages usingvalidation.*namespace keys (e.g.,validation.passwordMatch,validation.fileTooLarge) instead of creating message-specific keys
When checking calendar lock status, useisLockedfield to determine if read access requires password; when checking write protection, check onlypasswordHashfield
Hash passwords using SHA-256 via utilities inlib/password-utils.ts
Only add comments for complex logic or non-obvious behavior - write self-documenting code with clear variable and function names
Files:
app/api/shifts/stats/route.tscomponents/shift-stats.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
All code, comments, variable names, and user-facing messages must be in English
Files:
app/api/shifts/stats/route.tscomponents/shift-stats.tsx
components/**/*.tsx
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
components/**/*.tsx: Store hex colors in format#3b82f6and apply 20% opacity for backgrounds using${color}20pattern
All dialogs must follow the unified design pattern with gradient backgrounds, consistent padding (p-6, px-6, pb-6), border-border/50 styling, backdrop blur, and gradient text for titles
Centralize form field labels and placeholders usingform.*namespace keys (e.g.,form.nameLabel,form.namePlaceholder) instead of duplicating across components
Control dialog state via props (open, onOpenChange), reset local state whenopenchanges to false, and useonSubmitand optionalonDeletecallbacks
Implement calendar interactions with left-click to toggle shift with selected preset, right-click to open note dialog (prevent default context menu), and delete if exists/create if not logic
UsePRESET_COLORSarray for color selection in components
UseshowMobileCalendarDialogto separate calendar selector logic for mobile UI
Files:
components/shift-stats.tsx
**/*.tsx
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use centralized common messages with parametrized
{item}parameter for CRUD operations instead of creating message-specific keys - usecommon.created,common.updated,common.deleted,common.createError,common.updateError,common.deleteError
Files:
components/shift-stats.tsx
🧠 Learnings (6)
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to app/page.tsx : Use `useState` for shifts, presets, notes, and calendars; `useEffect` for data fetching on calendar/date changes; `useRouter().replace()` for URL state sync; `statsRefreshTrigger` counter for mutation tracking
Applied to files:
app/api/shifts/stats/route.tscomponents/shift-stats.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to **/*.{ts,tsx} : Use `formatDateToLocal()` utility function for consistent date formatting in YYYY-MM-DD format
Applied to files:
app/api/shifts/stats/route.ts
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to app/**/*.tsx : Listen to Server-Sent Events (SSE) for real-time updates on shift, preset, note, and sync-log changes, using silent refresh patterns with `fetchData(false)` and counter-based refresh triggers
Applied to files:
components/shift-stats.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to app/**/*.tsx : Use `useTranslations()` hook from next-intl for all user-facing text in React components
Applied to files:
components/shift-stats.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to components/**/*.tsx : Implement calendar interactions with left-click to toggle shift with selected preset, right-click to open note dialog (prevent default context menu), and delete if exists/create if not logic
Applied to files:
components/shift-stats.tsx
📚 Learning: 2025-12-15T00:16:49.617Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-15T00:16:49.617Z
Learning: Applies to components/**/*.tsx : Use `showMobileCalendarDialog` to separate calendar selector logic for mobile UI
Applied to files:
components/shift-stats.tsx
🧬 Code graph analysis (1)
app/api/shifts/stats/route.ts (2)
lib/db/schema.ts (1)
shifts(49-82)lib/date-utils.ts (1)
calculateShiftDuration(18-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-dev
🔇 Additional comments (7)
app/api/shifts/stats/route.ts (2)
181-196: LGTM! Safe division operations and proper averaging logic.All division operations are properly guarded against zero divisors. The calculations for per-shift and per-day averages correctly use
daysWithShiftsfor accurate daily metrics rather than calendar days in the period.
206-220: LGTM! Comprehensive response with proper guards.The response correctly includes all new metrics with appropriate rounding. The
minDuration === Infinity ? 0 : minDurationguard at line 216 prevents invalid values when no timed shifts exist.components/shift-stats.tsx (5)
41-97: LGTM! Well-structured types and color palette.The extended
ShiftStatsinterface correctly matches the API response, and theCHART_COLORSarray follows the coding guideline format (#3b82f6) with sufficient variety for chart visualizations.
198-228: LGTM! Well-typed and styled tooltip component.The CustomTooltip component provides clear type definitions and consistent styling with the app's design system. The payload type is appropriately specific for the current chart data structures.
301-341: LGTM! Responsive view mode selector with proper conditional rendering.The view mode selector correctly renders only when data exists and provides responsive layout for mobile and desktop viewports.
351-451: LGTM! Comprehensive overview mode with polished UI.The overview mode provides clear metric cards and a detailed per-type breakdown with visual progress indicators. The conditional rendering of min/max durations and responsive design are well-implemented.
515-624: LGTM! Well-implemented bar and radar visualizations.Both chart modes provide clear data comparisons with appropriate responsive containers, tooltips, and legends. The radar chart's focus on top 6 shift types with dual metrics (total hours and average per shift) provides valuable performance insights.
Adds richer shift analytics and visualization support.
Adds daily breakdown and trend data, total/min/max durations, and averaged metrics (per-shift and per-day) in the stats API while improving date handling and excluding all-day shifts from duration metrics.
Enhances the UI with new view modes (overview, pie, bar, radar), prepared datasets and custom tooltips, summary cards and detailed lists for better insights, and integrates interactive charts via Recharts. Also centralizes weekday translations to the common namespace.
Includes Recharts in dependencies and updates lockfile to ship the new charting library, enabling better trend visualization and more actionable shift statistics.
Summary by CodeRabbit
New Features
Localization
✏️ Tip: You can customize this high-level summary in your review settings.