{# =========================================================================== jobs/_row.html — compact (collapsed) job row =========================================================================== PORT NOTE (load-bearing — the expand/collapse interaction depends on ALL of these EXACTLY; keep attribute names + the placeholder-row pattern intact): • data-dedup-key, data-expand-url, data-collapse-url • hx-push-url="false" • the inline onclick that runs htmx.ajax(GET, expand|collapse url, {target: nextElementSibling, swap:'outerHTML'}) and flips this.dataset.expanded. • The hidden placeholder that FOLLOWS this row is emitted by jobs/_table.html and restored by jobs/_row_collapse_response.html. The compact row is rendered for both the full page and the /jobs/table partial. DESIGN DECISION #4 — "dense table, no card conversion": This stays a real row. No card/list reflow. We only re-weighted the hierarchy WITHIN the row so a triager's eye lands Score → Title → meta: · Title = secondary anchor: slate-100 medium, now preceded by a chevron that rotates 90° when the row is expanded (the affordance the old row lacked). · Company/Location/Salary/Posted = de-emphasised meta (slate-400/500, 13px). · Row hover = slate-800/40; expanded row = slate-800/60 (via data-expanded). Row vertical padding bumped (py-1.5 → py-2.5) ONLY because the score chip is 44px tall and sets the row height; text density is otherwise unchanged. #} {# Salary formatting — UNCHANGED from original. #} {% if job.salary_min and job.salary_max %} {% set salary = "$%dk–$%dk" | format((job.salary_min // 1000), (job.salary_max // 1000)) %} {% elif job.salary_max %} {% set salary = "up to $%dk" | format(job.salary_max // 1000) %} {% elif job.salary_min %} {% set salary = "$%dk+" | format(job.salary_min // 1000) %} {% else %} {% set salary = "N/A" %} {% endif %} {# Recency display (#365): best-known posting date, honestly labeled. No posted_date → fall back to first_seen and say "seen"; an approximate/proxy date (relative-string parse, alert-email header) gets a "~" so the UI never overclaims "posted X ago". #} {% set posted = job.posted_date if job.posted_date else job.first_seen %} {% if not job.posted_date %} {% set posted_marker = 'seen ' %} {% set posted_title_label = 'first seen ' %} {% elif job.posted_date_precision in ('approximate', 'proxy') %} {% set posted_marker = '~' %} {% set posted_title_label = 'posted (approximate) ' %} {% else %} {% set posted_marker = '' %} {% set posted_title_label = 'posted ' %} {% endif %} {# Review-needed flag — UNCHANGED logic from original (Phase 47.06). #} {% set _review_reasons = job.unresolved_reasons | from_json %} {% set _review_locs = job.locations_structured | from_json %} {% set _loc_unresolved = false %} {% if _review_locs and _review_locs is not mapping and _review_locs is not string %} {% set _loc_unresolved = (_review_locs | selectattr('unresolved') | list | length) > 0 %} {% endif %} {% set _needs_review = (_review_reasons | length > 0) or _loc_unresolved %} {% set _review_labels = _review_reasons + (['unresolved location'] if _loc_unresolved else []) %} {# SCORE — the grade chip. Primary anchor. #} {% with oob=false %}{% include "jobs/_score_cell.html" %}{% endwith %} {# TITLE — secondary anchor, with the expand chevron. #} (see the
{# Chevron rotates via the [data-expanded=true] rule on the