1{% extends "base.html" %}
2{% import "macros.html" as macros %}
3
4{% block title %}{{ project_name }} — td{% endblock %}
5
6{% block content %}
7<nav aria-label="Breadcrumb">
8 <ol class="unstyled hstack">
9 <li><a href="/" class="unstyled">Projects</a></li>
10 <li aria-hidden="true">/</li>
11 <li><a href="/projects/{{ project_name }}" class="unstyled" aria-current="page"><strong>{{ project_name }}</strong></a></li>
12 </ol>
13</nav>
14
15<h1>{{ project_name }}</h1>
16
17<div class="row">
18 <article class="card col-4">
19 <p class="text-light">Open</p>
20 <p><strong>{{ stats_open }}</strong></p>
21 </article>
22 <article class="card col-4">
23 <p class="text-light">In progress</p>
24 <p><strong>{{ stats_in_progress }}</strong></p>
25 </article>
26 <article class="card col-4">
27 <p class="text-light">Closed</p>
28 <p><strong>{{ stats_closed }}</strong></p>
29 </article>
30</div>
31
32{% if !next_up.is_empty() %}
33<details open class="mt-4">
34 <summary>Next up</summary>
35 <div class="table">
36 <table>
37 <caption class="sr-only">Top scored tasks recommended to work on next</caption>
38 <thead>
39 <tr>
40 <th scope="col">#</th>
41 <th scope="col">ID</th>
42 <th scope="col">Score</th>
43 <th scope="col">Title</th>
44 <th scope="col">Change status</th>
45 </tr>
46 </thead>
47 <tbody>
48 {% for (i, s) in next_up.iter().enumerate() %}
49 <tr>
50 <td>{{ i + 1 }}</td>
51 <td><a href="/projects/{{ project_name }}/tasks/{{ s.id }}"><code>{{ s.short_id }}</code></a></td>
52 <td>{{ s.score }}</td>
53 <td>{{ s.title }}</td>
54 <td>
55 <ot-dropdown>
56 <button popovertarget="next-status-{{ s.short_id }}" class="outline small" aria-label="Change status of {{ s.short_id }}, currently {{ s.status_display }}">
57 {{ s.status_display }}
58 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
59 </button>
60 <menu popover id="next-status-{{ s.short_id }}">
61 <button role="menuitemradio" aria-checked="{{ s.status == "open" }}" {% if s.status == "open" %}disabled{% else %}form="next-set-open-{{ s.short_id }}"{% endif %}>Open</button>
62 <button role="menuitemradio" aria-checked="{{ s.status == "in_progress" }}" {% if s.status == "in_progress" %}disabled{% else %}form="next-set-in-progress-{{ s.short_id }}"{% endif %}>In progress</button>
63 <button role="menuitemradio" aria-checked="{{ s.status == "closed" }}" {% if s.status == "closed" %}disabled{% else %}form="next-set-closed-{{ s.short_id }}"{% endif %}>Closed</button>
64 </menu>
65 </ot-dropdown>
66 {% if s.status != "open" %}
67 <form id="next-set-open-{{ s.short_id }}" method="post" action="/projects/{{ project_name }}/tasks/{{ s.id }}" hidden>
68 <input type="hidden" name="status" value="open">
69 <input type="hidden" name="redirect" value="/projects/{{ project_name }}">
70 </form>
71 {% endif %}
72 {% if s.status != "in_progress" %}
73 <form id="next-set-in-progress-{{ s.short_id }}" method="post" action="/projects/{{ project_name }}/tasks/{{ s.id }}" hidden>
74 <input type="hidden" name="status" value="in_progress">
75 <input type="hidden" name="redirect" value="/projects/{{ project_name }}">
76 </form>
77 {% endif %}
78 {% if s.status != "closed" %}
79 <form id="next-set-closed-{{ s.short_id }}" method="post" action="/projects/{{ project_name }}/tasks/{{ s.id }}" hidden>
80 <input type="hidden" name="status" value="closed">
81 <input type="hidden" name="redirect" value="/projects/{{ project_name }}">
82 </form>
83 {% endif %}
84 </td>
85 </tr>
86 {% endfor %}
87 </tbody>
88 </table>
89 </div>
90</details>
91{% endif %}
92
93<details{% if next_up.is_empty() || filter_priority.is_some() || filter_effort.is_some() || filter_label.is_some() || !filter_search.is_empty() || filter_status.as_deref() != Some("open") || sort_ctx.field != "priority" || sort_ctx.order != "asc" %} open{% endif %} class="mt-4">
94 <summary>Tasks</summary>
95
96 <form method="get" action="/projects/{{ project_name }}" class="hstack gap-2 mt-2 mb-4">
97 <div data-field>
98 <label for="filter-status">Status</label>
99 <select id="filter-status" name="status" aria-label="Filter by status">
100 <option value="">All</option>
101 <option value="open"{% if filter_status.as_deref() == Some("open") %} selected{% endif %}>Open</option>
102 <option value="in_progress"{% if filter_status.as_deref() == Some("in_progress") %} selected{% endif %}>In progress</option>
103 <option value="closed"{% if filter_status.as_deref() == Some("closed") %} selected{% endif %}>Closed</option>
104 </select>
105 </div>
106 <div data-field>
107 <label for="filter-priority">Priority</label>
108 <select id="filter-priority" name="priority" aria-label="Filter by priority">
109 <option value="">All</option>
110 <option value="high"{% if filter_priority.as_deref() == Some("high") %} selected{% endif %}>High</option>
111 <option value="medium"{% if filter_priority.as_deref() == Some("medium") %} selected{% endif %}>Medium</option>
112 <option value="low"{% if filter_priority.as_deref() == Some("low") %} selected{% endif %}>Low</option>
113 </select>
114 </div>
115 <div data-field>
116 <label for="filter-effort">Effort</label>
117 <select id="filter-effort" name="effort" aria-label="Filter by effort">
118 <option value="">All</option>
119 <option value="low"{% if filter_effort.as_deref() == Some("low") %} selected{% endif %}>Low</option>
120 <option value="medium"{% if filter_effort.as_deref() == Some("medium") %} selected{% endif %}>Medium</option>
121 <option value="high"{% if filter_effort.as_deref() == Some("high") %} selected{% endif %}>High</option>
122 </select>
123 </div>
124 <div data-field>
125 <label for="filter-label">Label</label>
126 <select id="filter-label" name="label" aria-label="Filter by label">
127 <option value="">All</option>
128 {% for l in all_labels %}
129 <option value="{{ l }}"{% if filter_label.as_deref() == Some(l.as_str()) %} selected{% endif %}>{{ l }}</option>
130 {% endfor %}
131 </select>
132 </div>
133 <div data-field>
134 <label for="filter-search">Search</label>
135 <input type="text" id="filter-search" name="q" value="{{ filter_search }}" placeholder="Search titles…" aria-label="Search tasks by title">
136 </div>
137 <input type="hidden" name="sort" value="{{ sort_ctx.field }}">
138 <input type="hidden" name="order" value="{{ sort_ctx.order }}">
139 <button type="submit" class="outline">Filter</button>
140 </form>
141
142 {% if page_tasks.is_empty() %}
143 <p class="text-light">No tasks match the current filters.</p>
144 {% else %}
145 {% call macros::sortable_task_table(project_name, page_tasks, "Task list for project", sort_ctx) %}{% endcall %}
146
147 {% if total_pages > 1 %}
148 <nav aria-label="Task list pagination" class="mt-4">
149 <menu class="buttons">
150 {% if page > 1 %}
151 {% let prev = page - 1 %}
152 <li><a href="{{ self.pagination_href(prev) }}" class="button outline small">← Previous</a></li>
153 {% endif %}
154 {% for p in pagination_pages %}
155 <li>
156 {% if *p == page %}
157 <a href="{{ self.pagination_href(p) }}" class="button small" aria-current="page">{{ p }}</a>
158 {% else %}
159 <a href="{{ self.pagination_href(p) }}" class="button outline small">{{ p }}</a>
160 {% endif %}
161 </li>
162 {% endfor %}
163 {% if page < total_pages %}
164 {% let next = page + 1 %}
165 <li><a href="{{ self.pagination_href(next) }}" class="button outline small">Next →</a></li>
166 {% endif %}
167 </menu>
168 </nav>
169 {% endif %}
170 {% endif %}
171</details>
172{% endblock %}