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>
261 lines
11 KiB
HTML
261 lines
11 KiB
HTML
{#
|
||
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">
|
||
«
|
||
</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)">
|
||
«
|
||
</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">
|
||
»
|
||
</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)">
|
||
»
|
||
</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 %}
|