tracker/templates/partials.html
{{define "legend"}}
<ul class="legend" aria-label="ความหมายของสี">
<li><span class="sw sw-todo" aria-hidden="true"></span>Todo</li>
<li><span class="sw sw-in_progress" aria-hidden="true"></span>In Progress</li>
<li><span class="sw sw-done" aria-hidden="true"></span>Done</li>
<li><span class="sw sw-review" aria-hidden="true"></span>Wait for review</li>
<li><span class="sw sw-reviewed" aria-hidden="true"></span>Reviewed</li>
</ul>
{{end}}
{{/* One task segment. */}}
{{define "seg"}}<li class="seg seg-{{.Status}}" title="{{.ID}} {{.Title}} ({{statusLabel .Status}})"><span class="sr">{{.ID}} {{.Title}}: {{statusLabel .Status}}</span></li>{{end}}
{{/* Segments of one epic. */}}
{{define "epicsegs"}}{{if .Tasks}}<ol class="segs" style="flex:{{len .Tasks}}" aria-label="{{.Code}} {{.Title}}: Reviewed {{.Counts.Reviewed}}, Done {{.Counts.Done}}, กำลังทำ {{.Counts.InProgress}}, รอรีวิว {{.Counts.Review}}, ยังไม่เริ่ม {{.Counts.Todo}}">{{range .Tasks}}{{template "seg" .}}{{end}}</ol>{{end}}{{end}}
{{/* One milestone as a lane: lane number, one bar grouped by epic, and a row per epic. */}}
{{define "lane"}}{{$mid := .ID}}
<div class="lane">
<span class="lane-no" aria-label="Milestone {{.No}}">{{.No}}</span>
<div class="lane-body">
<div class="lane-head">
<h3 class="lane-title"><a href="{{milestoneHref .ID}}">{{.Title}}</a></h3>
<span class="lane-meta">{{.Counts.Complete}}/{{.Counts.Total}} task เสร็จ ใน {{len .Epics}} epic{{if .Target}} <span class="lane-target">เป้าหมาย {{thaiDate .Target}}</span>{{end}}</span>
</div>
{{if .Counts.Total}}
<div class="segs-grouped">{{range .Epics}}{{template "epicsegs" .}}{{end}}</div>
{{else}}
<p class="lane-empty">ยังไม่มีงานใน milestone นี้</p>
{{end}}
{{if .Epics}}
<ul class="epic-rows" aria-label="Epic ใน Milestone {{.No}}">
{{range .Epics}}
<li class="epic-row">
<span class="epic-code">{{.Code}}</span>
<a class="epic-name" href="{{epicHref $mid .ID}}">{{.Title}}</a>
<span class="epic-bar">{{if .Tasks}}{{template "epicsegs" .}}{{end}}</span>
<span class="epic-meta">{{.Counts.Complete}}/{{.Counts.Total}}</span>
</li>
{{end}}
</ul>
{{end}}
</div>
<span class="lane-pct">{{.Counts.Percent}}%</span>
</div>
{{end}}
{{/* Breadcrumb for a task: Milestone, epic, task id. */}}
{{define "where"}}<a href="{{milestoneHref .MilestoneID}}">Milestone {{.MilestoneNo}}</a> › <a href="{{epicHref .MilestoneID .EpicID}}">{{.EpicCode}} {{.EpicTitle}}</a>{{end}}
{{/* Breadcrumb trail for the milestone, epic and task pages. */}}
{{define "crumbs"}}{{if .}}<nav class="crumbs" aria-label="ตำแหน่งของหน้า"><ol>{{range .}}<li>{{if .Href}}<a href="{{.Href}}">{{.Label}}</a>{{else}}<span aria-current="page">{{.Label}}</span>{{end}}</li>{{end}}</ol></nav>{{end}}{{end}}
{{/* Status mix of a milestone or epic, as text. */}}
{{define "countline"}}<span class="countline"><span>Reviewed {{.Reviewed}}</span><span>Done {{.Done}}</span><span>In Progress {{.InProgress}}</span><span>Wait for review {{.Review}}</span><span>Todo {{.Todo}}</span></span>{{end}}
{{define "chip"}}<span class="chip chip-{{.}}">{{statusLabel .}}</span>{{end}}
{{/* Attached files: repo paths open in the file viewer in a new tab. */}}
{{define "refs"}}{{if .}}<ul class="refs">{{range .}}{{$h := refHref .}}<li>{{if $h}}<a class="ref-link" href="{{$h}}" target="_blank" rel="noopener"><code>{{.}}</code><span class="ref-ext" aria-hidden="true">↗</span><span class="sr"> (เปิดในแท็บใหม่)</span></a>{{else}}<code>{{.}}</code>{{end}}</li>{{end}}</ul>{{end}}{{end}}
{{/* Review details: state, reviewer, what to check, numbered checklist, decision. Used by the review page and the task page. */}}
{{define "reviewbody"}}{{with .Review}}{{$st := .StateOrTodo}}
<dl class="review-meta">
<div><dt>สถานะรีวิว</dt><dd><span class="rs rs-{{$st}}" aria-hidden="true"></span>{{reviewState $st}}</dd></div>
{{if .Reviewer}}<div><dt>ผู้รีวิว</dt><dd>{{.Reviewer}}</dd></div>{{end}}
{{if .RequestedAt}}<div><dt>ส่งรีวิวเมื่อ</dt><dd>{{thaiDate .RequestedAt}}</dd></div>{{end}}
{{if .DecidedAt}}<div><dt>ตัดสินเมื่อ</dt><dd>{{thaiDate .DecidedAt}}{{if .DecidedBy}} โดย {{.DecidedBy}}{{end}}</dd></div>{{end}}
{{if $.Owner}}<div><dt>ผู้ทำ</dt><dd>{{$.Owner}}</dd></div>{{end}}
</dl>
{{if .Decision}}<div class="decision decision-{{$st}}"><p class="decision-label">ผลการรีวิว</p><div class="md">{{md .Decision}}</div></div>{{end}}
{{if .Focus}}<div class="review-focus"><p class="decision-label">สิ่งที่ต้องตรวจ</p><div class="md">{{md .Focus}}</div></div>{{end}}
{{if .History}}
<details class="history">
<summary>ประวัติการรีวิว ({{len .History}})</summary>
<ol class="history-list">
{{range .History}}<li><p class="history-meta"><span class="rs rs-{{.State}}" aria-hidden="true"></span><b>{{reviewState .State}}</b>{{if .By}} โดย {{.By}}{{end}}{{if .At}} <time>{{thaiDate .At}}</time>{{end}}</p>{{if .Decision}}<div class="md">{{md .Decision}}</div>{{end}}</li>{{end}}
</ol>
</details>
{{end}}
{{end}}
{{end}}
{{/* Review form: checklist ticks, Approve / Reject / Hold, result text (Markdown), reviewer name, Save. */}}
{{define "reviewform"}}
<form class="review-form" method="post" action="/reviews/{{.TaskID}}" novalidate>
<input type="hidden" name="return" value="{{.Return}}">
{{if .Error}}<p class="form-error" role="alert">{{.Error}}</p>{{end}}
{{if .Items}}
<fieldset class="checklist">
<legend class="checklist-head"><b>Checklist สำหรับผู้รีวิว</b> <span class="muted">ติ๊กขั้นที่ทำแล้ว</span></legend>
<ol class="checklist-steps">
{{range .Items}}<li><input type="checkbox" class="cl-input" id="cl-{{$.TaskID}}-{{.Index}}" name="checklist" value="{{.Index}}"{{if .Checked}} checked{{end}}><label for="cl-{{$.TaskID}}-{{.Index}}" class="md">{{md .Text}}</label></li>{{end}}
</ol>
</fieldset>
{{end}}
<fieldset class="outcome">
<legend>ผลการรีวิว</legend>
<div class="outcome-options">
<label class="oc oc-approve"><input type="radio" name="outcome" value="approve"{{if eq .Outcome "approve"}} checked{{end}}><span>Approve</span></label>
<label class="oc oc-reject"><input type="radio" name="outcome" value="reject"{{if eq .Outcome "reject"}} checked{{end}}><span>Reject</span></label>
<label class="oc oc-hold"><input type="radio" name="outcome" value="hold"{{if eq .Outcome "hold"}} checked{{end}}><span>Hold</span></label>
</div>
</fieldset>
<label class="field" for="decision-{{.TaskID}}">
<span class="field-label">รายละเอียดผลการรีวิว <span class="muted">(Markdown ได้ บังคับกรอกเมื่อ Reject หรือ Hold)</span></span>
<textarea id="decision-{{.TaskID}}" name="decision" rows="5" placeholder="เช่น **ต้องแก้** ข้อ 2: รวม FIN_OFFICER กับ FIN_HEAD เป็นบทบาทเดียว">{{.Decision}}</textarea>
</label>
<div class="form-row">
<label class="field field-name" for="reviewer-{{.TaskID}}">
<span class="field-label">ชื่อผู้รีวิว</span>
<input id="reviewer-{{.TaskID}}" name="reviewer" type="text" maxlength="100" value="{{.Reviewer}}" autocomplete="name">
</label>
<button type="submit" class="btn-save">บันทึกผลการรีวิว</button>
</div>
</form>
{{end}}