|
48 | 48 | .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;} |
49 | 49 | .btn-reject-stale:hover{background:var(--red-mid);color:var(--red);border-color:var(--red);} |
50 | 50 | .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);} |
51 | 64 | .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);} |
52 | 65 | .btn-pipeline:hover{background:#2563eb;box-shadow:0 0 0 3px rgba(59,130,246,.25);} |
53 | 66 | .btn-pipeline svg{width:14px;height:14px;} |
|
506 | 519 | </div> |
507 | 520 | </div> |
508 | 521 | </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> |
509 | 529 | </div> |
510 | 530 |
|
511 | 531 | <div class="toast" id="toast"></div> |
|
631 | 651 | const data = await api(`/api/jobs?status=${status}`); |
632 | 652 | jobs = data.jobs || []; |
633 | 653 | 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 { |
635 | 659 | renderCardWrap(); |
636 | 660 | renderCard(idx); |
637 | | - } else { |
638 | | - renderEmpty(status); |
639 | 661 | } |
640 | 662 | updateNav(); |
641 | 663 | } catch(e) { |
|
673 | 695 | async function confirmRejectStale() { |
674 | 696 | closeRejectStaleModal(); |
675 | 697 | 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}); |
681 | 699 | showToast(`Rejected ${res.rejected} stale job${res.rejected === 1 ? '' : 's'}`, ''); |
682 | 700 | loadStats(); |
683 | 701 | loadJobs(currentStatus); |
|
809 | 827 | updateNav(); |
810 | 828 | } |
811 | 829 |
|
| 830 | +const _LIST_STATUSES = new Set(['rejected', 'expired']); |
| 831 | + |
812 | 832 | function renderEmpty(status) { |
813 | 833 | document.getElementById('cardWrap').style.display = 'none'; |
| 834 | + document.getElementById('listView').style.display = 'none'; |
814 | 835 | document.getElementById('emptyTitle').textContent = `No ${status} jobs`; |
815 | 836 | document.getElementById('emptyState').style.display = 'flex'; |
816 | 837 | document.getElementById('navCounter').textContent = `0 ${status} jobs`; |
817 | 838 | } |
818 | 839 |
|
819 | 840 | function renderCardWrap() { |
820 | 841 | document.getElementById('emptyState').style.display = 'none'; |
| 842 | + document.getElementById('listView').style.display = 'none'; |
821 | 843 | document.getElementById('cardWrap').style.display = ''; |
822 | 844 | } |
823 | 845 |
|
| 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 | + |
824 | 869 | // ── Navigation ───────────────────────────────────────────────────────────── |
825 | 870 | function updateNav() { |
826 | 871 | const total = jobs.length; |
|
0 commit comments