{{define "content" -}}

~{{.Repo.OwnerName}}/{{.Repo.Name}} · parade

{{template "beadsHead" (dict "Repo" .Repo "Ref" .Ref "Head" .Head)}} {{template "viewtabs" (dict "Repo" .Repo "Views" .Views "Current" "beads" "Ref" .Ref)}} {{if or (eq .Data.Mode "detail") (eq .Data.Mode "epic")}} {{/* ------------- detail / epic pane ------------- */}}

← Back to the parade

{{with .Data.Issue}} {{/* The board's banner below can say "the first N of M" because it knows how many cards it placed. A detail pane has no such N — it read one issue out of tables that were clipped at 2000 rows each — so it says the part it can support: the read was partial, and, when the issues table itself was cut, how many issues the tracker really holds. Same banner, same words for the same fact; it is not a second style. */}} {{if $.Data.Truncated}}
This read was clipped, so what is below may be short.{{if $.Data.IssuesClipped}} The tracker holds {{$.Data.ShownOf}} issues and only the first of them were read.{{end}}
{{end}}

{{.ID}} {{.Title}} {{if eq $.Data.Mode "epic"}}epic{{end}} {{.Lane}}

status: {{.Status}} {{if .Priority}}P{{.Priority}}{{end}} {{if .IssueType}}{{.IssueType}}{{end}} {{if .Assignee}}@{{.Assignee}}{{end}} {{range .Labels}}{{.}}{{end}}
{{/* The command this page cannot run for you. Which ones are offered follows the issue's status through the lane the detail pane already derived from it (Rolling = in progress, Past Stand = closed, Lined Up = open), so there is no second status vocabulary here — and nothing is offered that bd would refuse. The command names the issue and nothing about paths: this service does not know where the tracker is checked out, and the database name is in the breadcrumb if the reader needs to pick a directory. */}}
{{if eq .Lane "Rolling"}} bd close {{.ID}} bd update {{.ID}} --status=open {{else if eq .Lane "Past Stand"}} bd reopen {{.ID}} {{else}} bd update {{.ID}} --claim bd close {{.ID}} {{end}}
{{if .CreatedBy}}{{end}} {{if .Owner}}{{end}} {{if .EstimatedMinutes}}{{end}} {{if .ExternalRef}}{{end}} {{if .SpecID}}{{end}} {{if .CreatedAt}}{{end}} {{if .StartedAt}}{{end}} {{if .UpdatedAt}}{{end}} {{if .ClosedAt}}{{end}}
Created by{{.CreatedBy}}
Owner{{.Owner}}
Estimate{{.EstimatedMinutes}} min
External ref{{.ExternalRef}}
Spec{{.SpecID}}
Created{{.CreatedAt}}
Started{{.StartedAt}}
Updated{{.UpdatedAt}}
Closed{{.ClosedAt}}
{{/* The long-text bodies go through $.Links, which escapes the stored text and then wraps the issue ids it recognises in links to the database that owns them. An id whose prefix belongs to no database this viewer may browse is left as the text it is. */}} {{if .Description}}
Description
{{$.Links.Text .Description}}
{{end}} {{if .Design}}
Design
{{$.Links.Text .Design}}
{{end}} {{if .AcceptanceCriteria}}
Acceptance criteria
{{$.Links.Text .AcceptanceCriteria}}
{{end}} {{if .Notes}}
Notes
{{$.Links.Text .Notes}}
{{end}}
{{if eq $.Data.Mode "epic"}}

Subtasks{{if $.Data.SubtaskTotal}} {{$.Data.SubtaskDone}} of {{$.Data.SubtaskTotal}} done{{end}}

{{if $.Data.SubtaskTotal}}
{{else}}

No subtasks linked to this epic.

{{end}}
{{end}}

Depends on

{{if $.Data.DependsOn}}
    {{range $.Data.DependsOn}}
  • {{.IssueID}} {{if .Title}}— {{.Title}}{{end}} {{.Type}} {{if .Closed}}closed{{else}}{{.Status}}{{end}}
  • {{end}}
{{else}}

No outgoing dependencies.

{{end}}

Depended on by

{{if $.Data.DependedOnBy}}
    {{range $.Data.DependedOnBy}}
  • {{.IssueID}} {{if .Title}}— {{.Title}}{{end}} {{.Type}}
  • {{end}}
{{else}}

Nothing depends on this issue.

{{end}}
{{if or $.Data.DependsTree $.Data.DependentTree}}
{{if $.Data.DependsTree}}

Prerequisite chain — everything this waits on, transitively

{{end}} {{if $.Data.DependentTree}}

Unblocks — everything waiting on this, transitively

{{end}}
{{end}}
{{if $.Data.Comments}} {{range $.Data.Comments}}
{{.Author}} {{if .CreatedAt}}· {{.CreatedAt}}{{end}}
{{$.Links.Text .Text}}
{{end}} {{else}}

No comments.

{{end}} {{/* The Comments tab has no closed event, so the close reason is shown here at the end; the History tab already carries it as the closed event. */}} {{if .CloseReason}}

Close reason

{{.CloseReason}}
{{end}}
{{if $.Data.History}}
    {{range $.Data.History}}
  • {{.Actor}} {{$.Links.Text .Summary}} {{if .CreatedAt}}· {{.CreatedAt}}{{end}}
    {{if .Text}}
    {{$.Links.Text .Text}}
    {{end}}
  • {{end}}
{{else}}

