feat: implement task queue statistics and add UI templates for job management and visualization.

This commit is contained in:
2026-08-15 00:00:41 -07:00
parent 5510e3ac82
commit 286278c1ec
12 changed files with 1144 additions and 644 deletions
+139
View File
@@ -0,0 +1,139 @@
package ui
const applyTabHTML = `{{ define "applyTab" }}
<div class="space-y-4">
<!-- Filter Bar -->
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
<div>
<h2 class="text-base font-bold text-white">Application Pipeline</h2>
<p class="text-xs text-slate-400 mt-0.5">Track all saved and in-progress applications</p>
</div>
<div class="flex items-center gap-2 flex-wrap">
<input id="apply-filter-text" type="search" placeholder="Search title, company…"
class="px-3 py-1.5 text-xs rounded-lg bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-indigo-500 w-44 placeholder:text-slate-500">
<select id="apply-filter-status"
class="px-3 py-1.5 text-xs rounded-lg bg-slate-800 border border-slate-700 text-white focus:outline-none focus:border-indigo-500">
<option value="active">Active (Saved + In Progress)</option>
<option value="SAVED">💾 Saved</option>
<option value="APPLIED">🚀 Applied</option>
<option value="INTERVIEW">💬 Interview</option>
<option value="OFFER">🎉 Offer</option>
<option value="REJECTED">❌ Rejected</option>
<option value="all">All</option>
</select>
<button hx-get="/applications" hx-target="#apply-container" hx-swap="innerHTML"
class="px-3 py-1.5 text-xs font-semibold text-indigo-400 hover:text-indigo-300 bg-slate-800 border border-slate-700 rounded-lg transition-colors">
↺ Refresh
</button>
</div>
</div>
<!-- Apply Table Container -->
<div id="apply-container"
hx-get="/applications" hx-trigger="refresh" hx-swap="innerHTML">
{{ template "applyTable" .Applications }}
</div>
</div>
{{ end }}`
const applyTableHTML = `{{ define "applyTable" }}
<div class="overflow-x-auto rounded-xl border border-slate-800">
<table id="apply-table" class="w-full text-left text-xs text-slate-300">
<thead class="bg-slate-900/80 text-slate-400 uppercase font-semibold text-[10px]">
<tr>
<th class="px-4 py-3" data-sort="title">Job &amp; Company</th>
<th class="px-4 py-3">Pipeline Stage</th>
<th class="px-4 py-3" data-sort="score">Score</th>
<th class="px-4 py-3 text-center">Link</th>
<th class="px-4 py-3" data-sort="updated">Last Updated</th>
<th class="px-4 py-3 text-right">Change Status</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-800 bg-slate-900/40">
{{ range . }}
{{ if .App }}
<tr class="hover:bg-slate-800/40 transition-colors group" data-row data-app-status="{{ .App.Status }}">
<!-- Title & Company -->
<td class="px-4 py-3" data-sort-val="{{ .Title }}">
<button hx-get="/jobs/{{ .ID }}"
hx-target="#detail-modal-body"
hx-swap="innerHTML"
onclick="openDetailModal()"
class="text-left font-bold text-white group-hover:text-indigo-300 hover:underline block truncate max-w-[200px]">
{{ .Title }}
</button>
<span class="text-[11px] text-indigo-400 font-semibold">{{ .Company }}</span>
<span class="text-[11px] text-slate-500 ml-1">📍 {{ .Location }}</span>
</td>
<!-- Pipeline Stage (arrow indicator) -->
<td class="px-4 py-3">
<div class="flex items-center gap-0.5 text-[10px] font-semibold">
<span class="px-2 py-0.5 rounded-l-full {{ if eq .App.Status "SAVED" }}bg-slate-500 text-white{{ else }}bg-slate-800/60 text-slate-600{{ end }}">Saved</span>
<span class="text-slate-700">▶</span>
<span class="px-2 py-0.5 {{ if eq .App.Status "APPLIED" }}bg-indigo-600 text-white{{ else }}bg-slate-800/60 text-slate-600{{ end }}">Applied</span>
<span class="text-slate-700">▶</span>
<span class="px-2 py-0.5 {{ if eq .App.Status "INTERVIEW" }}bg-amber-500 text-white{{ else }}bg-slate-800/60 text-slate-600{{ end }}">Interview</span>
<span class="text-slate-700">▶</span>
{{ if eq .App.Status "OFFER" }}
<span class="px-2 py-0.5 rounded-r-full bg-emerald-500 text-white">Offer 🎉</span>
{{ else if eq .App.Status "REJECTED" }}
<span class="px-2 py-0.5 rounded-r-full bg-rose-500 text-white">Rejected</span>
{{ else }}
<span class="px-2 py-0.5 rounded-r-full bg-slate-800/60 text-slate-600">Offer</span>
{{ end }}
</div>
</td>
<!-- Score -->
<td class="px-4 py-3" data-sort-val="{{ if .Analysis }}{{ printf "%.0f" .Analysis.Score }}{{ else }}-1{{ end }}">
{{ if .Analysis }}
<span class="font-bold {{ if .Analysis.IsQualified }}text-emerald-400{{ else }}text-slate-500{{ end }}">
{{ printf "%.0f" .Analysis.Score }}
</span>
{{ else }}
<span class="text-slate-600">–</span>
{{ end }}
</td>
<!-- Link -->
<td class="px-4 py-3 text-center">
<a href="{{ .ApplyURL }}" target="_blank"
class="inline-flex items-center justify-center w-7 h-7 rounded bg-slate-800 hover:bg-slate-700 text-indigo-400 border border-slate-700 transition-colors">
🔗
</a>
</td>
<!-- Last Updated -->
<td class="px-4 py-3 text-slate-400 text-[11px]" data-sort-val="{{ .App.UpdatedAt.Unix }}">
{{ formatDate .App.UpdatedAt }}
</td>
<!-- Change Status -->
<td class="px-4 py-3 text-right">
<select hx-patch="/applications/{{ .App.ID }}/status"
hx-target="#apply-container"
hx-swap="innerHTML"
class="px-2 py-1 bg-slate-900 border border-slate-700 rounded-lg text-slate-200 text-[10px] focus:outline-none focus:border-indigo-500">
<option value="SAVED" {{ if eq .App.Status "SAVED" }}selected{{ end }}>💾 Saved</option>
<option value="APPLIED" {{ if eq .App.Status "APPLIED" }}selected{{ end }}>🚀 Applied</option>
<option value="INTERVIEW" {{ if eq .App.Status "INTERVIEW" }}selected{{ end }}>💬 Interview</option>
<option value="OFFER" {{ if eq .App.Status "OFFER" }}selected{{ end }}>🎉 Offer</option>
<option value="REJECTED" {{ if eq .App.Status "REJECTED" }}selected{{ end }}>❌ Rejected</option>
</select>
</td>
</tr>
{{ end }}
{{ else }}
<tr>
<td colspan="6" class="px-4 py-12 text-center text-slate-500">
<p class="text-lg mb-2">📋</p>
<p>No saved applications yet. Go to <strong>Jobs</strong> tab and click <strong>+ Save</strong> on any job.</p>
</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
{{ end }}`