Skip to content

feat: add time-series chart to stats page#9

Merged
Divkix merged 12 commits intomainfrom
feat/time-series-chart
Jan 14, 2026
Merged

feat: add time-series chart to stats page#9
Divkix merged 12 commits intomainfrom
feat/time-series-chart

Conversation

@Divkix
Copy link
Owner

@Divkix Divkix commented Jan 14, 2026

Summary

  • Add area chart showing log volume over time to the stats page
  • Users can identify patterns like "why did errors spike at 3am?" or "when did traffic drop?"
  • Chart updates automatically when time range changes

Changes

  • Utility Functions (src/lib/utils/timeseries.ts): Time bucketing utilities for grouping logs
  • API Endpoint (/api/projects/[id]/stats/timeseries): Returns time-bucketed log counts
  • Chart Component (src/lib/components/timeseries-chart.svelte): LayerChart-based area chart
  • Page Integration: Added "Logs Over Time" section below the level distribution chart

Test plan

  • Unit tests: 17 tests for utility functions
  • Integration tests: 14 tests for API endpoint
  • Component tests: 22 tests for chart component
  • E2E tests: 4 tests for page integration
  • Type check passes
  • Lint passes
  • Manual test: Chart visible on stats page
  • Manual test: Chart updates on range change
  • Manual test: Works on mobile (touch tooltips)

Add utility functions for time-series chart implementation:
- getTimeBucketConfig: returns bucket interval and count for time range
- bucketTimestamps: groups timestamps into bucket indices
- fillMissingBuckets: fills gaps with zero counts
- formatBucketLabel: formats bucket timestamps for chart axis

Install layerchart@1.0.13 for charting capabilities.

Includes 17 unit tests covering all edge cases.
Add GET /api/projects/[id]/stats/timeseries endpoint that returns
time-bucketed log counts for visualization in an area chart.

Features:
- Supports 15m, 1h, 24h, 7d time ranges with appropriate granularity
- Returns buckets array with timestamp and count for each interval
- Properly filters by project ID and time range
- Handles empty state with zero-filled buckets

Includes 14 integration tests covering:
- Authentication requirements
- Project validation (404 for non-existent)
- All time range variations
- Bucket ordering and timestamp validation
- Project isolation (no cross-project data leakage)
Add area chart component for displaying log volume over time:
- Uses LayerChart with dynamic imports (browser-only rendering)
- Time scale X-axis with appropriate formatting per time range
- Touch-friendly tooltips using mode: 'band'
- Loading skeleton, error, and empty states
- Responsive design with CSS custom properties for theming

Includes 22 component tests covering:
- Container rendering and accessibility attributes
- Loading, error, and empty state displays
- Different time range support
- Edge cases (large counts, zero data, single point)
Add "Logs Over Time" area chart section to the stats page:
- Fetches timeseries data from API on mount and range change
- Displays area chart below the level distribution donut chart
- Handles loading, error, and empty states
- Responsive design for mobile and desktop

Add @types/d3-scale for TypeScript support.
Add end-to-end tests for the new timeseries chart feature:

Stats Page - Timeseries Chart:
- should display timeseries chart on stats page
- should show loading state then chart
- should update timeseries chart when time range changes

Stats Page - Timeseries Empty State:
- should display empty state when no logs exist

Tests verify chart visibility, loading states, API interactions,
and time range changes.
Auto-format code changes from biome linter.
Copilot AI review requested due to automatic review settings January 14, 2026 04:45
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a time-series area chart to the stats page that visualizes log volume over time across different time ranges (15m, 1h, 24h, 7d). The implementation follows a thorough TDD approach with comprehensive test coverage at all levels.

Changes:

  • Added time-bucketing utility functions for grouping logs into time intervals
  • Created new API endpoint /api/projects/[id]/stats/timeseries for fetching time-bucketed log counts
  • Implemented LayerChart-based area chart component with loading, error, and empty states
  • Integrated chart into stats page with automatic updates when time range changes

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/lib/utils/timeseries.ts Utility functions for time bucketing with 4 exported functions
src/lib/utils/timeseries.unit.test.ts 17 unit tests covering all utility functions
src/routes/api/projects/[id]/stats/timeseries/+server.ts API endpoint with authentication and project validation
tests/integration/api/projects/[id]/stats/timeseries.integration.test.ts 14 integration tests for API endpoint
src/lib/components/timeseries-chart.svelte Chart component with loading/error/empty states
src/lib/components/__tests__/timeseries-chart.component.test.ts 22 component tests
src/routes/(app)/projects/[id]/stats/+page.svelte Page integration with $effect for reactivity
tests/e2e/stats.spec.ts 4 E2E tests for user interactions
package.json Added layerchart@1.0.13 and @types/d3-scale@4.0.9
bun.lock Lock file updates for new dependencies

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

CSS custom properties (var(--primary)) don't work inside LayerChart's
SVG/D3 rendering context. Replace with actual HSL color value that
works in both light and dark modes.
The donut chart and timeseries chart were using different time ranges
because they calculated the 'from' timestamp independently. This caused
logs to appear in the donut but not in the timeseries for narrow ranges
like 15m.

Fix by:
- Page server now returns the exact 'from' timestamp it used
- Timeseries API accepts optional 'from' parameter
- Page passes the 'from' timestamp to ensure both use the same range
@Divkix Divkix force-pushed the feat/time-series-chart branch from 7ad4787 to 6c076af Compare January 14, 2026 04:54
@gitguardian
Copy link

gitguardian bot commented Jan 14, 2026

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
24037119 Triggered Generic Password 3b8122b tests/integration/api/projects/[id]/stats/timeseries.integration.test.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Move waitForResponse() listeners before page.goto() to ensure the
response is captured regardless of timing. The previous approach
could miss responses on slower CI machines where the API call
completed before the listener was set up.

Affected tests:
- should show loading state then chart
- should update timeseries chart when time range changes
- should display empty state when no logs exist
…ltip

- select.svelte: Replace `bind:value={value as never}` with function binding
  pattern to satisfy Svelte 5's stricter bind expression validation while
  working around bits-ui discriminated union type inference issues

- timeseries-chart.svelte: Add explicit type annotation to tooltip snippet
  parameter to fix implicit any error with noImplicitAny enabled
Add layerchart and @types/d3-scale to ignoreDependencies in knip.json.
These packages are imported dynamically in timeseries-chart.svelte and
are incorrectly flagged as unused by knip's static analysis.
@Divkix Divkix merged commit 4ab7adb into main Jan 14, 2026
6 of 7 checks passed
@Divkix Divkix deleted the feat/time-series-chart branch January 14, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant