131 lines
6.4 KiB
HTML
131 lines
6.4 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Tickets — {{ brand }}{% endblock %}
|
|
{% block content %}
|
|
<h1 class="text-3xl font-bold">Tickets</h1>
|
|
<p class="text-white/60 text-sm">{{ tagline }}</p>
|
|
|
|
{# ---------------- Filter Panel (dark) ---------------- #}
|
|
<section class="mt-5 rounded-2xl overflow-hidden border shadow-lg"
|
|
style="border-color: color-mix(in oklab, var(--bt-accent) 35%, transparent);">
|
|
<div class="h-1.5" style="background: var(--bt-accent);"></div>
|
|
<div class="bg-slate-900/60 backdrop-blur-sm text-white p-4 sm:p-5">
|
|
<h2 class="text-base font-semibold flex items-center gap-2">
|
|
<span class="inline-block w-2.5 h-2.5 rounded-full" style="background: var(--bt-accent);"></span>
|
|
Filter & Search
|
|
</h2>
|
|
|
|
<form method="get" class="mt-3 grid grid-cols-1 md:grid-cols-4 gap-3">
|
|
<div>
|
|
<label class="text-xs text-white/60">Status</label>
|
|
<select name="status"
|
|
class="block w-full bg-slate-900/70 text-white border border-white/15 rounded-lg px-2 py-2
|
|
focus:outline-none focus:ring-2"
|
|
style="--tw-ring-color: color-mix(in oklab, var(--bt-accent) 60%, transparent);">
|
|
<option value="">Any</option>
|
|
{% for s in ['submitted','triage','in_progress','awaiting_review','done','blocked','needs_more_info','cancelled'] %}
|
|
<option value="{{s}}" {{ 'selected' if request.args.get('status')==s else '' }}>{{ s.replace('_',' ').title() }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="text-xs text-white/60">Assignee (Discord ID)</label>
|
|
<input name="assignee" value="{{ request.args.get('assignee','') }}"
|
|
class="w-full bg-slate-900/70 text-white placeholder-white/40 border border-white/15 rounded-lg px-3 py-2
|
|
focus:outline-none focus:ring-2"
|
|
style="--tw-ring-color: color-mix(in oklab, var(--bt-accent) 60%, transparent);"
|
|
placeholder="1234567890" />
|
|
</div>
|
|
|
|
<div class="md:col-span-2">
|
|
<label class="text-xs text-white/60">Search</label>
|
|
<div class="flex gap-2">
|
|
<input name="q" value="{{ request.args.get('q','') }}"
|
|
class="w-full bg-slate-900/70 text-white placeholder-white/40 border border-white/15 rounded-lg px-3 py-2
|
|
focus:outline-none focus:ring-2"
|
|
style="--tw-ring-color: color-mix(in oklab, var(--bt-accent) 60%, transparent);"
|
|
placeholder="title, description, labels…" />
|
|
<button type="submit"
|
|
class="px-4 py-2 rounded-lg border border-white/15"
|
|
style="background: var(--bt-accent); color:#0a0a0a;">Apply</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</section>
|
|
|
|
{# ---------------- Card helpers ---------------- #}
|
|
{% macro status_badge(s) -%}
|
|
{% set label = s.replace('_',' ').title() %}
|
|
{% if s == 'done' %}
|
|
<span class="px-2 py-0.5 text-xs rounded-full bg-emerald-500/15 text-emerald-300 border border-emerald-400/30">{{ label }}</span>
|
|
{% elif s in ['blocked','cancelled'] %}
|
|
<span class="px-2 py-0.5 text-xs rounded-full bg-rose-500/15 text-rose-300 border border-rose-400/30">{{ label }}</span>
|
|
{% elif s in ['awaiting_review','needs_more_info'] %}
|
|
<span class="px-2 py-0.5 text-xs rounded-full bg-amber-500/15 text-amber-300 border border-amber-400/30">{{ label }}</span>
|
|
{% elif s in ['in_progress','triage'] %}
|
|
<span class="px-2 py-0.5 text-xs rounded-full bg-sky-500/15 text-sky-300 border border-sky-400/30">{{ label }}</span>
|
|
{% else %}
|
|
<span class="px-2 py-0.5 text-xs rounded-full bg-white/10 text-white/80 border border-white/20">{{ label }}</span>
|
|
{% endif %}
|
|
{%- endmacro %}
|
|
|
|
{% macro priority_chip(p) -%}
|
|
{% if p == 'urgent' %}
|
|
<span class="tag bg-rose-500/20 border-rose-400/30 text-rose-200">Priority: Urgent</span>
|
|
{% elif p == 'high' %}
|
|
<span class="tag bg-amber-500/20 border-amber-400/30 text-amber-100">Priority: High</span>
|
|
{% elif p == 'low' %}
|
|
<span class="tag bg-slate-500/20 border-slate-400/30 text-slate-200">Priority: Low</span>
|
|
{% else %}
|
|
<span class="tag">Priority: Normal</span>
|
|
{% endif %}
|
|
{%- endmacro %}
|
|
|
|
{# ---------------- Ticket Grid ---------------- #}
|
|
<div class="mt-6 grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-5">
|
|
{% for t in tickets %}
|
|
<a href="{{ url_for('ticket_detail', ticket_id=t.id) }}"
|
|
class="group relative block rounded-2xl border border-white/10 bg-gradient-to-b from-white/5 to-white/[0.02] p-4 sm:p-5
|
|
shadow-[0_10px_30px_-10px_rgba(0,0,0,.4)] hover:border-white/20 hover:shadow-[0_18px_40px_-12px_rgba(0,0,0,.6)] transition">
|
|
<span class="pointer-events-none absolute inset-0 rounded-2xl ring-1 ring-transparent group-hover:ring-[color:var(--bt-accent)]/40"></span>
|
|
|
|
<div class="flex items-start justify-between gap-3">
|
|
<div class="min-w-0">
|
|
<h3 class="font-semibold text-base sm:text-lg leading-snug truncate">
|
|
<span class="text-white/50">#{{ t.id }}</span> · {{ t.title }}
|
|
</h3>
|
|
<div class="mt-1 text-xs text-white/60">Opened by {{ t.created_by_name or t.created_by_id or 'Unknown' }}</div>
|
|
</div>
|
|
{{ status_badge(t.status) }}
|
|
</div>
|
|
|
|
<p class="mt-3 text-white/85 text-sm sm:text-[0.95rem]"
|
|
style="display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;">
|
|
{{ t.description }}
|
|
</p>
|
|
|
|
<div class="mt-3 flex flex-wrap items-center gap-2 text-xs">
|
|
{{ priority_chip(t.priority) }}
|
|
{% if t.sprint %}<span class="tag">Sprint: {{ t.sprint }}</span>{% endif %}
|
|
{% if t.due_at %}<span class="tag bg-rose-500/15 border-rose-400/30 text-rose-200">Due: {{ t.due_at.strftime('%Y-%m-%d') }}</span>{% endif %}
|
|
{% for label in t.label_list() %}<span class="tag">{{ label }}</span>{% endfor %}
|
|
</div>
|
|
|
|
<div class="mt-4 flex items-center justify-between text-xs text-white/60">
|
|
<div>
|
|
{% if t.assignee_name or t.assignee_id %}
|
|
Assigned to <b class="text-white/80">{{ t.assignee_name or ('<@' ~ t.assignee_id ~ '>') }}</b>
|
|
{% else %}
|
|
<span class="text-white/40 italic">Unassigned</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="whitespace-nowrap">Updated {{ t.updated_at.strftime('%Y-%m-%d %H:%M') }} UTC</div>
|
|
</div>
|
|
</a>
|
|
{% else %}
|
|
<div class="text-white/60">No tickets found.</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endblock %}
|