Skip to content

Commit 0c7780a

Browse files
mcherifclaude
andcommitted
Fix bulk-reject-stale call + add list view for Rejected/Expired tabs
- Fix: confirmRejectStale was passing a fetch-options object as the api() method arg instead of the string 'POST', causing every bulk reject to 500 - Add compact list view (title, company, score badge, relative date, external link) that renders automatically when Rejected or Expired tab is active - renderList() / renderCardWrap() / renderEmpty() all manage list/card/empty visibility consistently Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f3930c9 commit 0c7780a

1 file changed

Lines changed: 53 additions & 8 deletions

File tree

ui/index.html

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@
4848
.btn-reject-stale{display:none;align-items:center;gap:6px;padding:7px 13px;background:var(--red-light);color:var(--red);border:1px solid var(--red-mid);border-radius:var(--radius);font-family:'Inter',sans-serif;font-size:12.5px;font-weight:600;cursor:pointer;transition:all var(--transition);margin-right:6px;}
4949
.btn-reject-stale:hover{background:var(--red-mid);color:var(--red);border-color:var(--red);}
5050
.btn-reject-stale.visible{display:flex;}
51+
.list-view{padding:20px 28px;overflow-y:auto;flex:1;}
52+
.list-view-header{font-size:12px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em;padding:0 4px 8px;border-bottom:1px solid var(--border-subtle);margin-bottom:4px;display:flex;gap:12px;}
53+
.list-view-header span:first-child{flex:1}
54+
.list-row{display:flex;align-items:center;gap:12px;padding:9px 4px;border-bottom:1px solid var(--border-subtle);transition:background var(--transition);}
55+
.list-row:hover{background:var(--bg-hover);border-radius:6px;}
56+
.list-avatar{width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;flex-shrink:0;}
57+
.list-info{flex:1;min-width:0;}
58+
.list-title{font-size:13px;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
59+
.list-company{font-size:11.5px;color:var(--text-secondary);}
60+
.list-score{font-size:11px;font-weight:700;padding:2px 8px;border-radius:10px;width:46px;text-align:center;flex-shrink:0;}
61+
.list-date{font-size:11.5px;color:var(--text-muted);width:60px;text-align:right;flex-shrink:0;}
62+
.list-link{width:26px;height:26px;border-radius:6px;background:var(--bg-subtle);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;color:var(--text-muted);text-decoration:none;transition:all var(--transition);flex-shrink:0;}
63+
.list-link:hover{background:var(--accent-light);color:var(--accent);border-color:var(--accent-mid);}
5164
.btn-pipeline{display:flex;align-items:center;gap:8px;padding:8px 16px;background:var(--accent);color:#fff;border:none;border-radius:var(--radius);font-family:'Inter',sans-serif;font-size:13px;font-weight:600;cursor:pointer;transition:all var(--transition);box-shadow:var(--shadow-sm);}
5265
.btn-pipeline:hover{background:#2563eb;box-shadow:0 0 0 3px rgba(59,130,246,.25);}
5366
.btn-pipeline svg{width:14px;height:14px;}
@@ -506,6 +519,13 @@
506519
</div>
507520
</div>
508521
</div>
522+
523+
<div class="list-view" id="listView" style="display:none">
524+
<div class="list-view-header">
525+
<span>Job</span><span style="width:46px;text-align:center">Score</span><span style="width:60px;text-align:right">Added</span><span style="width:26px"></span>
526+
</div>
527+
<div id="listViewRows"></div>
528+
</div>
509529
</div>
510530

511531
<div class="toast" id="toast"></div>
@@ -631,11 +651,13 @@
631651
const data = await api(`/api/jobs?status=${status}`);
632652
jobs = data.jobs || [];
633653
idx = 0;
634-
if (jobs.length > 0) {
654+
if (jobs.length === 0) {
655+
renderEmpty(status);
656+
} else if (_LIST_STATUSES.has(status)) {
657+
renderList(jobs);
658+
} else {
635659
renderCardWrap();
636660
renderCard(idx);
637-
} else {
638-
renderEmpty(status);
639661
}
640662
updateNav();
641663
} catch(e) {
@@ -673,11 +695,7 @@
673695
async function confirmRejectStale() {
674696
closeRejectStaleModal();
675697
try {
676-
const res = await api('/api/jobs/bulk-reject-stale', {
677-
method: 'POST',
678-
headers: {'Content-Type': 'application/json'},
679-
body: JSON.stringify({status: currentStatus, older_than_days: 14})
680-
});
698+
const res = await api('/api/jobs/bulk-reject-stale', 'POST', {status: currentStatus, older_than_days: 14});
681699
showToast(`Rejected ${res.rejected} stale job${res.rejected === 1 ? '' : 's'}`, '');
682700
loadStats();
683701
loadJobs(currentStatus);
@@ -809,18 +827,45 @@
809827
updateNav();
810828
}
811829

830+
const _LIST_STATUSES = new Set(['rejected', 'expired']);
831+
812832
function renderEmpty(status) {
813833
document.getElementById('cardWrap').style.display = 'none';
834+
document.getElementById('listView').style.display = 'none';
814835
document.getElementById('emptyTitle').textContent = `No ${status} jobs`;
815836
document.getElementById('emptyState').style.display = 'flex';
816837
document.getElementById('navCounter').textContent = `0 ${status} jobs`;
817838
}
818839

819840
function renderCardWrap() {
820841
document.getElementById('emptyState').style.display = 'none';
842+
document.getElementById('listView').style.display = 'none';
821843
document.getElementById('cardWrap').style.display = '';
822844
}
823845

846+
function renderList(jobList) {
847+
document.getElementById('emptyState').style.display = 'none';
848+
document.getElementById('cardWrap').style.display = 'none';
849+
const scoreColor = s => s >= 70 ? `background:var(--green-mid);color:var(--green)` :
850+
s >= 50 ? `background:var(--orange-mid);color:var(--orange)` :
851+
`background:var(--red-mid);color:var(--red)`;
852+
document.getElementById('listViewRows').innerHTML = jobList.map(j => `
853+
<div class="list-row">
854+
<div class="list-avatar" style="background:${j.avatar_color}">${esc(j.avatar_text)}</div>
855+
<div class="list-info">
856+
<div class="list-title">${esc(j.title)}</div>
857+
<div class="list-company">${esc(j.company)}</div>
858+
</div>
859+
<div class="list-score" style="${j.fit_score != null ? scoreColor(j.fit_score) : 'color:var(--text-muted)'}">${j.fit_score != null ? j.fit_score : '—'}</div>
860+
<div class="list-date">${j.created_at ? relativeDate(j.created_at) : '—'}</div>
861+
<a class="list-link" href="${esc(j.url)}" target="_blank" rel="noopener" title="Open job posting">
862+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
863+
</a>
864+
</div>`).join('');
865+
document.getElementById('listView').style.display = '';
866+
document.getElementById('navCounter').textContent = `${jobList.length} ${currentStatus} jobs`;
867+
}
868+
824869
// ── Navigation ─────────────────────────────────────────────────────────────
825870
function updateNav() {
826871
const total = jobs.length;

0 commit comments

Comments
 (0)