Phase 5h slice 3 — splits the Tiles rollup into ProjectsCurrent (primary grid) and ProjectsQuiet (collapsible fold) per m's §7 'pinned ∪ recently-active ∪ open-work' rule. URL contract extended: /dashboard — Tiles, scope=current (defaults elided) /dashboard?scope=all — every active project in the grid /dashboard?scope=current — same as default (chip allows explicit) Scope chip lives next to the tab strip on Tiles only; Tasks + Events tabs hide it (no scope concept there). Default chip label: '◇ current', flips to '○ all' when scope=all. Chip href toggles to the alternate state preserving filter + view. Quiet fold: - <details> element opened on click — projects with IsCurrent=false land here, including all stale candidates. - Fold summary: 'Quiet (N) — older than 14d · M stale' (M omitted when 0). - Quiet tiles render with the same shape as primary tiles, slightly faded; stale tiles also carry a 'tile-stale' class (dashed border) and a 'stale' flag in the header. Stale card on the Tasks tab retires entirely — m's pick. The LastActivity stamp on each tile carries the staleness signal; the 'consider archiving?' nudge migrates to the Quiet fold framing. Stale data still computes (collectStale runs in buildDashboard) because the rollup needs the per-item stale flag and the repo-activity map for LastActivity. Cache key extends: (filter | view=X | scope=Y) so toggling scope from the chip lands in a separate cache slot (no stale render). Tests: - TestDashboardStaleCardSurfacesDormantMaiProject retargeted at the new Quiet fold + tile-stale class on Tiles. - TestDashboardStaleCardSkipsRecentRepo asserts the inverse via class inspection on the tile <article>. - 4 new tests cover the scope chip: renders on Tiles only, label flips on scope=all, scope=all hides the Quiet fold, chip URL flips correctly. Empty state: scope=current with no current projects shows a 'Nothing current. Pin a project, or show all active.' note with a direct link to scope=all.
70 lines
2.7 KiB
Cheetah
70 lines
2.7 KiB
Cheetah
{{define "dashboard-tiles"}}
|
|
{{if .P.ProjectsCurrent}}
|
|
<section class="dash-tiles">
|
|
{{range .P.ProjectsCurrent}}
|
|
{{template "tile" .}}
|
|
{{end}}
|
|
</section>
|
|
{{else}}
|
|
<p class="empty muted dash-tiles-empty">
|
|
{{if eq .Scope "current"}}
|
|
Nothing current. Pin a project from its detail page, or
|
|
<a href="?scope=all"
|
|
hx-get="?scope=all"
|
|
hx-target="#dashboard-section"
|
|
hx-swap="outerHTML"
|
|
hx-push-url="true">show all active</a>.
|
|
{{else}}
|
|
No projects to show.
|
|
{{end}}
|
|
</p>
|
|
{{end}}
|
|
|
|
{{if .P.ProjectsQuiet}}
|
|
<details class="dash-quiet">
|
|
<summary class="dash-quiet-summary muted">
|
|
Quiet ({{len .P.ProjectsQuiet}}) — older than {{.P.QuietWindowLabel}}{{if .P.QuietStaleCount}} · {{.P.QuietStaleCount}} stale{{end}}
|
|
</summary>
|
|
<section class="dash-tiles dash-tiles-quiet">
|
|
{{range .P.ProjectsQuiet}}
|
|
{{template "tile" .}}
|
|
{{end}}
|
|
</section>
|
|
</details>
|
|
{{end}}
|
|
{{end}}
|
|
|
|
{{define "tile"}}
|
|
{{$path := .Item.PrimaryPath}}
|
|
<article class="tile{{if .Item.Pinned}} tile-pinned{{end}}{{if .Stale}} tile-stale{{end}}" data-item-path="{{$path}}">
|
|
<header class="tile-head">
|
|
<a class="tile-title" href="/i/{{$path}}">
|
|
{{if .Item.Pinned}}<span class="tile-star" title="pinned">★</span>{{end}}
|
|
{{.Item.Title}}
|
|
</a>
|
|
<span class="tile-path muted">{{$path}}</span>
|
|
{{if .IsLive}}<a class="tile-live" href="{{.Item.PublicLiveURL}}" target="_blank" rel="noopener" title="live">live</a>{{end}}
|
|
{{if .Stale}}<span class="tile-stale-flag muted" title="mai-managed · quiet repo · no open work">stale</span>{{end}}
|
|
</header>
|
|
<p class="tile-counts">
|
|
{{if .OpenTasks}}<span class="tile-open"><strong>{{.OpenTasks}}</strong> open</span>{{end}}
|
|
{{if .Overdue}}<span class="tile-overdue"><strong>{{.Overdue}}</strong>!</span>{{end}}
|
|
{{if .OpenIssues}}<span class="tile-issues"><strong>{{.OpenIssues}}</strong> issue{{if ne .OpenIssues 1}}s{{end}}</span>{{end}}
|
|
{{if and (not .OpenTasks) (not .OpenIssues)}}<span class="tile-quiet muted">quiet</span>{{end}}
|
|
</p>
|
|
{{if .NextSignal}}
|
|
<p class="tile-signal" title="{{.NextSignalKind}}">
|
|
<span class="tile-signal-kind muted">{{if eq .NextSignalKind "task"}}•{{else}}◆{{end}}</span>
|
|
<span class="tile-signal-text">{{.NextSignal}}</span>
|
|
</p>
|
|
{{end}}
|
|
<footer class="tile-foot">
|
|
{{if .LastActivityRel}}
|
|
<span class="tile-stamp muted" title="{{.LastActivity.Format "2006-01-02 15:04"}}">{{.LastActivityRel}}</span>
|
|
{{else}}
|
|
<span class="tile-stamp muted">—</span>
|
|
{{end}}
|
|
</footer>
|
|
</article>
|
|
{{end}}
|