No activity yet.

{{end}}
{{/* ------------- the stored rows behind all of the above ------------- Everything on this pane is a reading of these rows — a field set that names some columns and drops the rest, an event humanized into a sentence, an edge reduced to an id and a type — and a reading that went wrong looks exactly like one that did not. So the rows themselves are here, as they were read, to be compared against the rendering above them. Collapsed, because this is a tool for checking the page and not the reason anyone opened it — the same
the stream layout gives Past Stand. The values go through {{.Value}} and nothing else: they are stored text, so html/template escapes them, and they are deliberately NOT run through $.Links.Text the way the description and the comment bodies above are. A linkifier rewrites what it is given — an id inside a value would come out as an anchor — and a value that has been rewritten is no longer the value that is stored, which is the only thing this section is for. */}} {{if $.Data.Raw}}
Stored rows — what this pane was built from, as read {{range $.Data.Raw}}
{{.Table}} {{if .Clipped}}first {{len .Rows}} of {{.Matched}} rows{{else}}{{.Matched}} row{{if ne .Matched 1}}s{{end}}{{end}}
{{range .Rows}} {{range .Cells}} {{end}}
{{.Column}} {{if .Null}}NULL{{else}}{{.Value}}{{end}}
{{end}}
{{end}}
{{end}} {{else}} {{/* Two ways an issue can be missing, and the page may only claim the one it can see. A complete read that does not carry the id is an absence; a read clipped at 2000 rows is not, because the id may sit in the tail this page never looked at, and "not found" there would be the page stating something it does not know. */}} {{if $.Data.MissingBeyondCap}}
Not among the issues read. The tracker holds {{$.Data.ShownOf}} issues and only the first of them were read, so this id may sit in the tail this page never saw rather than not exist.
{{else}}
Issue not found.
{{end}} {{end}} {{else}} {{/* ---------------- board ---------------- */}} {{/* The board reports the tables it draws from, and no others — and it reports them one by one, because they fail differently. The count line is the issues read and is printed only when the issues read was short: "the first 4 of 4" is not a warning, it is a wrong sentence. Every other clipped table names itself, both its row counts and what the board lost by the rows it never read — a clipped labels table costs every card its pills and narrows the label filter while leaving all four counts true, which is why one flag could not have carried this. The issues entry is skipped in the list below because the count line above is that entry, in the wording the board has always used. */}} {{if .Data.Clipped}}
{{if .Data.IssuesClipped}}Showing the first {{.Data.Total}} of {{.Data.ShownOf}} issues.{{end}} {{range .Data.Clipped}}{{if ne .Table "issues"}}
The {{.Table}} table was clipped at {{.Shown}} of {{.Total}} rows: {{.Effect}}.
{{end}}{{end}}
{{end}}
{{if .Ref}}{{end}} {{/* The layout is not a filter, but this form is a GET: without carrying it, submitting a filter from the stream would answer with the board. */}} {{if eq .Data.Layout "stream"}}{{end}} {{/* Clear drops the filters and keeps the layout: the layout is how the page is read, not what it is filtered to. */}} {{if .Data.Filter.Active}}Clear{{end}}
view: {{if eq .Data.Layout "stream"}} Board {{else}}Board{{end}} · {{if eq .Data.Layout "stream"}}Stream {{else}}Stream{{end}}
{{.Data.Counts.Rolling}}
Rolling
{{.Data.Counts.LinedUp}}
Lined Up
{{.Data.Counts.Stalled}}
Stalled
{{.Data.Counts.PastStand}}
Past Stand
{{.Data.Counts.Total}}
Total
{{if eq .Data.Layout "stream"}} {{/* ---------------- stream: the same buckets as one column ---------------- */}}
{{range .Data.Sections}} {{if .Collapsed}}
{{.Name}}{{if .Note}}{{.Note}}{{end}}{{len .Issues}} {{template "beadStreamRows" (dict "Repo" $.Repo "Section" .)}}
{{else}}
{{.Name}}{{if .Note}}{{.Note}}{{end}}{{len .Issues}}
{{template "beadStreamRows" (dict "Repo" $.Repo "Section" .)}}
{{end}} {{end}}
{{else}} {{/* ---------------- board: four lanes side by side ---------------- */}} {{end}}{{/* layout */}} {{end}}{{/* detail vs board */}}
{{- end}} {{/* One stream section's cards. The row markup is the board's row markup: the same card, only differently arranged — so the two copies move together. It is copied rather than shared because folding the board's lane body into this partial would reflow the board's output, and the board is meant to render exactly what it rendered before this layout existed. */}} {{define "beadStreamRows"}}
{{range .Section.Issues}} {{if .Ready}}{{end}}{{.ID}} {{.Title}} {{if .PriorityLabel}}{{.PriorityLabel}}{{end}} {{if .Type}}{{.Type}}{{end}} {{if .Assignee}}@{{.Assignee}}{{end}} {{range .Labels}}{{.}}{{end}} {{if .BlockedBy}}🚧 {{.BlockedBy}}{{end}} {{if .Blocks}}blocks {{.Blocks}}{{end}} {{else}}

Nothing in this section.

{{end}}
{{end}}