Files
projax/web/templates/issues_section.tmpl
mAi 5a56ad91e5 feat(phase 3h gitea writeback): close/reopen/comment/create from projax
- gitea pkg: CloseIssue, ReopenIssue, CreateIssue, AddComment + ErrForbidden
  classification on 401/403. Client.do sets Content-Type on non-empty bodies.
- web handler: POST /i/{path}/issues/{close|reopen|comment|create}
  - authorisation guard: repo form value must match a gitea-repo item_link
    on the target item (rejects form-crafted writes to unrelated repos)
  - HTMX re-renders issues_section partial after each action
  - busts gitea per-repo cache (open + closed-recent) and dashboard 60s TTL
- templates: ✓ close button + reopen + collapsible comment box on every
  issue row; "+ new issue" disclosure per repo
- design.md §6 retitled "Phase 2.d read; 3h writeback" with auth/perm
  semantics + parked list
- 5 unit tests in gitea/, 5 integration tests in web/ covering happy paths
  + 403 → inline banner fallback
2026-05-15 19:22:11 +02:00

90 lines
4.2 KiB
Cheetah

{{define "issues-section"}}
<section class="issues" id="issues-section">
<h2>Issues{{if .IssuesOpenTotal}} ({{.IssuesOpenTotal}}){{end}}</h2>
{{if .Banner}}<p class="banner warn">{{.Banner}}</p>{{end}}
{{range .Issues}}
{{$repo := .Repo}}
<div class="repo-block" data-repo="{{.Repo}}">
<h3>
<a href="{{.RepoURL}}" target="_blank" rel="noopener noreferrer">{{.Repo}}</a>
<small class="muted"><a href="{{.IssuesURL}}" target="_blank" rel="noopener noreferrer">↗ Gitea repo</a></small>
</h3>
{{if .Error}}<p class="banner warn">{{.Error}}</p>{{end}}
<details class="new-issue">
<summary class="muted">+ new issue</summary>
<form class="new-issue-form"
hx-post="/i/{{$.Item.PrimaryPath}}/issues/create"
hx-target="#issues-section"
hx-swap="outerHTML">
<input type="hidden" name="repo" value="{{.Repo}}">
<input name="title" placeholder="title" required>
<textarea name="body" placeholder="body (markdown)" rows="3"></textarea>
<button type="submit">create</button>
</form>
</details>
{{if .Open}}
<ul class="issues open">
{{range .Open}}
<li class="issue-row" id="issue-{{$repo}}-{{.Number}}">
<a class="num" href="{{.HTMLURL}}" target="_blank" rel="noopener noreferrer">#{{.Number}}</a>
<a class="title" href="{{.HTMLURL}}" target="_blank" rel="noopener noreferrer">{{.Title}}</a>
{{range .Labels}}<span class="label">{{.}}</span>{{end}}
{{if .Milestone}}<span class="milestone">{{.Milestone}}</span>{{end}}
{{range .Assignees}}<span class="assignee">@{{.}}</span>{{end}}
{{if .UpdatedRel}}<small class="muted">updated {{.UpdatedRel}}</small>{{end}}
<form class="issue-close"
hx-post="/i/{{$.Item.PrimaryPath}}/issues/close"
hx-target="#issues-section"
hx-swap="outerHTML">
<input type="hidden" name="repo" value="{{$repo}}">
<input type="hidden" name="number" value="{{.Number}}">
<button type="submit" title="close issue">✓ close</button>
</form>
<details class="issue-comment">
<summary class="muted">comment</summary>
<form class="comment-form"
hx-post="/i/{{$.Item.PrimaryPath}}/issues/comment"
hx-target="#issues-section"
hx-swap="outerHTML">
<input type="hidden" name="repo" value="{{$repo}}">
<input type="hidden" name="number" value="{{.Number}}">
<textarea name="body" placeholder="comment (markdown)" rows="2" required></textarea>
<button type="submit">post</button>
</form>
</details>
</li>
{{end}}
</ul>
{{else if not .Error}}
<p class="muted">No open issues.</p>
{{end}}
{{if .ClosedRecent}}
<details>
<summary class="muted">{{len .ClosedRecent}} closed in last 30 days</summary>
<ul class="issues closed">
{{range .ClosedRecent}}
<li class="issue-row">
<a class="num" href="{{.HTMLURL}}" target="_blank" rel="noopener noreferrer">#{{.Number}}</a>
<a class="title" href="{{.HTMLURL}}" target="_blank" rel="noopener noreferrer">{{.Title}}</a>
{{range .Labels}}<span class="label">{{.}}</span>{{end}}
{{if .UpdatedRel}}<small class="muted">{{.UpdatedRel}}</small>{{end}}
<form class="issue-reopen"
hx-post="/i/{{$.Item.PrimaryPath}}/issues/reopen"
hx-target="#issues-section"
hx-swap="outerHTML">
<input type="hidden" name="repo" value="{{$repo}}">
<input type="hidden" name="number" value="{{.Number}}">
<button type="submit" title="reopen">↻ reopen</button>
</form>
</li>
{{end}}
</ul>
</details>
{{end}}
</div>
{{end}}
</section>
{{end}}