Skip to content

feat(dashboard): polish charts, add time ranges, and visual refinements#17

Open
whoabuddy wants to merge 6 commits intomainfrom
feat/dashboard-polish
Open

feat(dashboard): polish charts, add time ranges, and visual refinements#17
whoabuddy wants to merge 6 commits intomainfrom
feat/dashboard-polish

Conversation

@whoabuddy
Copy link

Summary

  • Split single line chart into stacked Errors/Warnings + Traffic Volume charts with independent y-axes for readability
  • Add 7d/14d/30d time-range toggle to app-detail and overview pages via Alpine.js + existing ?days=N API
  • Add grouped bar chart to overview page showing per-app errors/warnings over time
  • Wire sparklines into stats cards, add error rate percentages, branded footer, improved empty states, and table hover transitions
  • Refactor chart configs to extract shared helpers, reducing ~91 lines of duplication

Test plan

  • npm test passes (all 35 tests)
  • Visit /dashboard overview — grouped bar chart renders with time-range toggle
  • Visit /dashboard/app/:id — two stacked charts render, time-range toggle works
  • Sparklines appear in stats cards on both pages
  • Error rate percentages display correctly
  • Empty states render when no apps/logs exist
  • Footer appears on all dashboard pages

🤖 Generated with Claude Code

whoabuddy and others added 6 commits March 12, 2026 16:56
…affic charts

Errors and warnings were invisible when info/debug counts were 10-50x larger
because all levels shared one y-axis. This splits the chart into two vertically
stacked panels with independent y-axes:

- Top panel "Errors & Warnings" (h-40): red + yellow datasets, x-axis labels
  hidden to avoid duplication
- Bottom panel "Traffic Volume" (h-48): blue info + gray debug datasets, with
  x-axis labels visible

Both panels share the same date labels for visual time-correlation. The new
splitDailyStatsChartConfigs() helper in charts.ts returns two separate Chart.js
config JSON strings; the existing dailyStatsChartConfig() is preserved.

Co-Authored-By: Claude <noreply@anthropic.com>
Adds three toggle buttons above the split Chart.js charts that let users
switch the stats window without a page reload. Clicking a button calls
loadStats() which fetches /dashboard/api/stats/:app_id?days=N and updates
both chart instances in-place (data.labels + data.datasets + chart.update()).

Stats cards and chart section title now reflect the active range dynamically
via Alpine.js x-text bindings. statsTotals state recalculates from the API
response on each range change so the four summary cards stay in sync.

Module-level ewChart/trafficChart vars are assigned in DOMContentLoaded and
closed over by the Alpine loadStats() method so no DOM query is needed on
each fetch. No backend changes required — the DO already supports ?days=N.

Co-Authored-By: Claude <noreply@anthropic.com>
…ge toggle

Extends the overview page with a grouped bar chart showing per-app errors
and warnings over the selected time range (7d/14d/30d). The chart updates
in-place when the range changes, reusing the same Alpine.js + Chart.js
in-place update pattern from the app-detail page.

- Add overviewBarChartConfig() to charts.ts for grouped bar chart config
- Add AppChartData/OverviewChartResponse types to dashboard/types.ts
- Add getOverviewChart() to api/overview.ts for multi-day per-day stats
- Add /dashboard/api/overview/chart?days=N endpoint to index.ts
- Update overview page with chart section, range buttons, and chart init

Co-Authored-By: Claude <noreply@anthropic.com>
Add sparkline trend visualizations to all stats cards on both overview
and app-detail pages, pulling from existing sparkline() utility that was
previously unused. Error cards now also display an error rate percentage
("X% of all logs") to give context alongside raw counts.

- Extend statsCard() to accept optional sparklineHtml and subtext params
- Overview: 2-point sparklines (yesterday→today) for errors/warnings/info
- App-detail: full multi-day sparklines from stats[] history per level
- App-detail stats cards now use card-glow class for consistency
- Error rate shown on error card when >0%

Co-Authored-By: Claude <noreply@anthropic.com>
…ansitions

Final visual polish sweep to make the dashboard presentation-ready:

- Add branded footer() to layout.ts, rendered in every page via htmlDocument()
  showing brand name, service name, and "Powered by Cloudflare Workers & Durable Objects"
