feat(5.10): UX trimiteri (pill filtre, paginare, editare) + Mapari in meniu + branding ROMFAST

14 stories TDD prin echipa de workeri (lead orchestreaza, 3 teammates pe valuri cu fisiere disjuncte; routes.py + base.html serializate ca fisiere fierbinti).

- US-001 fix filtrare data (_iso_date_prefix pe garda+comparatie, prinde timestamp cu ora)
- US-002/007 operatie service distincta in payload_view + afisare in detaliu
- US-003 pill-uri categorii (button/aria-pressed; needs_mapping --warn, needs_data/error --err); fara lista ID-uri/dropdown
- US-004 paginare numerotata 25/pag (total ramificat SQL-COUNT vs fetch-all+slice, clamp page, poll pastreaza pagina)
- US-005 VIN block-level sub nr
- US-006/006b editare cod RAR + validare nomenclator + recalcul idempotency (needs_data/needs_mapping via /corecteaza, error via /repune)
- US-008 card eroare 3-niveluri doar pe read-only + rezumat top-of-form
- US-009 Mapari in meniu hamburger; scoatere tab-bar + role=tablist orfan
- US-010/011 pagina Mapari consolidata + butoane icon SVG + dirty-state (fara kebab/emoji)
- US-012/012b header centrat + logo ROMFAST (/static/romfast_logo.png) in header
- US-013 paleta azur ROMFAST (#2E74D6/#1F66C9) + IBM Plex Sans/Mono self-host (woff2 reale)
- US-014 selector tema ciclic Light/Dark/Petrol/Auto + anti-FOUC pe 4 stari

Backend trimitere (worker/masina stari/idempotenta/mapping) + schema NEATINSE (UI/UX pur + 1 fix de filtrare).
VERIFY context curat PASS; /code-review high: 1 finding material reparat (US-006b). Regresie 896 passed, 1 skipped, 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-06-25 20:20:58 +00:00
parent 3bc0825e0b
commit 5a964a1a8d
43 changed files with 3949 additions and 414 deletions

View File

@@ -1,3 +1,15 @@
<style>
/* Pill-uri categorii blocate (US-003 PRD 5.10)
Culoarea e injectata inline (color_var: --warn / --err) dupa DESIGN.md:
Lipsa cod = --warn (chihlimbar), Date incomplete + Eroare = --err (rosu).
Activ = fundal pe culoarea categoriei (NU accent albastru — S1/A5). */
.pill-cat { transition: background 0.15s, color 0.15s; }
.pill-cat:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Activ: background = culoarea categoriei (currentColor = var(--err/--warn) din inline style),
text = var(--card) (contrast AA). NU accent albastru (S1/A5 DESIGN.md). */
.pill-cat[aria-pressed="true"] { background: currentColor !important; color: var(--card) !important; border-color: currentColor !important; }
.pill-cat[aria-pressed="true"] span { background: var(--card) !important; color: currentColor; }
</style>
<div id="status-bar" class="status-bar card"
hx-get="/_fragments/status"
hx-trigger="every 15s"
@@ -47,36 +59,49 @@
</span>
</div>
<!-- Necesita atentia ta (US-014): categorii actionabile — link la lista filtrata
+ identificatorii primelor randuri blocate. Se randeaza DOAR daca exista randuri
blocate; cand contorul ajunge 0 (sters/re-pus/purjat), sectiunea dispare. -->
{% if blocate_actionabil %}
<!-- Pill-uri categorii blocate (US-003 PRD 5.10): inlocuiesc lista de ID-uri.
<button> reale cu aria-pressed, focalizabile, activare Enter/Space.
Inactiv = contur+text pe culoarea categoriei; activ = umplere pe culoarea categoriei.
Pill ascuns cand n=0 (lista pills_categorii filtreaza deja). -->
{% if pills_categorii %}
<div style="margin-top:12px; padding-top:10px; border-top:1px solid var(--line);">
<div style="font-size:13px; font-weight:600; margin-bottom:8px;">Necesita atentia ta</div>
<div style="display:flex; gap:18px; flex-wrap:wrap;">
{% for cat in blocate_actionabil %}
<div style="min-width:200px;">
{# Link: filtreaza lista Trimiteri pe aceasta stare (HTMX in-place) cu fallback
deep-link server-side (?tab=acasa&status=...). #}
<a class="{{ cat.eticheta[2] }}" style="font-size:13px; font-weight:600; text-decoration:none;"
href="/?tab=acasa&status={{ cat.status }}"
hx-get="/_fragments/submissions?status={{ cat.status }}"
hx-target="#submissions-wrap" hx-swap="innerHTML"
onclick="var s=document.getElementById('trimiteri-section'); if(s) s.scrollIntoView({behavior:'smooth'});">
{{ cat.eticheta[0] }} ({{ cat.n }}) &rsaquo;
</a>
<ul style="list-style:none; margin:6px 0 0; padding:0;">
{% for r in cat.randuri %}
<li class="muted" style="font-size:12px;">
#{{ r.id }} {{ r.vin }}{% if r.nr %} / {{ r.nr }}{% endif %}
</li>
{% endfor %}
{% if cat.rest %}
<li class="muted" style="font-size:12px;">…si inca {{ cat.rest }}</li>
{% endif %}
</ul>
</div>
<div style="display:flex; gap:8px; flex-wrap:wrap; align-items:center;">
<span style="font-size:12px; color:var(--muted);">Necesita atentie:</span>
{% for pill in pills_categorii %}
<button type="button"
class="pill-cat"
aria-pressed="false"
hx-get="/_fragments/submissions?status={{ pill.status }}"
hx-target="#submissions-wrap"
hx-swap="innerHTML"
onclick="(function(b){
var pressed=b.getAttribute('aria-pressed')==='true';
document.querySelectorAll('.pill-cat').forEach(function(x){x.setAttribute('aria-pressed','false');});
if(!pressed){b.setAttribute('aria-pressed','true');}
var s=document.getElementById('trimiteri-section');
if(s){s.scrollIntoView({behavior:'smooth'});}
})(this)"
style="display:inline-flex; align-items:center; gap:5px;
padding:3px 10px; border-radius:99px; font-size:12px; font-weight:600;
cursor:pointer; border:1.5px solid var({{ pill.color_var }}); color:var({{ pill.color_var }});
background:transparent; transition:background 0.15s, color 0.15s;">
{{ pill.label }}
<span style="font-size:11px; font-weight:700; background:var({{ pill.color_var }}); color:var(--card);
padding:0 5px; border-radius:99px; min-width:18px; text-align:center;">{{ pill.n }}</span>
</button>
{% endfor %}
{# Buton "Toate" — reseteaza filtrul de categorie #}
<button type="button"
class="pill-cat-reset"
aria-label="Arata toate trimiterile"
hx-get="/_fragments/submissions"
hx-target="#submissions-wrap"
hx-swap="innerHTML"
onclick="document.querySelectorAll('.pill-cat').forEach(function(x){x.setAttribute('aria-pressed','false');})"
style="padding:3px 10px; border-radius:99px; font-size:12px; cursor:pointer;
border:1px solid var(--line); background:transparent; color:var(--muted);">
Toate
</button>
</div>
</div>
{% endif %}