feat(dashboard): polish Events tab — summary header, fuller day labels
Phase 5h slice 5 — the Events tab's routing landed in slice 2; this
slice adds the dedicated-surface polish that distinguishes it from
the Events card on the Tasks tab.
Changes:
- Top header summary: 'N events · next 7 days' so m sees the window
shape at a glance without scanning rows.
- Day headings now carry three columns: the relative label
('Today' / 'Tomorrow' / weekday), the ISO date (mono font), and a
right-aligned event count. Bigger visual hierarchy than the cards-tab
flavour to justify the dedicated tab's existence.
- Empty-state copy invites linking a CalDAV calendar from a project's
detail page so a never-seen-events tab doesn't feel broken.
- StartLabel fallback to '—' when an event has no parseable start
time so the row doesn't collapse weirdly.
CSS adds .dash-events-summary, .event-day-heading flex layout,
.event-day-label / .event-day-date / .event-day-count spans, and a
constrained .dash-events-empty for the empty-state width.
Test: TestDashboardEventsViewRenders now also asserts the empty-state
copy ships, so a future refactor that drops the invite-to-link prose
gets caught.
This commit is contained in:
@@ -93,6 +93,8 @@ func TestDashboardTasksViewFallback(t *testing.T) {
|
||||
|
||||
// TestDashboardEventsViewRenders confirms that ?view=events renders the
|
||||
// promoted Events surface (dash-events-view) and not the cards or tiles.
|
||||
// Also asserts the slice-5 polish: the empty state copy invites the user
|
||||
// to link a CalDAV calendar so the dedicated tab doesn't feel broken.
|
||||
func TestDashboardEventsViewRenders(t *testing.T) {
|
||||
srv, pool := mustServer(t)
|
||||
defer pool.Close()
|
||||
@@ -107,6 +109,9 @@ func TestDashboardEventsViewRenders(t *testing.T) {
|
||||
if strings.Contains(body, `class="card card-tasks"`) {
|
||||
t.Errorf("view=events should NOT render the Tasks 5-card layout")
|
||||
}
|
||||
if !strings.Contains(body, "Link a CalDAV calendar") {
|
||||
t.Errorf("empty-state copy should invite linking a calendar")
|
||||
}
|
||||
}
|
||||
|
||||
// TestDashboardUnknownViewFallsBackToTiles confirms graceful default
|
||||
|
||||
@@ -421,11 +421,20 @@ table.bulk .chip-add-btn:hover { background: var(--accent); color: var(--accent-
|
||||
.dashboard .dash-tiles-quiet .tile { opacity: 0.85; }
|
||||
|
||||
.dashboard .dash-events-view { margin-top: 8px; }
|
||||
.dashboard .dash-events-view .event-day-large { margin: 16px 0; }
|
||||
.dashboard .dash-events-view .event-day-large h2 {
|
||||
font-size: 1em; font-weight: 600; margin: 0 0 8px 0;
|
||||
.dashboard .dash-events-summary { margin: 4px 0 16px; }
|
||||
.dashboard .dash-events-summary h2 { margin: 0; font-size: 1.05em; font-weight: 600; }
|
||||
.dashboard .dash-events-view .event-day-large { margin: 18px 0; }
|
||||
.dashboard .dash-events-view .event-day-heading {
|
||||
font-size: 0.95em; font-weight: 600; margin: 0 0 8px 0;
|
||||
border-bottom: 1px dotted var(--border); padding-bottom: 4px;
|
||||
display: flex; gap: 10px; align-items: baseline;
|
||||
}
|
||||
.dashboard .dash-events-view .event-day-label { color: var(--fg); }
|
||||
.dashboard .dash-events-view .event-day-date {
|
||||
font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.85em;
|
||||
}
|
||||
.dashboard .dash-events-view .event-day-count { margin-left: auto; font-size: 0.85em; }
|
||||
.dashboard .dash-events-empty { margin: 24px 0; max-width: 540px; }
|
||||
.dashboard .dash-events-view .event-list { list-style: none; padding: 0; margin: 0; }
|
||||
.dashboard .dash-events-view .event-row {
|
||||
display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
|
||||
|
||||
@@ -236,13 +236,20 @@
|
||||
{{define "dashboard-events-view"}}
|
||||
<section class="dash-events-view">
|
||||
{{if .P.Events}}
|
||||
<header class="dash-events-summary">
|
||||
<h2>{{.P.EventsTotal}} event{{if ne .P.EventsTotal 1}}s{{end}} <span class="muted">· next 7 days</span></h2>
|
||||
</header>
|
||||
{{range .P.Events}}
|
||||
<section class="event-day-large">
|
||||
<h2 class="muted">{{.DayLabel}} <small>({{len .Events}})</small></h2>
|
||||
<h3 class="event-day-heading">
|
||||
<span class="event-day-label">{{.DayLabel}}</span>
|
||||
<span class="muted event-day-date">{{.DayKey}}</span>
|
||||
<span class="muted event-day-count">{{len .Events}} event{{if ne (len .Events) 1}}s{{end}}</span>
|
||||
</h3>
|
||||
<ul class="event-list">
|
||||
{{range .Events}}
|
||||
<li class="event-row">
|
||||
<span class="start">{{.StartLabel}}</span>
|
||||
<span class="start">{{if .StartLabel}}{{.StartLabel}}{{else}}—{{end}}</span>
|
||||
<a class="proj" href="/i/{{.Item.PrimaryPath}}">{{.Item.PrimaryPath}}</a>
|
||||
<span class="summary">{{.Event.Summary}}</span>
|
||||
{{if .Event.Location}}<span class="loc muted">· {{.Event.Location}}</span>{{end}}
|
||||
@@ -253,7 +260,7 @@
|
||||
</section>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<p class="empty muted">No events in the next 7 days.</p>
|
||||
<p class="empty muted dash-events-empty">No events in the next 7 days. Link a CalDAV calendar from a project's detail page to start surfacing events here.</p>
|
||||
{{end}}
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user