1{% macro task_section(project_name, section, all_labels, redirect_url) %}
2 <form method="get" action="/projects/{{ project_name }}" class="hstack gap-2 mt-2 mb-4">
3 <div data-field>
4 <label for="{{ section.sort_ctx.prefix }}filter-priority">Priority</label>
5 <select id="{{ section.sort_ctx.prefix }}filter-priority" name="{{ section.sort_ctx.prefix }}priority" aria-label="Filter by priority">
6 <option value="">All</option>
7 <option value="high"{% if section.filter_priority.as_deref() == Some("high") %} selected{% endif %}>High</option>
8 <option value="medium"{% if section.filter_priority.as_deref() == Some("medium") %} selected{% endif %}>Medium</option>
9 <option value="low"{% if section.filter_priority.as_deref() == Some("low") %} selected{% endif %}>Low</option>
10 </select>
11 </div>
12 <div data-field>
13 <label for="{{ section.sort_ctx.prefix }}filter-effort">Effort</label>
14 <select id="{{ section.sort_ctx.prefix }}filter-effort" name="{{ section.sort_ctx.prefix }}effort" aria-label="Filter by effort">
15 <option value="">All</option>
16 <option value="low"{% if section.filter_effort.as_deref() == Some("low") %} selected{% endif %}>Low</option>
17 <option value="medium"{% if section.filter_effort.as_deref() == Some("medium") %} selected{% endif %}>Medium</option>
18 <option value="high"{% if section.filter_effort.as_deref() == Some("high") %} selected{% endif %}>High</option>
19 </select>
20 </div>
21 <div data-field>
22 <label for="{{ section.sort_ctx.prefix }}filter-label">Label</label>
23 <select id="{{ section.sort_ctx.prefix }}filter-label" name="{{ section.sort_ctx.prefix }}label" aria-label="Filter by label">
24 <option value="">All</option>
25 {% for l in all_labels %}
26 <option value="{{ l }}"{% if section.filter_label.as_deref() == Some(l.as_str()) %} selected{% endif %}>{{ l }}</option>
27 {% endfor %}
28 </select>
29 </div>
30 <div data-field>
31 <label for="{{ section.sort_ctx.prefix }}filter-type">Type</label>
32 <select id="{{ section.sort_ctx.prefix }}filter-type" name="{{ section.sort_ctx.prefix }}type" aria-label="Filter by type">
33 <option value="">All</option>
34 <option value="task"{% if section.filter_type.as_deref() == Some("task") %} selected{% endif %}>Task</option>
35 <option value="bug"{% if section.filter_type.as_deref() == Some("bug") %} selected{% endif %}>Bug</option>
36 <option value="feature"{% if section.filter_type.as_deref() == Some("feature") %} selected{% endif %}>Feature</option>
37 </select>
38 </div>
39 <div data-field>
40 <label for="{{ section.sort_ctx.prefix }}filter-search">Search</label>
41 <input type="text" id="{{ section.sort_ctx.prefix }}filter-search" name="{{ section.sort_ctx.prefix }}q" value="{{ section.filter_search }}" placeholder="Search titlesā¦" aria-label="Search tasks by title">
42 </div>
43 {# Preserve other sections' state as hidden fields when this form submits #}
44 {% let preserve = section.sort_ctx.preserve_qs %}
45 {% if !preserve.is_empty() %}
46 {% for pair in preserve.split('&') %}
47 {% let kv = pair.splitn(2, '=').collect::<Vec<_>>() %}
48 {% if kv.len() == 2 %}
49 <input type="hidden" name="{{ kv[0] }}" value="{{ kv[1] }}">
50 {% endif %}
51 {% endfor %}
52 {% endif %}
53 <input type="hidden" name="{{ section.sort_ctx.prefix }}sort" value="{{ section.sort_ctx.field }}">
54 <input type="hidden" name="{{ section.sort_ctx.prefix }}order" value="{{ section.sort_ctx.order }}">
55 <button type="submit" class="outline">Filter</button>
56 </form>
57
58 {% if section.tasks.is_empty() %}
59 <p class="text-light">No tasks match the current filters.</p>
60 {% else %}
61 <div class="table">
62 <table>
63 <caption class="sr-only">{{ section.label }} tasks for project</caption>
64 <thead>
65 <tr>
66 <th scope="col"><a href="{{ section.sort_ctx.column_href("id") }}">ID{{ section.sort_ctx.arrow("id") }}</a></th>
67 <th scope="col"><a href="{{ section.sort_ctx.column_href("type") }}">Type{{ section.sort_ctx.arrow("type") }}</a></th>
68 <th scope="col"><a href="{{ section.sort_ctx.column_href("priority") }}">Priority{{ section.sort_ctx.arrow("priority") }}</a></th>
69 <th scope="col"><a href="{{ section.sort_ctx.column_href("effort") }}">Effort{{ section.sort_ctx.arrow("effort") }}</a></th>
70 <th scope="col"><a href="{{ section.sort_ctx.column_href("title") }}">Title{{ section.sort_ctx.arrow("title") }}</a></th>
71 <th scope="col">Labels</th>
72 <th scope="col"><a href="{{ section.sort_ctx.column_href("created") }}">Created{{ section.sort_ctx.arrow("created") }}</a></th>
73 <th scope="col">Change status</th>
74 </tr>
75 </thead>
76 <tbody>
77 {% for t in section.tasks %}
78 <tr>
79 <td><a href="/projects/{{ project_name }}/tasks/{{ t.full_id }}"><code>{{ t.short_id }}</code></a></td>
80 <td>{{ t.task_type }}</td>
81 <td>{{ t.priority }}</td>
82 <td>{{ t.effort }}</td>
83 <td>{{ t.title }}</td>
84 <td>{% for l in t.labels %}{% if !loop.first %}, {% endif %}{{ l }}{% endfor %}</td>
85 <td><time datetime="{{ t.created_at }}">{{ t.created_at_display }}</time></td>
86 <td>
87 <ot-dropdown>
88 <button popovertarget="{{ section.sort_ctx.prefix }}status-{{ t.short_id }}" class="outline small" aria-label="Change status of {{ t.short_id }}, currently {{ t.status_display }}">
89 {{ t.status_display }}
90 <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>
91 </button>
92 <menu popover id="{{ section.sort_ctx.prefix }}status-{{ t.short_id }}">
93 <button role="menuitemradio" aria-checked="{{ t.status == "open" }}" {% if t.status == "open" %}disabled{% else %}form="{{ section.sort_ctx.prefix }}set-open-{{ t.short_id }}"{% endif %}>Open</button>
94 <button role="menuitemradio" aria-checked="{{ t.status == "in_progress" }}" {% if t.status == "in_progress" %}disabled{% else %}form="{{ section.sort_ctx.prefix }}set-in-progress-{{ t.short_id }}"{% endif %}>In progress</button>
95 <button role="menuitemradio" aria-checked="{{ t.status == "closed" }}" {% if t.status == "closed" %}disabled{% else %}form="{{ section.sort_ctx.prefix }}set-closed-{{ t.short_id }}"{% endif %}>Closed</button>
96 </menu>
97 </ot-dropdown>
98 {% if t.status != "open" %}
99 <form id="{{ section.sort_ctx.prefix }}set-open-{{ t.short_id }}" method="post" action="/projects/{{ project_name }}/tasks/{{ t.full_id }}" hidden>
100 <input type="hidden" name="status" value="open">
101 <input type="hidden" name="redirect" value="{{ redirect_url }}">
102 </form>
103 {% endif %}
104 {% if t.status != "in_progress" %}
105 <form id="{{ section.sort_ctx.prefix }}set-in-progress-{{ t.short_id }}" method="post" action="/projects/{{ project_name }}/tasks/{{ t.full_id }}" hidden>
106 <input type="hidden" name="status" value="in_progress">
107 <input type="hidden" name="redirect" value="{{ redirect_url }}">
108 </form>
109 {% endif %}
110 {% if t.status != "closed" %}
111 <form id="{{ section.sort_ctx.prefix }}set-closed-{{ t.short_id }}" method="post" action="/projects/{{ project_name }}/tasks/{{ t.full_id }}" hidden>
112 <input type="hidden" name="status" value="closed">
113 <input type="hidden" name="redirect" value="{{ redirect_url }}">
114 </form>
115 {% endif %}
116 </td>
117 </tr>
118 {% endfor %}
119 </tbody>
120 </table>
121 </div>
122
123 {% if section.total_pages > 1 %}
124 <nav aria-label="{{ section.label }} task list pagination" class="mt-4">
125 <menu class="buttons">
126 {% if section.page > 1 %}
127 {% let prev = section.page - 1 %}
128 <li><a href="{{ section.pagination_href(prev) }}" class="button outline small">ā Previous</a></li>
129 {% endif %}
130 {% for p in section.pagination_pages %}
131 <li>
132 {% if *p == section.page %}
133 <a href="{{ section.pagination_href(p) }}" class="button small" aria-current="page">{{ p }}</a>
134 {% else %}
135 <a href="{{ section.pagination_href(p) }}" class="button outline small">{{ p }}</a>
136 {% endif %}
137 </li>
138 {% endfor %}
139 {% if section.page < section.total_pages %}
140 {% let next = section.page + 1 %}
141 <li><a href="{{ section.pagination_href(next) }}" class="button outline small">Next ā</a></li>
142 {% endif %}
143 </menu>
144 </nav>
145 {% endif %}
146 {% endif %}
147{% endmacro %}
148
149{% macro task_form_dialog(dialog_id, heading, form_action, submit_label, mode, all_projects, active_project, prefix, title, description, task_type, priority, effort, parent, labels) %}
150<dialog id="{{ dialog_id }}" closedby="any">
151 <form method="post" action="{{ form_action }}" {% if mode == "create" %}id="form-new-task"{% endif %}>
152 <header>
153 <h3>{{ heading }}</h3>
154 </header>
155 <div class="vstack">
156 {% if mode == "create" %}
157 <div data-field>
158 <label for="{{ prefix }}-project">Project</label>
159 <select id="{{ prefix }}-project" aria-label="Project" required>
160 <option value=""{% if active_project.is_none() %} selected{% endif %}>Select projectā¦</option>
161 {% for p in all_projects %}
162 <option value="{{ p }}"{% if active_project.as_deref() == Some(p.as_str()) %} selected{% endif %}>{{ p }}</option>
163 {% endfor %}
164 </select>
165 </div>
166 {% endif %}
167 <label data-field>
168 Title
169 <input type="text" id="{{ prefix }}-title" name="title" value="{{ title }}" required>
170 </label>
171 <label data-field>
172 Description
173 <textarea id="{{ prefix }}-desc" name="description" rows="{% if mode == "edit" %}6{% else %}3{% endif %}">{{ description }}</textarea>
174 </label>
175 <div class="hstack gap-2">
176 <div data-field>
177 <label for="{{ prefix }}-type">Type</label>
178 <select id="{{ prefix }}-type" name="task_type">
179 <option value="task"{% if task_type == "task" %} selected{% endif %}>Task</option>
180 <option value="bug"{% if task_type == "bug" %} selected{% endif %}>Bug</option>
181 <option value="feature"{% if task_type == "feature" %} selected{% endif %}>Feature</option>
182 </select>
183 </div>
184 <div data-field>
185 <label for="{{ prefix }}-priority">Priority</label>
186 <select id="{{ prefix }}-priority" name="priority">
187 <option value="high"{% if priority == "high" %} selected{% endif %}>High</option>
188 <option value="medium"{% if priority == "medium" %} selected{% endif %}>Medium</option>
189 <option value="low"{% if priority == "low" %} selected{% endif %}>Low</option>
190 </select>
191 </div>
192 <div data-field>
193 <label for="{{ prefix }}-effort">Effort</label>
194 <select id="{{ prefix }}-effort" name="effort">
195 <option value="low"{% if effort == "low" %} selected{% endif %}>Low</option>
196 <option value="medium"{% if effort == "medium" %} selected{% endif %}>Medium</option>
197 <option value="high"{% if effort == "high" %} selected{% endif %}>High</option>
198 </select>
199 </div>
200 </div>
201 {% if mode == "create" %}
202 <label data-field>
203 Labels <small class="text-light">(comma-separated)</small>
204 <input type="text" id="{{ prefix }}-labels" name="labels" value="{{ labels }}" placeholder="frontend, urgent">
205 </label>
206 {% endif %}
207 <label data-field>
208 Parent task ID <small class="text-light">{% if mode == "edit" %}(leave empty to clear){% else %}(optional){% endif %}</small>
209 <input type="text" id="{{ prefix }}-parent" name="parent" value="{{ parent }}" placeholder="td-XXXXXXX" aria-label="Parent task ID">
210 </label>
211 {% if mode == "edit" %}
212 <input type="hidden" name="_parent_present" value="1">
213 {% endif %}
214 </div>
215 <footer>
216 <button type="button" commandfor="{{ dialog_id }}" command="close" class="outline">Cancel</button>
217 <button type="submit">{{ submit_label }}</button>
218 </footer>
219 </form>
220</dialog>
221{% endmacro %}