Files
step3/scripts/ui/dashboard_sparkline.gd
hz 4251885a73 Redesign dashboard top-4 stats as separate cards
Frontend half of the dashboard stat-card redesign, matching the
provided mockup. The 4 metrics used to be plain rows sharing one
bordered box (_add_metric -> a single metric_grid inside one _card).
Replaced with 4 independently-_card-wrapped panels in a responsive
GridContainer (4 cols / 2 compact, same threshold _update_responsive
already used).

Each card now has: a colored icon badge, title + info glyph, a big
value, a colored trend/status line, a small visual, and a footer
caption:
- Question Bank Rating / CCS Rating: a new minimal DashboardSparkline
  (dashboard_sparkline.gd) fed from the current rating and the
  week-ago snapshot (reconstructed client-side from the delta the API
  already returns - no separate history endpoint needed). Trend line
  and caption come from the new delta/percentile fields; both degrade
  to a plain single-point sparkline and a rated-count caption when no
  snapshot exists yet.
- Exam Readiness: a new DashboardSegmentBar (dashboard_segment_bar.gd -
  nothing like a segmented pill progress indicator existed anywhere in
  the codebase) instead of a sparkline, colored by the High/Medium/Low
  label; shows a neutral "complete a simulation" state when the user
  has none yet rather than a fabricated readiness.
- Average Score: reuses the existing ProgressBar/_style_progress,
  relabeled from "First-Attempt Accuracy".

Dropped the old "Study Time" card (not part of the mockup - the
underlying data still drives the weekly activity chart below, it just
no longer gets a dedicated top-line number) and the now-fully-unused
_format_duration.

Verified headlessly: instantiated the real screen against fake
DashboardService data, confirmed every card's value/trend/caption text
and the segment-bar/progress-bar values match the mockup exactly, and
rendered actual screenshots of both the populated and "no data yet"
fallback states.
2026-07-26 00:43:39 -05:00

30 lines
932 B
GDScript

extends Control
var values: PackedFloat32Array = []
var accent := Color.WHITE
var value_range := Vector2(0.0, 100.0)
func set_values(next_values: Array, next_accent: Color, next_range := Vector2(0.0, 100.0)) -> void:
values = PackedFloat32Array(next_values)
accent = next_accent
value_range = next_range
queue_redraw()
func _draw() -> void:
if values.size() < 2:
if values.size() == 1:
draw_circle(Vector2(size.x - 4.0, size.y * 0.5), 3.0, accent)
return
var span: float = maxf(0.0001, value_range.y - value_range.x)
var points := PackedVector2Array()
var count: int = values.size()
for index in range(count):
var x: float = lerpf(0.0, size.x, float(index) / float(count - 1))
var ratio: float = clampf((values[index] - value_range.x) / span, 0.0, 1.0)
var y: float = lerpf(size.y, 0.0, ratio)
points.append(Vector2(x, y))
draw_polyline(points, accent, 2.0, true)
draw_circle(points[-1], 3.0, accent)