FossilRepo

Fix forum posts, ticket filters, wiki markup rendering - Forum: fix query for root posts (NULL not 0 for firt/fprev) - Tickets: add status filter pills (All/Open/Fixed/Closed), search bar - Tickets: show type, priority columns, UUID links - Wiki: add Fossil wiki markup renderer (handles HTML content, [link|text] syntax, <verbatim> blocks, falls back to markdown) - Wiki: auto-detect HTML vs markdown content per page

lmata 2026-04-06 13:52 trunk
Commit 0fb1c2724e2356873548362f22dae1f6385c9b7c3e4e8335f935447f7b1a40a5
--- fossil/reader.py
+++ fossil/reader.py
@@ -579,11 +579,11 @@
579579
"""
580580
SELECT b.uuid, fp.fmtime, e.user, e.comment, b.rid
581581
FROM forumpost fp
582582
JOIN blob b ON fp.fpid = b.rid
583583
JOIN event e ON fp.fpid = e.objid
584
- WHERE fp.firt = 0 AND fp.fprev = 0
584
+ WHERE fp.firt IS NULL AND fp.fprev IS NULL
585585
ORDER BY fp.fmtime DESC
586586
LIMIT ?
587587
""",
588588
(limit,),
589589
).fetchall()
590590
--- fossil/reader.py
+++ fossil/reader.py
@@ -579,11 +579,11 @@
579 """
580 SELECT b.uuid, fp.fmtime, e.user, e.comment, b.rid
581 FROM forumpost fp
582 JOIN blob b ON fp.fpid = b.rid
583 JOIN event e ON fp.fpid = e.objid
584 WHERE fp.firt = 0 AND fp.fprev = 0
585 ORDER BY fp.fmtime DESC
586 LIMIT ?
587 """,
588 (limit,),
589 ).fetchall()
590
--- fossil/reader.py
+++ fossil/reader.py
@@ -579,11 +579,11 @@
579 """
580 SELECT b.uuid, fp.fmtime, e.user, e.comment, b.rid
581 FROM forumpost fp
582 JOIN blob b ON fp.fpid = b.rid
583 JOIN event e ON fp.fpid = e.objid
584 WHERE fp.firt IS NULL AND fp.fprev IS NULL
585 ORDER BY fp.fmtime DESC
586 LIMIT ?
587 """,
588 (limit,),
589 ).fetchall()
590
+31 -2
--- fossil/views.py
+++ fossil/views.py
@@ -1,5 +1,7 @@
1
+import re
2
+
13
import markdown as md
24
from django.contrib.auth.decorators import login_required
35
from django.http import Http404
46
from django.shortcuts import get_object_or_404, render
57
from django.utils.safestring import mark_safe
@@ -8,10 +10,37 @@
810
from projects.models import Project
911
1012
from .models import FossilRepository
1113
from .reader import FossilReader
1214
15
+
16
+def _render_fossil_content(content: str) -> str:
17
+ """Render content that may be Fossil wiki markup, HTML, or Markdown.
18
+
19
+ Fossil wiki pages can contain:
20
+ - Raw HTML (most Fossil wiki pages)
21
+ - Fossil-specific markup: [link|text], <verbatim>...</verbatim>
22
+ - Markdown (newer pages)
23
+ """
24
+ if not content:
25
+ return ""
26
+
27
+ # Convert Fossil-specific syntax
28
+ # [/path|text] -> <a href="/path">text</a>
29
+ content = re.sub(r"\[(/[^|\]]+)\|([^\]]+)\]", r'<a href="\1">\2</a>', content)
30
+ # [url|text] -> <a href="url">text</a>
31
+ content = re.sub(r"\[(https?://[^|\]]+)\|([^\]]+)\]", r'<a href="\1">\2</a>', content)
32
+ # <verbatim>...</verbatim> -> <pre><code>...</code></pre>
33
+ content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL)
34
+
35
+ # If content looks like it has HTML tags, treat as HTML (Fossil wiki)
36
+ if re.search(r"<(h[1-6]|p|ol|ul|li|div|table|pre|a|br)\b", content, re.IGNORECASE):
37
+ return content
38
+
39
+ # Otherwise try markdown
40
+ return md.markdown(content, extensions=["fenced_code", "tables", "toc"])
41
+
1342
1443
def _get_repo_and_reader(slug):
1544
"""Return (project, fossil_repo, reader) or raise 404."""
1645
project = get_object_or_404(Project, slug=slug, deleted_at__isnull=True)
1746
fossil_repo = get_object_or_404(FossilRepository, project=project, deleted_at__isnull=True)
@@ -257,11 +286,11 @@
257286
pages = reader.get_wiki_pages()
258287
home_page = reader.get_wiki_page("Home")
259288
260289
home_content_html = ""
261290
if home_page:
262
- home_content_html = mark_safe(md.markdown(home_page.content, extensions=["fenced_code", "tables", "toc"]))
291
+ home_content_html = mark_safe(_render_fossil_content(home_page.content))
263292
264293
return render(
265294
request,
266295
"fossil/wiki_list.html",
267296
{
@@ -285,11 +314,11 @@
285314
all_pages = reader.get_wiki_pages()
286315
287316
if not page:
288317
raise Http404(f"Wiki page not found: {page_name}")
289318
290
- content_html = mark_safe(md.markdown(page.content, extensions=["fenced_code", "tables", "toc"]))
319
+ content_html = mark_safe(_render_fossil_content(page.content))
291320
292321
return render(
293322
request,
294323
"fossil/wiki_page.html",
295324
{
296325
--- fossil/views.py
+++ fossil/views.py
@@ -1,5 +1,7 @@
 
 
1 import markdown as md
2 from django.contrib.auth.decorators import login_required
3 from django.http import Http404
4 from django.shortcuts import get_object_or_404, render
5 from django.utils.safestring import mark_safe
@@ -8,10 +10,37 @@
8 from projects.models import Project
9
10 from .models import FossilRepository
11 from .reader import FossilReader
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
14 def _get_repo_and_reader(slug):
15 """Return (project, fossil_repo, reader) or raise 404."""
16 project = get_object_or_404(Project, slug=slug, deleted_at__isnull=True)
17 fossil_repo = get_object_or_404(FossilRepository, project=project, deleted_at__isnull=True)
@@ -257,11 +286,11 @@
257 pages = reader.get_wiki_pages()
258 home_page = reader.get_wiki_page("Home")
259
260 home_content_html = ""
261 if home_page:
262 home_content_html = mark_safe(md.markdown(home_page.content, extensions=["fenced_code", "tables", "toc"]))
263
264 return render(
265 request,
266 "fossil/wiki_list.html",
267 {
@@ -285,11 +314,11 @@
285 all_pages = reader.get_wiki_pages()
286
287 if not page:
288 raise Http404(f"Wiki page not found: {page_name}")
289
290 content_html = mark_safe(md.markdown(page.content, extensions=["fenced_code", "tables", "toc"]))
291
292 return render(
293 request,
294 "fossil/wiki_page.html",
295 {
296
--- fossil/views.py
+++ fossil/views.py
@@ -1,5 +1,7 @@
1 import re
2
3 import markdown as md
4 from django.contrib.auth.decorators import login_required
5 from django.http import Http404
6 from django.shortcuts import get_object_or_404, render
7 from django.utils.safestring import mark_safe
@@ -8,10 +10,37 @@
10 from projects.models import Project
11
12 from .models import FossilRepository
13 from .reader import FossilReader
14
15
16 def _render_fossil_content(content: str) -> str:
17 """Render content that may be Fossil wiki markup, HTML, or Markdown.
18
19 Fossil wiki pages can contain:
20 - Raw HTML (most Fossil wiki pages)
21 - Fossil-specific markup: [link|text], <verbatim>...</verbatim>
22 - Markdown (newer pages)
23 """
24 if not content:
25 return ""
26
27 # Convert Fossil-specific syntax
28 # [/path|text] -> <a href="/path">text</a>
29 content = re.sub(r"\[(/[^|\]]+)\|([^\]]+)\]", r'<a href="\1">\2</a>', content)
30 # [url|text] -> <a href="url">text</a>
31 content = re.sub(r"\[(https?://[^|\]]+)\|([^\]]+)\]", r'<a href="\1">\2</a>', content)
32 # <verbatim>...</verbatim> -> <pre><code>...</code></pre>
33 content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL)
34
35 # If content looks like it has HTML tags, treat as HTML (Fossil wiki)
36 if re.search(r"<(h[1-6]|p|ol|ul|li|div|table|pre|a|br)\b", content, re.IGNORECASE):
37 return content
38
39 # Otherwise try markdown
40 return md.markdown(content, extensions=["fenced_code", "tables", "toc"])
41
42
43 def _get_repo_and_reader(slug):
44 """Return (project, fossil_repo, reader) or raise 404."""
45 project = get_object_or_404(Project, slug=slug, deleted_at__isnull=True)
46 fossil_repo = get_object_or_404(FossilRepository, project=project, deleted_at__isnull=True)
@@ -257,11 +286,11 @@
286 pages = reader.get_wiki_pages()
287 home_page = reader.get_wiki_page("Home")
288
289 home_content_html = ""
290 if home_page:
291 home_content_html = mark_safe(_render_fossil_content(home_page.content))
292
293 return render(
294 request,
295 "fossil/wiki_list.html",
296 {
@@ -285,11 +314,11 @@
314 all_pages = reader.get_wiki_pages()
315
316 if not page:
317 raise Http404(f"Wiki page not found: {page_name}")
318
319 content_html = mark_safe(_render_fossil_content(page.content))
320
321 return render(
322 request,
323 "fossil/wiki_page.html",
324 {
325
--- templates/fossil/partials/ticket_table.html
+++ templates/fossil/partials/ticket_table.html
@@ -1,40 +1,46 @@
11
<div id="ticket-table">
22
<div class="overflow-hidden rounded-lg border border-gray-700 bg-gray-800 shadow-sm">
33
<table class="min-w-full divide-y divide-gray-700">
44
<thead class="bg-gray-900">
55
<tr>
6
- <th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-400">Title</th>
7
- <th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-400">Status</th>
8
- <th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-400">Owner</th>
9
- <th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-400">Created</th>
6
+ <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-400">Title</th>
7
+ <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-400 w-24">Status</th>
8
+ <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-400 w-28">Type</th>
9
+ <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-400 w-20">Priority</th>
10
+ <th class="px-4 py-3 text-right text-xs font-medium uppercase text-gray-400 w-36">Created</th>
1011
</tr>
1112
</thead>
1213
<tbody class="divide-y divide-gray-700 bg-gray-800">
1314
{% for ticket in tickets %}
1415
<tr class="hover:bg-gray-700/50">
15
- <td class="px-6 py-4 whitespace-nowrap">
16
+ <td class="px-4 py-3">
1617
<a href="{% url 'fossil:ticket_detail' slug=project.slug ticket_uuid=ticket.uuid %}"
1718
class="text-brand-light hover:text-brand font-medium text-sm">
1819
{{ ticket.title|default:"(untitled)" }}
1920
</a>
21
+ <div class="mt-0.5">
22
+ <a href="{% url 'fossil:ticket_detail' slug=project.slug ticket_uuid=ticket.uuid %}"
23
+ class="text-xs font-mono text-gray-600 hover:text-brand-light">{{ ticket.uuid|truncatechars:12 }}</a>
24
+ </div>
2025
</td>
21
- <td class="px-6 py-4 whitespace-nowrap">
26
+ <td class="px-4 py-3 whitespace-nowrap">
2227
{% if ticket.status == "Open" %}
2328
<span class="inline-flex rounded-full bg-green-900/50 px-2 text-xs font-semibold leading-5 text-green-300">{{ ticket.status }}</span>
2429
{% elif ticket.status == "Closed" or ticket.status == "Fixed" %}
2530
<span class="inline-flex rounded-full bg-gray-700 px-2 text-xs font-semibold leading-5 text-gray-300">{{ ticket.status }}</span>
2631
{% else %}
2732
<span class="inline-flex rounded-full bg-yellow-900/50 px-2 text-xs font-semibold leading-5 text-yellow-300">{{ ticket.status }}</span>
2833
{% endif %}
2934
</td>
30
- <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">{{ ticket.owner|default:"—" }}</td>
31
- <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">{{ ticket.created|date:"N j, Y" }}</td>
35
+ <td class="px-4 py-3 whitespace-nowrap text-xs text-gray-400">{{ ticket.type|default:"—" }}</td>
36
+ <td class="px-4 py-3 whitespace-nowrap text-xs text-gray-400">{{ ticket.priority|default:"—" }}</td>
37
+ <td class="px-4 py-3 whitespace-nowrap text-xs text-gray-500 text-right">{{ ticket.created|date:"Y-m-d" }}</td>
3238
</tr>
3339
{% empty %}
3440
<tr>
35
- <td colspan="4" class="px-6 py-8 text-center text-sm text-gray-400">No tickets.</td>
41
+ <td colspan="5" class="px-4 py-8 text-center text-sm text-gray-400">No tickets found.</td>
3642
</tr>
3743
{% endfor %}
3844
</tbody>
3945
</table>
4046
</div>
4147
--- templates/fossil/partials/ticket_table.html
+++ templates/fossil/partials/ticket_table.html
@@ -1,40 +1,46 @@
1 <div id="ticket-table">
2 <div class="overflow-hidden rounded-lg border border-gray-700 bg-gray-800 shadow-sm">
3 <table class="min-w-full divide-y divide-gray-700">
4 <thead class="bg-gray-900">
5 <tr>
6 <th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-400">Title</th>
7 <th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-400">Status</th>
8 <th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-400">Owner</th>
9 <th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-400">Created</th>
 
10 </tr>
11 </thead>
12 <tbody class="divide-y divide-gray-700 bg-gray-800">
13 {% for ticket in tickets %}
14 <tr class="hover:bg-gray-700/50">
15 <td class="px-6 py-4 whitespace-nowrap">
16 <a href="{% url 'fossil:ticket_detail' slug=project.slug ticket_uuid=ticket.uuid %}"
17 class="text-brand-light hover:text-brand font-medium text-sm">
18 {{ ticket.title|default:"(untitled)" }}
19 </a>
 
 
 
 
20 </td>
21 <td class="px-6 py-4 whitespace-nowrap">
22 {% if ticket.status == "Open" %}
23 <span class="inline-flex rounded-full bg-green-900/50 px-2 text-xs font-semibold leading-5 text-green-300">{{ ticket.status }}</span>
24 {% elif ticket.status == "Closed" or ticket.status == "Fixed" %}
25 <span class="inline-flex rounded-full bg-gray-700 px-2 text-xs font-semibold leading-5 text-gray-300">{{ ticket.status }}</span>
26 {% else %}
27 <span class="inline-flex rounded-full bg-yellow-900/50 px-2 text-xs font-semibold leading-5 text-yellow-300">{{ ticket.status }}</span>
28 {% endif %}
29 </td>
30 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">{{ ticket.owner|default:"—" }}</td>
31 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">{{ ticket.created|date:"N j, Y" }}</td>
 
32 </tr>
33 {% empty %}
34 <tr>
35 <td colspan="4" class="px-6 py-8 text-center text-sm text-gray-400">No tickets.</td>
36 </tr>
37 {% endfor %}
38 </tbody>
39 </table>
40 </div>
41
--- templates/fossil/partials/ticket_table.html
+++ templates/fossil/partials/ticket_table.html
@@ -1,40 +1,46 @@
1 <div id="ticket-table">
2 <div class="overflow-hidden rounded-lg border border-gray-700 bg-gray-800 shadow-sm">
3 <table class="min-w-full divide-y divide-gray-700">
4 <thead class="bg-gray-900">
5 <tr>
6 <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-400">Title</th>
7 <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-400 w-24">Status</th>
8 <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-400 w-28">Type</th>
9 <th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-400 w-20">Priority</th>
10 <th class="px-4 py-3 text-right text-xs font-medium uppercase text-gray-400 w-36">Created</th>
11 </tr>
12 </thead>
13 <tbody class="divide-y divide-gray-700 bg-gray-800">
14 {% for ticket in tickets %}
15 <tr class="hover:bg-gray-700/50">
16 <td class="px-4 py-3">
17 <a href="{% url 'fossil:ticket_detail' slug=project.slug ticket_uuid=ticket.uuid %}"
18 class="text-brand-light hover:text-brand font-medium text-sm">
19 {{ ticket.title|default:"(untitled)" }}
20 </a>
21 <div class="mt-0.5">
22 <a href="{% url 'fossil:ticket_detail' slug=project.slug ticket_uuid=ticket.uuid %}"
23 class="text-xs font-mono text-gray-600 hover:text-brand-light">{{ ticket.uuid|truncatechars:12 }}</a>
24 </div>
25 </td>
26 <td class="px-4 py-3 whitespace-nowrap">
27 {% if ticket.status == "Open" %}
28 <span class="inline-flex rounded-full bg-green-900/50 px-2 text-xs font-semibold leading-5 text-green-300">{{ ticket.status }}</span>
29 {% elif ticket.status == "Closed" or ticket.status == "Fixed" %}
30 <span class="inline-flex rounded-full bg-gray-700 px-2 text-xs font-semibold leading-5 text-gray-300">{{ ticket.status }}</span>
31 {% else %}
32 <span class="inline-flex rounded-full bg-yellow-900/50 px-2 text-xs font-semibold leading-5 text-yellow-300">{{ ticket.status }}</span>
33 {% endif %}
34 </td>
35 <td class="px-4 py-3 whitespace-nowrap text-xs text-gray-400">{{ ticket.type|default:"—" }}</td>
36 <td class="px-4 py-3 whitespace-nowrap text-xs text-gray-400">{{ ticket.priority|default:"—" }}</td>
37 <td class="px-4 py-3 whitespace-nowrap text-xs text-gray-500 text-right">{{ ticket.created|date:"Y-m-d" }}</td>
38 </tr>
39 {% empty %}
40 <tr>
41 <td colspan="5" class="px-4 py-8 text-center text-sm text-gray-400">No tickets found.</td>
42 </tr>
43 {% endfor %}
44 </tbody>
45 </table>
46 </div>
47
--- templates/fossil/ticket_list.html
+++ templates/fossil/ticket_list.html
@@ -3,20 +3,33 @@
33
44
{% block content %}
55
<h1 class="text-2xl font-bold text-gray-100 mb-2">{{ project.name }}</h1>
66
{% include "fossil/_project_nav.html" %}
77
8
-<div class="mb-4">
9
- <input type="search"
10
- name="search"
11
- value="{{ search }}"
12
- placeholder="Search tickets..."
13
- class="w-full max-w-md rounded-md border-gray-700 bg-gray-800 text-gray-100 shadow-sm focus:border-brand focus:ring-brand sm:text-sm"
14
- hx-get="{% url 'fossil:tickets' slug=project.slug %}"
15
- hx-trigger="input changed delay:300ms, search"
16
- hx-target="#ticket-table"
17
- hx-swap="outerHTML"
18
- hx-push-url="true" />
8
+<div class="flex items-center justify-between mb-4">
9
+ <div class="flex items-center gap-2 text-xs text-gray-500">
10
+ <span>Status:</span>
11
+ <a href="{% url 'fossil:tickets' slug=project.slug %}"
12
+ class="rounded-full px-2.5 py-1 {% if not status_filter %}bg-brand text-white{% else %}bg-gray-800 text-gray-400 hover:text-white border border-gray-700{% endif %}">All</a>
13
+ <a href="{% url 'fossil:tickets' slug=project.slug %}?status=Open"
14
+ class="rounded-full px-2.5 py-1 {% if status_filter == 'Open' %}bg-brand text-white{% else %}bg-gray-800 text-gray-400 hover:text-white border border-gray-700{% endif %}">Open</a>
15
+ <a href="{% url 'fossil:tickets' slug=project.slug %}?status=Fixed"
16
+ class="rounded-full px-2.5 py-1 {% if status_filter == 'Fixed' %}bg-brand text-white{% else %}bg-gray-800 text-gray-400 hover:text-white border border-gray-700{% endif %}">Fixed</a>
17
+ <a href="{% url 'fossil:tickets' slug=project.slug %}?status=Closed"
18
+ class="rounded-full px-2.5 py-1 {% if status_filter == 'Closed' %}bg-brand text-white{% else %}bg-gray-800 text-gray-400 hover:text-white border border-gray-700{% endif %}">Closed</a>
19
+ </div>
20
+ <div>
21
+ <input type="search"
22
+ name="search"
23
+ value="{{ search }}"
24
+ placeholder="Search tickets..."
25
+ class="rounded-md border-gray-700 bg-gray-800 text-gray-100 shadow-sm focus:border-brand focus:ring-brand text-sm px-3 py-1.5"
26
+ hx-get="{% url 'fossil:tickets' slug=project.slug %}{% if status_filter %}?status={{ status_filter }}{% endif %}"
27
+ hx-trigger="input changed delay:300ms, search"
28
+ hx-target="#ticket-table"
29
+ hx-swap="outerHTML"
30
+ hx-push-url="true" />
31
+ </div>
1932
</div>
2033
2134
{% include "fossil/partials/ticket_table.html" %}
2235
{% endblock %}
2336
--- templates/fossil/ticket_list.html
+++ templates/fossil/ticket_list.html
@@ -3,20 +3,33 @@
3
4 {% block content %}
5 <h1 class="text-2xl font-bold text-gray-100 mb-2">{{ project.name }}</h1>
6 {% include "fossil/_project_nav.html" %}
7
8 <div class="mb-4">
9 <input type="search"
10 name="search"
11 value="{{ search }}"
12 placeholder="Search tickets..."
13 class="w-full max-w-md rounded-md border-gray-700 bg-gray-800 text-gray-100 shadow-sm focus:border-brand focus:ring-brand sm:text-sm"
14 hx-get="{% url 'fossil:tickets' slug=project.slug %}"
15 hx-trigger="input changed delay:300ms, search"
16 hx-target="#ticket-table"
17 hx-swap="outerHTML"
18 hx-push-url="true" />
 
 
 
 
 
 
 
 
 
 
 
 
 
19 </div>
20
21 {% include "fossil/partials/ticket_table.html" %}
22 {% endblock %}
23
--- templates/fossil/ticket_list.html
+++ templates/fossil/ticket_list.html
@@ -3,20 +3,33 @@
3
4 {% block content %}
5 <h1 class="text-2xl font-bold text-gray-100 mb-2">{{ project.name }}</h1>
6 {% include "fossil/_project_nav.html" %}
7
8 <div class="flex items-center justify-between mb-4">
9 <div class="flex items-center gap-2 text-xs text-gray-500">
10 <span>Status:</span>
11 <a href="{% url 'fossil:tickets' slug=project.slug %}"
12 class="rounded-full px-2.5 py-1 {% if not status_filter %}bg-brand text-white{% else %}bg-gray-800 text-gray-400 hover:text-white border border-gray-700{% endif %}">All</a>
13 <a href="{% url 'fossil:tickets' slug=project.slug %}?status=Open"
14 class="rounded-full px-2.5 py-1 {% if status_filter == 'Open' %}bg-brand text-white{% else %}bg-gray-800 text-gray-400 hover:text-white border border-gray-700{% endif %}">Open</a>
15 <a href="{% url 'fossil:tickets' slug=project.slug %}?status=Fixed"
16 class="rounded-full px-2.5 py-1 {% if status_filter == 'Fixed' %}bg-brand text-white{% else %}bg-gray-800 text-gray-400 hover:text-white border border-gray-700{% endif %}">Fixed</a>
17 <a href="{% url 'fossil:tickets' slug=project.slug %}?status=Closed"
18 class="rounded-full px-2.5 py-1 {% if status_filter == 'Closed' %}bg-brand text-white{% else %}bg-gray-800 text-gray-400 hover:text-white border border-gray-700{% endif %}">Closed</a>
19 </div>
20 <div>
21 <input type="search"
22 name="search"
23 value="{{ search }}"
24 placeholder="Search tickets..."
25 class="rounded-md border-gray-700 bg-gray-800 text-gray-100 shadow-sm focus:border-brand focus:ring-brand text-sm px-3 py-1.5"
26 hx-get="{% url 'fossil:tickets' slug=project.slug %}{% if status_filter %}?status={{ status_filter }}{% endif %}"
27 hx-trigger="input changed delay:300ms, search"
28 hx-target="#ticket-table"
29 hx-swap="outerHTML"
30 hx-push-url="true" />
31 </div>
32 </div>
33
34 {% include "fossil/partials/ticket_table.html" %}
35 {% endblock %}
36

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button