feat: expand kanban assignment and filtering
Test and publish / verify (push) Successful in 2m22s

This commit is contained in:
2026-07-29 13:53:31 +03:30
parent e1475d0df0
commit 5a3ca4eb40
7 changed files with 432 additions and 91 deletions
+84 -37
View File
@@ -42,8 +42,12 @@
<small>The new tag will be created and added to this card.</small>
</details>
</fieldset>
<fieldset class="assignee-picker">
<legend>Assignees <small>Optional · select multiple</small></legend>
<div>{{range .Users}}<label><input type="checkbox" name="assignee_ids" value="{{.ID}}"><span><i class="avatar">{{initial .DisplayName}}</i>{{.DisplayName}}</span></label>{{end}}</div>
</fieldset>
<div class="form-row">
<label>Assignee<select name="assignee_id"><option value="">Unassigned</option>{{range .Users}}<option value="{{.ID}}">{{.DisplayName}}</option>{{end}}</select></label>
<label>Importance<select name="importance"><option value="normal">Normal</option><option value="low">Low</option><option value="high">High</option><option value="urgent">Urgent</option></select></label>
<label>Column<select name="status"><option value="backlog">Backlog</option><option value="in_progress">In progress</option><option value="blocked">Blocked</option><option value="done">Done</option></select></label>
</div>
<label>Due date <small>Optional · Persian</small><span class="jalali-field"><input name="due_date" data-jalali-picker autocomplete="off" inputmode="numeric" placeholder="{{.TodayJalali}}" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}"><button type="button" class="jalali-trigger" aria-label="Choose due date" title="Open Persian calendar"></button></span></label>
@@ -54,6 +58,32 @@
</header>
{{template "notice" .}}
{{$filter := .BoardFilter}}
<section class="board-filter-bar">
<div class="board-filter-title"><span></span><strong>Filter cards</strong></div>
<form method="get" action="/board">
<label><span>Tag</span><select name="tag" aria-label="Filter by tag">
<option value="">All tags</option>
{{range .BoardTags}}<option value="{{.ID}}" {{if eq $filter.TagID .ID}}selected{{end}}>{{.Name}}</option>{{end}}
</select></label>
<label><span>Assignee</span><select name="assignee" aria-label="Filter by assignee">
<option value="">Everyone</option>
{{range .Users}}<option value="{{.ID}}" {{if eq $filter.AssigneeID .ID}}selected{{end}}>{{.DisplayName}}</option>{{end}}
</select></label>
<label><span>Priority</span><select name="priority" aria-label="Filter by priority">
<option value="">All priorities</option>
<option value="urgent" {{if eq $filter.Priority "urgent"}}selected{{end}}>Urgent</option>
<option value="high" {{if eq $filter.Priority "high"}}selected{{end}}>High</option>
<option value="normal" {{if eq $filter.Priority "normal"}}selected{{end}}>Normal</option>
<option value="low" {{if eq $filter.Priority "low"}}selected{{end}}>Low</option>
</select></label>
<div class="board-filter-actions">
{{if $filter.Active}}<a href="/board" class="text-button">Clear</a>{{end}}
<button class="button secondary" type="submit">Apply</button>
</div>
</form>
</section>
<div class="kanban-board" data-board data-csrf="{{.CSRF}}">
{{range .Board}}
<section class="kanban-column {{.Status}}" data-board-column="{{.Status}}">
@@ -66,53 +96,70 @@
{{$task := .}}
<article class="kanban-card" draggable="true" data-task-id="{{.ID}}">
{{if .Tags}}<div class="card-tags">{{range .Tags}}<span class="board-tag {{.Color}}">{{.Name}}</span>{{end}}</div>{{end}}
<div class="card-topline"><span class="task-id">#{{.ID}}</span>{{if .DueDate}}<span class="task-due">Due {{dateFA .DueDate}}</span>{{end}}</div>
<div class="card-topline"><span class="task-id">#{{.ID}}</span><span class="card-signals"><span class="importance-badge {{.Importance}}">{{if eq .Importance "urgent"}}Urgent{{else if eq .Importance "high"}}High{{else if eq .Importance "low"}}Low{{else}}Normal{{end}}</span>{{if .DueDate}}<span class="task-due">Due {{dateFA .DueDate}}</span>{{end}}</span></div>
<h3>{{.Title}}</h3>
{{if .Description}}<p>{{.Description}}</p>{{end}}
<div class="task-meta">
{{if .AssigneeID.Valid}}<span class="task-assignee"><i class="avatar">{{initial .AssigneeName}}</i><span>{{.AssigneeName}}</span></span>{{else}}<span class="task-assignee unassigned"><i>?</i><span>Unassigned</span></span>{{end}}
{{if .Assignees}}<span class="task-assignees">{{range .Assignees}}<span class="task-assignee"><i class="avatar">{{initial .DisplayName}}</i><span>{{.DisplayName}}</span></span>{{end}}</span>{{else}}<span class="task-assignee unassigned"><i>?</i><span>Unassigned</span></span>{{end}}
<span class="task-creator">by {{.CreatorName}}</span>
</div>
{{if $.BoardTags}}
<details class="card-tag-editor">
<summary>Labels</summary>
<form method="post" action="/board/tasks/{{.ID}}/tags">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<div>{{range $.BoardTags}}<label><input type="checkbox" name="tag_ids" value="{{.ID}}" {{if hasTag $task.Tags .ID}}checked{{end}}><span class="board-tag {{.Color}}">{{.Name}}</span></label>{{end}}</div>
<button class="button primary full" type="submit">Save labels</button>
</form>
</details>
{{end}}
<div class="task-actions">
<form method="post" action="/board/tasks/{{.ID}}/move">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<select name="status" aria-label="Move task">
<option value="backlog" {{if eq .Status "backlog"}}selected{{end}}>Backlog</option>
<option value="in_progress" {{if eq .Status "in_progress"}}selected{{end}}>In progress</option>
<option value="blocked" {{if eq .Status "blocked"}}selected{{end}}>Blocked</option>
<option value="done" {{if eq .Status "done"}}selected{{end}}>Done</option>
</select>
<button class="text-button task-move-button" type="submit">Move</button>
</form>
{{if or (eq .CreatorID $.User.ID) (eq $.User.Role "admin")}}
<div class="task-lifecycle-actions">
<form method="post" action="/board/tasks/{{.ID}}/archive">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<button class="text-button archive-task" type="submit">Archive</button>
</form>
<details class="task-remove">
<summary>Delete</summary>
<form method="post" action="/board/tasks/{{.ID}}/delete">
<details class="card-controls">
<summary><span>Card controls</span><i></i></summary>
<div class="card-controls-body">
<details class="card-detail-editor">
<summary>People &amp; importance</summary>
<form method="post" action="/board/tasks/{{.ID}}/details">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<button class="text-button" type="submit">Delete permanently</button>
<fieldset class="compact-assignee-picker">
<legend>Assignees</legend>
<div>{{range $.Users}}<label><input type="checkbox" name="assignee_ids" value="{{.ID}}" {{if hasUser $task.Assignees .ID}}checked{{end}}><span><i class="avatar">{{initial .DisplayName}}</i>{{.DisplayName}}</span></label>{{end}}</div>
</fieldset>
<label>Importance<select name="importance"><option value="low" {{if eq $task.Importance "low"}}selected{{end}}>Low</option><option value="normal" {{if eq $task.Importance "normal"}}selected{{end}}>Normal</option><option value="high" {{if eq $task.Importance "high"}}selected{{end}}>High</option><option value="urgent" {{if eq $task.Importance "urgent"}}selected{{end}}>Urgent</option></select></label>
<button class="button primary full" type="submit">Save details</button>
</form>
</details>
{{if $.BoardTags}}
<details class="card-tag-editor">
<summary>Labels</summary>
<form method="post" action="/board/tasks/{{.ID}}/tags">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<div>{{range $.BoardTags}}<label><input type="checkbox" name="tag_ids" value="{{.ID}}" {{if hasTag $task.Tags .ID}}checked{{end}}><span class="board-tag {{.Color}}">{{.Name}}</span></label>{{end}}</div>
<button class="button primary full" type="submit">Save labels</button>
</form>
</details>
{{end}}
<div class="task-actions">
<form method="post" action="/board/tasks/{{.ID}}/move">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<select name="status" aria-label="Move task">
<option value="backlog" {{if eq .Status "backlog"}}selected{{end}}>Backlog</option>
<option value="in_progress" {{if eq .Status "in_progress"}}selected{{end}}>In progress</option>
<option value="blocked" {{if eq .Status "blocked"}}selected{{end}}>Blocked</option>
<option value="done" {{if eq .Status "done"}}selected{{end}}>Done</option>
</select>
<button class="text-button task-move-button" type="submit">Move</button>
</form>
{{if or (eq .CreatorID $.User.ID) (eq $.User.Role "admin")}}
<div class="task-lifecycle-actions">
<form method="post" action="/board/tasks/{{.ID}}/archive">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<button class="text-button archive-task" type="submit">Archive</button>
</form>
<details class="task-remove">
<summary>Delete</summary>
<form method="post" action="/board/tasks/{{.ID}}/delete">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<button class="text-button" type="submit">Delete permanently</button>
</form>
</details>
</div>
{{end}}
</div>
</div>
{{end}}
</div>
</details>
</article>
{{end}}
{{if not .Tasks}}<div class="kanban-empty"><span>Drop tasks here</span></div>{{end}}
{{if not .Tasks}}<div class="kanban-empty"><span>{{if $filter.Active}}No matching cards{{else}}Drop tasks here{{end}}</span></div>{{end}}
</div>
</section>
{{end}}