1{% extends "base.html" %}
2{% import "macros.html" as macros %}
3
4{% block title %}{{ task.title }} — 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">{{ project_name }}</a></li>
12 <li aria-hidden="true">/</li>
13 <li aria-current="page"><strong>{{ task.short_id }}</strong></li>
14 <li>
15 <button class="outline small js-copy-id" hidden aria-label="Copy task ID {{ task.short_id }}" data-copy="{{ task.short_id }}"><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"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg></button>
16 </li>
17 </ol>
18</nav>
19
20<article class="card mt-4">
21 <header>
22 <h1 class="task-title">{{ task.title }}</h1>
23 <span class="badge{% if task.status == "open" %} success{% elif task.status == "in_progress" %} warning{% endif %}">{{ task.status }}</span>
24 </header>
25
26 {% if !task.description.is_empty() %}
27 <div>{{ task.description|safe }}</div>
28 {% endif %}
29
30 <hr>
31 <footer>
32 <p class="text-light">{{ task.task_type }} · {{ task.priority }} priority · {{ task.effort }} effort<br>Created <time datetime="{{ task.created_at }}">{{ task.created_at_display }}</time> · Updated <time datetime="{{ task.updated_at }}">{{ task.updated_at_display }}</time></p>
33 </footer>
34
35 <div class="hstack gap-2 mt-4">
36 <button commandfor="dlg-edit-task" command="show-modal" class="outline small">Edit</button>
37 <ot-dropdown>
38 <button popovertarget="status-menu" class="outline small">
39 Change status
40 <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>
41 </button>
42 <menu popover id="status-menu">
43 <button role="menuitemradio" aria-checked="{{ task.status == "open" }}" {% if task.status == "open" %}disabled{% else %}form="set-open"{% endif %}>Open</button>
44 <button role="menuitemradio" aria-checked="{{ task.status == "in_progress" }}" {% if task.status == "in_progress" %}disabled{% else %}form="set-in-progress"{% endif %}>In progress</button>
45 <button role="menuitemradio" aria-checked="{{ task.status == "closed" }}" {% if task.status == "closed" %}disabled{% else %}form="set-closed"{% endif %}>Closed</button>
46 </menu>
47 </ot-dropdown>
48 {% if task.status != "open" %}
49 <form id="set-open" method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}" hidden>
50 <input type="hidden" name="status" value="open">
51 </form>
52 {% endif %}
53 {% if task.status != "in_progress" %}
54 <form id="set-in-progress" method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}" hidden>
55 <input type="hidden" name="status" value="in_progress">
56 </form>
57 {% endif %}
58 {% if task.status != "closed" %}
59 <form id="set-closed" method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}" hidden>
60 <input type="hidden" name="status" value="closed">
61 </form>
62 {% endif %}
63 <ot-dropdown>
64 <button popovertarget="confirm-delete" class="outline small danger">Delete</button>
65 <article class="card" popover id="confirm-delete">
66 <header>
67 <h4>Are you sure?</h4>
68 <p>This will delete <strong>{{ task.short_id }}</strong>.</p>
69 </header>
70 <br>
71 <footer class="hstack gap-2">
72 <button class="outline small" popovertarget="confirm-delete">Cancel</button>
73 <button data-variant="danger" class="small" form="delete-task" type="submit">Delete</button>
74 </footer>
75 </article>
76 </ot-dropdown>
77 <form id="delete-task" method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/delete" hidden></form>
78 </div>
79
80 {% if !task.labels.is_empty() %}
81 <section class="mt-4" aria-label="Labels">
82 <h2>Labels</h2>
83 <div class="hstack gap-2">
84 {% for l in task.labels %}
85 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/labels" class="inline">
86 <input type="hidden" name="action" value="rm">
87 <input type="hidden" name="label" value="{{ l }}">
88 <button type="submit" class="badge outline small" aria-label="Remove label {{ l }}" title="Remove">{{ l }} ×</button>
89 </form>
90 {% endfor %}
91 </div>
92 </section>
93 {% endif %}
94
95 <section class="mt-4" aria-label="Add label">
96 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/labels">
97 <input type="hidden" name="action" value="add">
98 <fieldset class="group">
99 <input type="text" name="label" placeholder="Label…" required aria-label="New label">
100 <button type="submit">Add label</button>
101 </fieldset>
102 </form>
103 </section>
104
105 <details class="mt-4">
106 <summary>Work log ({{ task.logs.len() }})</summary>
107 {% for log in task.logs %}
108 <article class="log-entry mt-4">
109 <p class="text-light"><small><time datetime="{{ log.timestamp }}">{{ log.timestamp_display }}</time></small></p>
110 <div>{{ log.message|safe }}</div>
111 </article>
112 {% if !loop.last %}
113 <hr>
114 {% endif %}
115 {% endfor %}
116 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/log" class="mt-4">
117 <label for="log-message">Add log entry</label>
118 <textarea id="log-message" name="message" rows="3" required placeholder="What happened…"></textarea>
119 <button type="submit" class="outline small mt-2">Add log</button>
120 </form>
121 </details>
122</article>
123
124<article class="card mt-4">
125 <h2>Blockers</h2>
126 {% if !blockers_open.is_empty() || !blockers_resolved.is_empty() %}
127 <ul>
128 {% for b in blockers_open %}
129 <li class="hstack items-center gap-2">
130 <a href="/projects/{{ project_name }}/tasks/{{ b.full_id }}"><code>{{ b.short_id }}</code></a> <span class="badge warning">open</span>
131 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/deps" class="inline">
132 <input type="hidden" name="action" value="rm">
133 <input type="hidden" name="blocker" value="{{ b.short_id }}">
134 <button type="submit" class="outline small" aria-label="Remove blocker {{ b.short_id }}">×</button>
135 </form>
136 </li>
137 {% endfor %}
138 {% for b in blockers_resolved %}
139 <li class="hstack items-center gap-2">
140 <a href="/projects/{{ project_name }}/tasks/{{ b.full_id }}"><code>{{ b.short_id }}</code></a> <span class="badge success">resolved</span>
141 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/deps" class="inline">
142 <input type="hidden" name="action" value="rm">
143 <input type="hidden" name="blocker" value="{{ b.short_id }}">
144 <button type="submit" class="outline small" aria-label="Remove blocker {{ b.short_id }}">×</button>
145 </form>
146 </li>
147 {% endfor %}
148 </ul>
149 {% endif %}
150 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/deps" class="mt-2">
151 <input type="hidden" name="action" value="add">
152 <fieldset class="group">
153 <input type="text" name="blocker" placeholder="Task ID…" required aria-label="Blocker task ID">
154 <button type="submit">Add blocker</button>
155 </fieldset>
156 </form>
157</article>
158
159{% if !subtasks.is_empty() %}
160<article class="card mt-4">
161 <h2>Subtasks</h2>
162 <div class="table">
163 <table>
164 <caption class="sr-only">Subtasks</caption>
165 <thead>
166 <tr>
167 <th scope="col">ID</th>
168 <th scope="col">Status</th>
169 <th scope="col">Type</th>
170 <th scope="col">Priority</th>
171 <th scope="col">Effort</th>
172 <th scope="col">Title</th>
173 <th scope="col">Labels</th>
174 <th scope="col">Created</th>
175 </tr>
176 </thead>
177 <tbody>
178 {% for t in subtasks %}
179 <tr>
180 <td><a href="/projects/{{ project_name }}/tasks/{{ t.full_id }}"><code>{{ t.short_id }}</code></a></td>
181 <td><span class="badge{% if t.status == "open" %} success{% elif t.status == "in_progress" %} warning{% endif %}">{{ t.status }}</span></td>
182 <td>{{ t.task_type }}</td>
183 <td>{{ t.priority }}</td>
184 <td>{{ t.effort }}</td>
185 <td>{{ t.title }}</td>
186 <td>{% for l in t.labels %}{% if !loop.first %}, {% endif %}{{ l }}{% endfor %}</td>
187 <td><time datetime="{{ t.created_at }}">{{ t.created_at_display }}</time></td>
188 </tr>
189 {% endfor %}
190 </tbody>
191 </table>
192 </div>
193</article>
194{% endif %}
195
196{% call macros::task_form_dialog("dlg-edit-task", edit_heading, edit_form_action, "Save", "edit", all_projects, active_project, "edit", task.title, task.description_raw, task.task_type, task.priority, task.effort, task.parent_id, "") %}{% endcall %}
197{% endblock %}