Dogfood pe import + Trimiteri (mobil/tableta <1024px), pur CSS + markup, backend
trimitere neatins:
- Card compact real pentru .tabel-trimiteri (preview + Trimiteri): vehicul=titlu,
stare=pill dreapta-sus, operatie+cod, meta data/km muted, nota mica. Inlocuieste
stiva generica eticheta+valoare (carduri de ~450px -> ~135px). Anuleaza regula
desktop tr.trimitere-row > td{padding:11px} in blocul compact.
- FIX editare preview: OOB swap pe <tr> esua tacit in htmx 1.9 (un <tr> brut se
pierde la parsarea unui fragment fara context de tabel) -> randul ramanea cu
starea veche dupa salvare. Inlocuit cu reload complet al preview-ului prin
HX-Trigger:reincarcaPreview + detalii randSalvat. /editeaza si /confirma-review
folosesc helper-ul _raspuns_rand_salvat.
- Feedback post-salvare: toast global "Randul N actualizat · <stare>" + scroll +
flash pe randul actualizat (base.html window.arataToast + listener randSalvat).
- Modal editare: Salveaza + Anuleaza pe acelasi rand (sistem .act): desktop text,
mobil doua iconite Lucide 44px alaturate (save/x). Macro icon('x') + .act-primary.
- Randuri deja-trimise/duplicate colapsate implicit in preview + toggle "Arata N".
- Select "Operatii de mapat" full-width pe mobil (nu mai iese din viewport).
- Bara de filtre Trimiteri adaptata mobil: pills pe banda cu scroll orizontal,
cautare vehicul proeminenta (nu 8 butoane full-width stivuite).
- Nota preview = culoarea camp-fix (accent) ca sa atraga atentia; hint-urile
camp-fix per-camp scoase (campul Note e self-explanatory).
- Confirmare trimitere: scos campul email (Declarant); text mai clar
("Confirma numarul din N gata de trimis"). Backend confirmed_by ramane optional.
Teste: contractul OOB (rupt in browser) inlocuit cu noul contract
(reincarcaPreview + randSalvat) in test_web_preview_edit / test_preview_edit_ui /
test_import_review. Suita: 992 passed (exclus live).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
215 lines
8.5 KiB
HTML
215 lines
8.5 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 rows %}
|
||
{# Form de stergere bulk. Selectia opereaza DOAR pe randuri blocate
|
||
(gestionabil); sent/sending/queued nu au checkbox (read-only). #}
|
||
<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; margin-bottom:8px;">
|
||
<button type="submit" id="bulk-sterge-btn"
|
||
style="background:var(--card); color:var(--err); border-color:var(--err); font-size:13px; padding:4px 10px;">
|
||
Sterge selectate
|
||
</button>
|
||
</div>
|
||
<div class="tablewrap tabel-trimiteri">
|
||
<table>
|
||
<thead><tr>
|
||
<th class="col-chk"><span class="muted" title="Selecteaza randuri blocate">✓</span></th>
|
||
<th class="col-id">#</th>
|
||
<th class="col-stare">Stare</th>
|
||
<th class="col-vehicul">Vehicul</th>
|
||
<th class="col-operatie">Operatie</th>
|
||
<th class="col-data">Data prestatie</th>
|
||
<th class="col-rar">Nr. prezentare RAR</th>
|
||
<th class="col-actualizat">Actualizat</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
{% for r in rows %}
|
||
{# Randul declanseaza deschiderea MODALULUI global (#detaliu-modal-body).
|
||
Clickabil/focusabil (role=button); Enter/Space deschid modalul (JS in base.html). #}
|
||
<tr id="trimitere-row-{{ r.id }}"
|
||
class="trimitere-row"
|
||
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">
|
||
<td class="col-chk" onclick="event.stopPropagation();">
|
||
{% if r.gestionabil %}
|
||
<input type="checkbox" name="submission_id" value="{{ r.id }}"
|
||
aria-label="Selecteaza trimiterea #{{ r.id }} pentru stergere">
|
||
{% endif %}
|
||
</td>
|
||
<td class="col-id muted" data-eticheta="#">{{ r.id }}</td>
|
||
<td class="col-stare" data-eticheta="Stare">
|
||
<span class="pill {{ r.stare_css }}" title="{{ r.stare_text }}">{{ r.stare_scurt }}</span>
|
||
{# Eticheta umana scurta sub pill — text mic, `s-error` pe error/needs_*
|
||
(singurele stari pe care `eticheta_problema` e ne-goala).
|
||
Stare transmisa prin TEXT, nu doar culoare. Codul brut ramane in modal. #}
|
||
{% 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 %}
|
||
</td>
|
||
<td class="col-vehicul" data-eticheta="Vehicul">
|
||
{{ r.prez.vehicul_nr }}
|
||
{% if r.prez.vin_scurt and r.prez.vin_scurt != '—' %}
|
||
{# VIN pe rand separat sub nr (element block, nu span inline) #}
|
||
<div class="muted" style="font-size:12px;">{{ r.prez.vin_scurt }}</div>
|
||
{% endif %}
|
||
</td>
|
||
<td class="col-operatie" data-eticheta="Operatie">
|
||
<div>{{ r.prez.operatie }}</div>
|
||
{# Doar codul RAR (ex. OE-2), FARA prefixul "cod RAR:" — chip muted discret;
|
||
cand nemapat afiseaza "nemapat" muted. #}
|
||
{% if r.prez.cod_rar and r.prez.cod_rar != '—' %}
|
||
<div class="cod-rar-sub"><span class="cod-rar-cod">{{ r.prez.cod_rar }}</span></div>
|
||
{% else %}
|
||
<div class="muted cod-rar-sub">nemapat</div>
|
||
{% endif %}
|
||
</td>
|
||
<td class="col-data" data-eticheta="Data prestatie">{{ r.prez.data_prestatie }}</td>
|
||
<td class="col-rar" data-eticheta="Nr. prezentare RAR">{{ r.id_prezentare or '—' }}</td>
|
||
<td class="col-actualizat muted" data-eticheta="Actualizat">{{ r.updated_at }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</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 %}
|