Files
rar-autopass/app/web/templates/_submissions.html
Claude Agent 8f39dfbc1e feat(5.16): aliniere lista/preview la mockup + fix lock seed la boot
Implementeaza planul aprobat din docs/raport-comparatie-mockup-5.16.md (T-1..T-9):

- T-1/T-8: rand lista 4->2 linii (placuta primar + cod RAR · operatie · data + pill),
  fallback placuta, eticheta-problema 10px->--fs-xs (_submissions.html, base.html)
- T-2: pill slim restilat fill-tint + dot 7px + text colorat per stare (base.html)
- T-3: bug 4a coliziune pill/vehicul in preview — col-stare 104->140px (base.html)
- T-4: preview 8->5 coloane (scos #, KM, Note; motivul -> title pe pill)
- T-5: titlu sectiune "Trimiterile tale" -> sr-only (a11y) + badge/export discret
- T-6: linia plan N/60 in corp doar pe avertizare; consum normal in badge+burger
- T-7: guard chenar gol chips extra (_chips_prestatii.html)
- T-9: "Anuleaza"->"Renunta"; nume operatie emfatic bold

Fix boot: init_db reincarca seedul de ~17k operatii (5.18) pe FIECARE pornire, pe
API + worker concurent -> "database is locked" la al doilea proces. Guard "_if_empty"
pe mapping_suggestions (ca seed_nomenclator_if_empty) -> boot rapid, fara cursa.

Teste actualizate (slim 2-linii, fallback placuta, plan in burger). TODOS.md:
defer trackuit (eroare HTMX lista, retokenizare px, diacritice).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 14:44:10 +00:00

261 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{#
OOB: actualizeaza inputul id="f-page" din #filtre-trimiteri.
Reincarcarea (hx-include="#filtre-trimiteri") preia automat pagina curenta.
Elementul OOB e extras din continutul normal de HTMX inainte de swap in #submissions-wrap.
#}
<input type="hidden" id="f-page" name="page" value="{{ page | default(1) }}" hx-swap-oob="true">
{# OOB: re-randeaza pill-urile de stare (in bara de filtre, in afara #submissions-wrap) cu
contoarele si starea activa proaspete la fiecare reincarcare a tabelului. #}
<span hx-swap-oob="innerHTML:#pills-categorii">{% include '_pills.html' %}</span>
{# Versiunea datelor cu care s-a randat tabelul; pollerul "Date noi" o compara. #}
<span id="trimiteri-versiune" data-v="{{ versiune_trimiteri | default('') }}" hidden></span>
{% if bulk_message %}
{# Sumar actiune bulk (US-010 PRD 5.15): afisat dupa bulk-fix, disparut la urmatoarea reincarcare. #}
<div class="bulk-message" role="status" aria-live="polite"
style="font-size:13px; color:var(--ink); background:var(--card2);
border:1px solid var(--line); border-radius:6px;
padding:6px 10px; margin-bottom:8px;">
{{ bulk_message }}
</div>
{% endif %}
{% if rows %}
{# Form bulk cu DOUA actiuni: (1) aplica cod RAR la selectate (bulk-fix, US-010),
(2) sterge selectate (sterge-bulk). Selectia opereaza DOAR pe randuri blocate
(gestionabil); sent/sending/queued nu au checkbox (read-only).
Butonul "Aplica cod" foloseste hx-post propriu (override form action).
hx-disinherit="hx-confirm" pe form => butonul aplica-cod NU mosteneste confirmare. #}
<form id="bulk-trimiteri"
hx-post="/trimiteri/sterge-bulk"
hx-target="#submissions-wrap"
hx-swap="innerHTML"
hx-confirm="Stergi definitiv trimiterile selectate?"
hx-disinherit="hx-confirm"
style="margin:0;">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div style="display:flex; justify-content:flex-end; align-items:center;
gap:6px; margin-bottom:8px; flex-wrap:wrap;">
{# Bulk-fix: input cod + buton aplica (US-010 PRD 5.15) #}
<input type="text" name="cod_prestatie" id="bulk-fix-cod"
placeholder="Cod RAR (ex: OE-1)"
autocomplete="off" autocapitalize="characters"
style="width:120px; font-size:12px; padding:3px 7px;
border:1px solid var(--line); border-radius:5px;
background:var(--card2); color:var(--ink);"
aria-label="Cod RAR de aplicat la randurile selectate">
<button type="button"
hx-post="/trimiteri/bulk-fix"
hx-target="#submissions-wrap"
hx-swap="innerHTML"
style="background:var(--card); color:var(--accent); border-color:var(--accent);
font-size:13px; padding:4px 10px; border-radius:5px; cursor:pointer;"
aria-label="Aplica codul RAR la randurile blocate selectate">
Aplica cod
</button>
{# Separator vizual #}
<span style="color:var(--muted); font-size:11px; padding:0 2px;" aria-hidden="true">|</span>
{# Bulk-delete: pastreaza exact comportamentul existent #}
<button type="submit" id="bulk-sterge-btn"
style="background:var(--card); color:var(--err); border-color:var(--err);
font-size:13px; padding:4px 10px; border-radius:5px; cursor:pointer;">
Sterge selectate
</button>
</div>
{# Lista slim trimiteri (US-004, PRD 5.15).
Inlocuieste tabelul cu randuri compacte: VIN mono + operatie·ora + pill.
Nr. inmatriculare, data prestatie si nr. prezentare RAR raman accesibile
pe linia meta discreta (linia 3) si in modalul de detaliu. #}
<ul class="lista-trimiteri-slim" role="list"
aria-label="Lista trimiteri">
{% for r in rows %}
{# Randul slim: stanga = VIN mono scurt (L1) + operatie·ora muted (L2) + meta (L3);
dreapta = pill de stare. Click deschide modalul global (#detaliu-modal-body).
Clickabil/focusabil (role=button); Enter/Space deschid modalul (JS in base.html). #}
<li id="trimitere-row-{{ r.id }}"
class="trimitere-slim"
data-detaliu-id="{{ r.id }}"
hx-get="/_fragments/trimitere/{{ r.id }}"
hx-target="#detaliu-modal-body"
hx-swap="innerHTML"
role="button" tabindex="0"
aria-haspopup="dialog"
style="cursor:pointer;"
title="Click pentru detaliul complet">
{# Zona checkbox — nu declanseaza modalul (stopPropagation).
Vizibila DOAR pe randurile gestionabile (error/needs_data/needs_mapping).
Latimea fixa previne reflow la prezenta/absenta checkbox-ului. #}
<div style="flex:0 0 22px; display:flex; align-items:center;" onclick="event.stopPropagation();">
{% if r.gestionabil %}
<input type="checkbox" name="submission_id" value="{{ r.id }}"
aria-label="Selecteaza trimiterea #{{ r.id }} pentru stergere">
{% endif %}
</div>
{# Bloc text principal — stanga, ocupa spatiul ramas. Rand de 2 linii (spec 5.16):
L1 = placuta (identificator primar); L2 = cod RAR · operatie · data prestatie. #}
<div style="flex:1 1 auto; min-width:0;">
{# Linia 1: nr. inmatriculare (placuta) — identificatorul primar pe care il
scaneaza operatorul. .slim-vin reumplut (acelasi nume de clasa, churn minim).
Fallback cand placuta lipseste ('—'): VIN scurt, apoi mesaj neutru
(nu randa em-dash izolat ca identificator). #}
{% if r.prez.vehicul_nr and r.prez.vehicul_nr != '—' %}
<div class="slim-vin">{{ r.prez.vehicul_nr }}</div>
{% elif r.prez.vin_scurt and r.prez.vin_scurt != '—' %}
<div class="slim-vin muted">{{ r.prez.vin_scurt }}</div>
{% else %}
<div class="slim-vin muted">fara numar</div>
{% endif %}
{# Linia 2: cod RAR (sau 'nemapat') · operatie (ink, ellipsis) · data prestatie.
Separatorul "·" e injectat prin CSS intre celule. Operatia primeste ellipsis
ca randul sa NU treaca pe a 3-a linie nici la 390px.
VIN integral, #id_prezentare si secundele traiesc in modalul de detaliu. #}
<div class="slim-meta slim-rand2">
{% if r.prez.cod_rar and r.prez.cod_rar != '—' %}
<span class="cod-rar-cod">{{ r.prez.cod_rar }}</span>
{% else %}
<span class="cod-rar-cod cod-rar-sub muted">nemapat</span>
{% endif %}
<span class="slim-op">{{ r.prez.operatie }}</span>
{% if r.prez.data_prestatie and r.prez.data_prestatie != '—' %}
<span class="slim-data muted">{{ r.prez.data_prestatie }}</span>
{% endif %}
</div>
{# Micro-linie umana a problemei — text mic s-error, DOAR pe stari de problema
(loud-on-exception D6). Randul normal/finalizat ramane strict 2 linii.
Token tipografic --fs-xs (>=12px, scala 5.16). #}
{% if r.eticheta_problema and r.eticheta_problema != r.stare_scurt and r.eticheta_problema != r.stare_text %}
<div class="eticheta-problema s-error">{{ r.eticheta_problema }}</div>
{% endif %}
</div>
{# Pill de stare — dreapta, flex:none #}
<span class="pill {{ r.stare_css }}" title="{{ r.stare_text }}"
style="flex:0 0 auto; white-space:nowrap;">{{ r.stare_scurt }}</span>
</li>
{% endfor %}
</ul>
</form>
{#
Paginare numerotata.
Afisata doar cand exista mai mult de o pagina.
Fiecare link pastreaza filtrele curente (status, vehicul, data_de, data_pana).
Pagina curenta: aria-current="page" (semantic).
#}
{% if total is defined %}
<div aria-live="polite"
style="font-size:12px; color:var(--muted); text-align:right; margin-top:6px; margin-bottom:2px;">
{% if total == 0 %}
0 trimiteri
{% else %}
{{ page_start }}{{ page_end }} din {{ total }}
{% endif %}
</div>
{% endif %}
{% if pages is defined and pages > 1 %}
{#
Construim param-string pentru filtrele curente (fara page) — refolosit in fiecare link.
Filtrul status vine din pill-uri (nu din form); il pastram in URL.
#}
{% set pq = "" %}
{% if f_status %}{% set pq = pq + "&status=" + f_status %}{% endif %}
{% if f_vehicul %}{% set pq = pq + "&vehicul=" + f_vehicul %}{% endif %}
{% if f_data_de %}{% set pq = pq + "&data_de=" + f_data_de %}{% endif %}
{% if f_data_pana %}{% set pq = pq + "&data_pana=" + f_data_pana %}{% endif %}
<nav aria-label="Paginare trimiteri"
style="display:flex; justify-content:center; gap:4px; flex-wrap:wrap; margin-top:10px;">
{# Buton Anterior #}
{% if page > 1 %}
<button type="button"
hx-get="/_fragments/submissions?page={{ page - 1 }}{{ pq }}"
hx-target="#submissions-wrap"
hx-swap="innerHTML"
style="padding:3px 10px; border-radius:6px; font-size:13px; cursor:pointer;
border:1px solid var(--line); background:var(--card); color:var(--fg);"
aria-label="Pagina anterioara">
&laquo;
</button>
{% else %}
<button type="button" disabled
style="padding:3px 10px; border-radius:6px; font-size:13px;
border:1px solid var(--line); background:var(--card); color:var(--muted);
opacity:0.4; cursor:default;"
aria-label="Pagina anterioara (indisponibila)">
&laquo;
</button>
{% endif %}
{# Numerele de pagina #}
{% for p in range(1, pages + 1) %}
{% if p == page %}
<button type="button"
aria-current="page"
style="padding:3px 10px; border-radius:6px; font-size:13px; cursor:default;
border:1px solid var(--accent); background:var(--accent); color:#fff;
font-weight:700;">
{{ p }}
</button>
{% else %}
<button type="button"
hx-get="/_fragments/submissions?page={{ p }}{{ pq }}"
hx-target="#submissions-wrap"
hx-swap="innerHTML"
style="padding:3px 10px; border-radius:6px; font-size:13px; cursor:pointer;
border:1px solid var(--line); background:var(--card); color:var(--fg);">
{{ p }}
</button>
{% endif %}
{% endfor %}
{# Buton Urmator #}
{% if page < pages %}
<button type="button"
hx-get="/_fragments/submissions?page={{ page + 1 }}{{ pq }}"
hx-target="#submissions-wrap"
hx-swap="innerHTML"
style="padding:3px 10px; border-radius:6px; font-size:13px; cursor:pointer;
border:1px solid var(--line); background:var(--card); color:var(--fg);"
aria-label="Pagina urmatoare">
&raquo;
</button>
{% else %}
<button type="button" disabled
style="padding:3px 10px; border-radius:6px; font-size:13px;
border:1px solid var(--line); background:var(--card); color:var(--muted);
opacity:0.4; cursor:default;"
aria-label="Pagina urmatoare (indisponibila)">
&raquo;
</button>
{% endif %}
</nav>
{% endif %}
{% elif filtru_activ %}
<div class="empty">
Nimic pe filtrul curent.
<a href="#"
onclick="var f=document.getElementById('filtre-trimiteri'); if(f) f.reset(); return true;"
hx-get="/_fragments/submissions" hx-target="#submissions-wrap" hx-swap="innerHTML">
sterge filtrele
</a>
</div>
{% else %}
<div class="empty">
Nicio trimitere inca —
<a href="/?tab=acasa">incepe cu un import</a>
sau trimite o prezentare prin API.
</div>
{% endif %}