- Improve emptyState() helper to accept an optional hint text below the message
- Overview: use emptyState() with SVG icons for "no apps" and "no recent errors" states
- App-detail: upgrade "no logs found" state with clipboard icon, message, and filter hint
- Add log-row CSS class with 0.1s ease background transition for smooth table hovers
- Apply log-row class to all data rows in overview tables
- Tune app-detail stats cards: use var(--text-muted) for labels and add card-glow class

Co-Authored-By: Claude <noreply@anthropic.com>
Extract shared chart helpers (lineDataset, barDataset, CHART_SCALES,
CHART_BASE_OPTIONS), consolidate repeated sparkline options, remove
dead code in overview refresh, and add addStats helper. Net -91 lines.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 13, 2026 04:16
@cloudflare-workers-and-pages
Copy link

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
worker-logs-production 6c9b110 Mar 13 2026, 04:15 AM

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 enhances the dashboard with visual improvements including split charts, time-range toggles, sparklines, and better empty states. It refactors chart configuration into shared helpers and adds a new overview bar chart endpoint.

Changes:

  • Split the single line chart into stacked Errors/Warnings + Traffic Volume charts with independent y-axes, added time-range toggle (7d/14d/30d) on both overview and app-detail pages
  • Added a new /dashboard/api/overview/chart endpoint and grouped bar chart to the overview page showing per-app errors/warnings over time
  • Added sparklines to stats cards, error rate percentages, branded footer, improved empty states with icons/hints, and table hover transitions

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/dashboard/types.ts New AppChartData and OverviewChartResponse types for the overview bar chart
src/dashboard/styles.ts Added CSS transition for table row hover
src/dashboard/components/layout.ts Extended statsCard with sparkline/subtext, added hint to emptyState, new footer component
src/dashboard/components/charts.ts Extracted shared chart helpers (lineDataset, barDataset, CHART_SCALES, CHART_BASE_OPTIONS), added splitDailyStatsChartConfigs and overviewBarChartConfig
src/dashboard/api/overview.ts New getOverviewChart function, addStats helper to reduce duplication
src/dashboard/index.ts New /api/overview/chart route
src/dashboard/pages/overview.ts Overview page updated with sparklines, error rates, bar chart, time-range toggle, improved empty states
src/dashboard/pages/app-detail.ts App-detail page updated with split charts, time-range toggle, sparklines, inline stats cards

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

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +74 to +94
<div class="brand-card card-glow rounded-lg p-4">
<div class="text-xs mb-1" style="color: var(--text-muted);">Debug (<span x-text="statsRange + 'd'">7d</span>)</div>
<div class="text-2xl font-bold text-gray-400" x-text="statsTotals.debug">${totals.debug}</div>
<div class="mt-2 h-5 w-full opacity-70">${debugSparkline}</div>
</div>
<div class="brand-card card-glow rounded-lg p-4">
<div class="text-xs mb-1" style="color: var(--text-muted);">Info (<span x-text="statsRange + 'd'">7d</span>)</div>
<div class="text-2xl font-bold text-blue-400" x-text="statsTotals.info">${totals.info}</div>
<div class="mt-2 h-5 w-full opacity-70">${infoSparkline}</div>
</div>
<div class="brand-card card-glow rounded-lg p-4">
<div class="text-xs mb-1" style="color: var(--text-muted);">Warn (<span x-text="statsRange + 'd'">7d</span>)</div>
<div class="text-2xl font-bold text-yellow-400" x-text="statsTotals.warn">${totals.warn}</div>
<div class="mt-2 h-5 w-full opacity-70">${warnSparkline}</div>
</div>
<div class="brand-card card-glow rounded-lg p-4">
<div class="text-xs mb-1" style="color: var(--text-muted);">Error (<span x-text="statsRange + 'd'">7d</span>)</div>
<div class="text-2xl font-bold text-red-400" x-text="statsTotals.error">${totals.error}</div>
${errorRate > 0 ? `<div class="text-xs mt-1" style="color: var(--text-muted);">${errorRate}% of all logs</div>` : ''}
<div class="mt-2 h-5 w-full opacity-70">${errorSparkline}</div>
</div>
import { dailyStatsChartConfig, formatHealthStatus, determineHealthStatus } from '../components/charts'
import { htmlDocument, header } from '../components/layout'
import { splitDailyStatsChartConfigs, formatHealthStatus, determineHealthStatus, sparkline } from '../components/charts'
import { escapeHtml, styles } from '../styles'
return c.json({ ok: false, error: 'Unauthorized' }, 401)
}

const days = Math.min(Math.max(parseInt(c.req.query('days') || '7', 10), 1), 90)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants