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><a href="/projects/{{ project_name }}/tasks/{{ task.full_id }}" class="unstyled" aria-current="page"><strong>{{ task.short_id }}</strong></a></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 == "closed" %} success{% elif task.status == "in_progress" %} secondary{% 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 <ot-dropdown>
37 <button popovertarget="status-menu" class="outline small">
38 Change status
39 <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>
40 </button>
41 <menu popover id="status-menu">
42 <button role="menuitemradio" aria-checked="{{ task.status == "open" }}" {% if task.status == "open" %}disabled{% else %}form="set-open"{% endif %}>Open</button>
43 <button role="menuitemradio" aria-checked="{{ task.status == "in_progress" }}" {% if task.status == "in_progress" %}disabled{% else %}form="set-in-progress"{% endif %}>In progress</button>
44 <button role="menuitemradio" aria-checked="{{ task.status == "closed" }}" {% if task.status == "closed" %}disabled{% else %}form="set-closed"{% endif %}>Closed</button>
45 </menu>
46 </ot-dropdown>
47 {% if task.status != "open" %}
48 <form id="set-open" method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}" hidden>
49 <input type="hidden" name="status" value="open">
50 </form>
51 {% endif %}
52 {% if task.status != "in_progress" %}
53 <form id="set-in-progress" method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}" hidden>
54 <input type="hidden" name="status" value="in_progress">
55 </form>
56 {% endif %}
57 {% if task.status != "closed" %}
58 <form id="set-closed" method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}" hidden>
59 <input type="hidden" name="status" value="closed">
60 </form>
61 {% endif %}
62 <ot-dropdown>
63 <button popovertarget="confirm-delete" class="outline small danger">Delete</button>
64 <article class="card" popover id="confirm-delete">
65 <header>
66 <h4>Are you sure?</h4>
67 <p>This will delete <strong>{{ task.short_id }}</strong>.</p>
68 </header>
69 <br>
70 <footer class="hstack gap-2">
71 <button class="outline small" popovertarget="confirm-delete">Cancel</button>
72 <button data-variant="danger" class="small" form="delete-task" type="submit">Delete</button>
73 </footer>
74 </article>
75 </ot-dropdown>
76 <form id="delete-task" method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/delete" hidden></form>
77 </div>
78
79 {% if !task.labels.is_empty() %}
80 <section class="mt-4" aria-label="Labels">
81 <h2>Labels</h2>
82 <div class="hstack gap-2">
83 {% for l in task.labels %}
84 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/labels" class="inline">
85 <input type="hidden" name="action" value="rm">
86 <input type="hidden" name="label" value="{{ l }}">
87 <button type="submit" class="badge outline small" aria-label="Remove label {{ l }}" title="Remove">{{ l }} ×</button>
88 </form>
89 {% endfor %}
90 </div>
91 </section>
92 {% endif %}
93
94 <section class="mt-4" aria-label="Add label">
95 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/labels">
96 <input type="hidden" name="action" value="add">
97 <fieldset class="group">
98 <input type="text" name="label" placeholder="Label…" required aria-label="New label">
99 <button type="submit">Add label</button>
100 </fieldset>
101 </form>
102 </section>
103
104 <details class="mt-4">
105 <summary>Work log ({{ task.logs.len() }})</summary>
106 {% for log in task.logs %}
107 <div><time datetime="{{ log.timestamp }}">{{ log.timestamp_display }}</time> — {{ log.message|safe }}</div>
108 {% endfor %}
109 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/log" class="mt-4">
110 <label for="log-message">Add log entry</label>
111 <textarea id="log-message" name="message" rows="3" required placeholder="What happened…"></textarea>
112 <button type="submit" class="outline small mt-2">Add log</button>
113 </form>
114 </details>
115</article>
116
117<article class="card mt-4">
118 <h2>Blockers</h2>
119 {% if !blockers_open.is_empty() || !blockers_resolved.is_empty() %}
120 <ul>
121 {% for b in blockers_open %}
122 <li class="hstack items-center gap-2">
123 <a href="/projects/{{ project_name }}/tasks/{{ b.full_id }}"><code>{{ b.short_id }}</code></a> <span class="badge warning">open</span>
124 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/deps" class="inline">
125 <input type="hidden" name="action" value="rm">
126 <input type="hidden" name="blocker" value="{{ b.short_id }}">
127 <button type="submit" class="outline small" aria-label="Remove blocker {{ b.short_id }}">×</button>
128 </form>
129 </li>
130 {% endfor %}
131 {% for b in blockers_resolved %}
132 <li class="hstack items-center gap-2">
133 <a href="/projects/{{ project_name }}/tasks/{{ b.full_id }}"><code>{{ b.short_id }}</code></a> <span class="badge success">resolved</span>
134 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/deps" class="inline">
135 <input type="hidden" name="action" value="rm">
136 <input type="hidden" name="blocker" value="{{ b.short_id }}">
137 <button type="submit" class="outline small" aria-label="Remove blocker {{ b.short_id }}">×</button>
138 </form>
139 </li>
140 {% endfor %}
141 </ul>
142 {% endif %}
143 <form method="post" action="/projects/{{ project_name }}/tasks/{{ task.full_id }}/deps" class="mt-2">
144 <input type="hidden" name="action" value="add">
145 <fieldset class="group">
146 <input type="text" name="blocker" placeholder="Task ID…" required aria-label="Blocker task ID">
147 <button type="submit">Add blocker</button>
148 </fieldset>
149 </form>
150</article>
151
152{% if !subtasks.is_empty() %}
153<article class="card mt-4">
154 <h2>Subtasks</h2>
155 {% call macros::task_table(project_name, subtasks, "Subtasks") %}{% endcall %}
156</article>
157{% endif %}
158{% endblock %}