feat(5.12): modal editare + cont obligatoriu la import; design.md + PRD 5.13 revizuit (/autoplan)
5.12 (livrat): editare in modal a randurilor de preview, cont obligatoriu inainte de import, formular editare extras (_form_editare, _editare_preview_modal), plus suita de teste aferenta (preview edit/compact, mapare op, form editare, signup, admin panel). Design + planificare: - docs/design.md: sistem de design (tokeni, breakpoints, scara control, componente, a11y). - docs/prd/prd-5.12-* si prd-5.13-* (5.13 cu raport /autoplan: CEO+Design+Eng, audit trail). Curatare: sterse PNG-urile de test/mockup temporare din radacina. Nota: implementarea CSS 5.13 (responsive compact + sistem butoane) NU e inca facuta — planul revizuit cere refactorul testelor fragile din test_web_responsive.py INAINTE de CSS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,18 @@
|
||||
<div id="acasa-section">
|
||||
|
||||
{# === Banner ne-blocant: cont incomplet (US-002) ===
|
||||
Apare cand accounts.name / email / CUI sunt necompletate (conturi legacy sau create din CLI).
|
||||
NU blocheaza importul sau uploadul — doar orienteaza operatorul sa completeze datele.
|
||||
Dispare automat dupa ce contul devine complet (re-render la urmatoarea navigare/reload).
|
||||
#}
|
||||
{% if cont_incomplet %}
|
||||
<div class="card banner" style="border-color:var(--warn); background:color-mix(in srgb, var(--warn) 12%, var(--card)); margin-bottom:14px; padding:10px 14px; font-size:13px;">
|
||||
<strong>Completeaza datele firmei (email / CUI).</strong>
|
||||
Contul tau nu are inca email de contact si CUI configurate.
|
||||
<a href="/?tab=cont" style="margin-left:6px;">Completeaza acum →</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# === Container colapsabil: stepper + upload intr-un singur element <details> (US-006).
|
||||
Serverul seteaza atributul `open` din are_trimiteri:
|
||||
are_trimiteri=False (first-run) → open (importul e vizibil imediat, fara JS)
|
||||
|
||||
@@ -1,6 +1,47 @@
|
||||
<div class="card" id="card-cont">
|
||||
<h2 style="font-size:15px; margin:0 0 16px;">Contul meu</h2>
|
||||
|
||||
<!-- Sectiunea: Date firma (US-002) -->
|
||||
<div style="margin-bottom:20px; padding-bottom:20px; border-bottom:1px solid var(--line);">
|
||||
<h3 style="font-size:13px; color:var(--muted); font-weight:500; margin:0 0 8px; text-transform:uppercase; letter-spacing:.04em;">Date firma</h3>
|
||||
|
||||
{% if date_firma_mesaj %}
|
||||
<div class="flash" style="margin-bottom:12px;">{{ date_firma_mesaj }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if date_firma_eroare %}
|
||||
<div class="banner" style="margin-bottom:12px; padding:8px 12px;">{{ date_firma_eroare }}</div>
|
||||
{% endif %}
|
||||
|
||||
<form hx-post="/cont/date-firma"
|
||||
hx-target="#card-cont"
|
||||
hx-swap="outerHTML">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<p style="margin:0 0 8px;">
|
||||
<label style="font-size:13px; color:var(--muted);">Companie</label><br>
|
||||
<input type="text" name="companie" required
|
||||
value="{{ account_meta.name or '' }}"
|
||||
style="width:100%; max-width:340px;"
|
||||
placeholder="Numele firmei (ex. Service Auto SRL)">
|
||||
</p>
|
||||
<p style="margin:0 0 8px;">
|
||||
<label style="font-size:13px; color:var(--muted);">Email contact</label><br>
|
||||
<input type="email" name="email" required
|
||||
value="{{ account_meta.email or '' }}"
|
||||
style="width:100%; max-width:340px;"
|
||||
placeholder="contact@firma.ro">
|
||||
</p>
|
||||
<p style="margin:0 0 12px;">
|
||||
<label style="font-size:13px; color:var(--muted);">CUI (cod unic de identificare)</label><br>
|
||||
<input type="text" name="cui" required
|
||||
value="{{ account_meta.cui or '' }}"
|
||||
style="width:100%; max-width:340px;"
|
||||
placeholder="RO12345678">
|
||||
</p>
|
||||
<button type="submit">Salveaza datele firmei</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Sectiunea: Cheia mea API -->
|
||||
<div style="margin-bottom:20px; padding-bottom:20px; border-bottom:1px solid var(--line);">
|
||||
<h3 style="font-size:13px; color:var(--muted); font-weight:500; margin:0 0 8px; text-transform:uppercase; letter-spacing:.04em;">Cheia mea API</h3>
|
||||
|
||||
78
app/web/templates/_editare_preview_modal.html
Normal file
78
app/web/templates/_editare_preview_modal.html
Normal file
@@ -0,0 +1,78 @@
|
||||
{# _editare_preview_modal.html — fragment de editare rand preview in modalul global.
|
||||
US-006 (PRD 5.12): swap-uit in #detaliu-modal-body de butonul Editeaza din preview.
|
||||
US-007 (PRD 5.12): butonul 'Confirma valorile' apare DOAR pe randurile needs_review
|
||||
(T2): trimite CSRF POST la /confirma-review, inchide modalul via HX-Trigger-After-Settle.
|
||||
|
||||
Necesita din context:
|
||||
import_id — id batch import
|
||||
row_index — index rand (0-based)
|
||||
csrf_token — token CSRF
|
||||
vin — VIN pentru titlu
|
||||
stare_css — clasa CSS pill (ex. "s-ok")
|
||||
stare_eticheta — text pill (ex. "Gata de trimis")
|
||||
message — mesaj de eroare general (None daca nu e)
|
||||
is_needs_review — True daca randul e in starea needs_review (afiseaza butonul Confirma)
|
||||
+ variabilele pentru _form_editare.html:
|
||||
form_nr, form_vin, form_data, form_odo_final, form_odo_initial
|
||||
err_map, fix_map, vin_context, btn_label
|
||||
#}
|
||||
<div class="card" style="border:none; padding:0; margin:0;">
|
||||
|
||||
{# Header cu heading accesibil (aria-labelledby al dialogului) #}
|
||||
<div style="display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin:0 0 12px;">
|
||||
<h2 id="detaliu-modal-titlu" style="font-size:15px; margin:0;">
|
||||
Editare rand {{ row_index + 1 }}
|
||||
{% if vin %}<span class="muted" style="font-weight:400; font-size:13px;">· {{ vin }}</span>{% endif %}
|
||||
</h2>
|
||||
<span class="pill {{ stare_css }}" style="font-size:11px;">{{ stare_eticheta }}</span>
|
||||
</div>
|
||||
|
||||
{% if message %}
|
||||
<div class="flash" style="border-color:var(--err); background:color-mix(in srgb, var(--err) 12%, var(--card)); margin-bottom:12px;"
|
||||
role="alert">{{ message }}</div>
|
||||
{% endif %}
|
||||
|
||||
<form hx-post="/_import/{{ import_id }}/rand/{{ row_index }}/editeaza"
|
||||
hx-target="#detaliu-modal-body"
|
||||
hx-swap="innerHTML"
|
||||
hx-disabled-elt="find button"
|
||||
hx-on::response-error="this.querySelector && this.querySelector('.rand-eroare-banner') && (this.querySelector('.rand-eroare-banner').style.display='block');">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token or '' }}">
|
||||
|
||||
<div class="rand-eroare-banner" role="alert"
|
||||
style="display:none; margin-bottom:10px; padding:8px 12px; border:1px solid var(--err);
|
||||
background:color-mix(in srgb, var(--err) 12%, var(--card)); border-radius:6px; font-size:13px;">
|
||||
Salvarea nu a reusit (retea / sesiune). Valorile introduse sunt pastrate — reincearca.
|
||||
</div>
|
||||
|
||||
{% include "_form_editare.html" %}
|
||||
|
||||
<div style="margin-top:10px; display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
|
||||
<button type="button"
|
||||
style="min-height:44px; padding:8px 18px;
|
||||
background:var(--card); color:var(--muted); border-color:var(--line);"
|
||||
data-modal-close>Anuleaza</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if is_needs_review %}
|
||||
{# T2 (US-007): Butonul 'Confirma valorile' apare DOAR pe randurile needs_review.
|
||||
POST separat (form propriu) la /confirma-review cu CSRF. Raspunsul inchide
|
||||
modalul via HX-Trigger-After-Settle: inchideModal + swap OOB randul si countorii. #}
|
||||
<form hx-post="/_import/{{ import_id }}/rand/{{ row_index }}/confirma-review"
|
||||
hx-target="#detaliu-modal-body"
|
||||
hx-swap="innerHTML"
|
||||
hx-disabled-elt="find button"
|
||||
style="margin-top:12px; border-top:1px solid var(--line); padding-top:12px;">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token or '' }}">
|
||||
<p class="muted" style="font-size:13px; margin:0 0 8px;">
|
||||
Valorile sunt corecte si doriesti sa includi acest rand la trimitere la RAR?
|
||||
</p>
|
||||
<button type="submit"
|
||||
style="min-height:44px; padding:8px 18px;
|
||||
background:var(--ok, #2a7); color:#fff; border-color:transparent;">
|
||||
Confirma valorile
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
41
app/web/templates/_form_editare.html
Normal file
41
app/web/templates/_form_editare.html
Normal file
@@ -0,0 +1,41 @@
|
||||
{# _form_editare.html — partial partajat: campurile vehicul/data/odometru.
|
||||
US-005 (PRD 5.12): extras DRY din _trimitere_detaliu.html; refolosit si de
|
||||
_preview_rand.html (US-006) pentru editarea randurilor de import in modal.
|
||||
|
||||
Inclus cu {% include "_form_editare.html" %} INSIDE un <form> element al
|
||||
template-ului parinte. Acel parinte pune form-ul, CSRF-ul si orice campuri
|
||||
suplimentare (ex. select cod_prestatie din _trimitere_detaliu.html).
|
||||
|
||||
Necesita din context (setate de parinte inainte de include):
|
||||
form_nr — valoare curenta nr_inmatriculare
|
||||
form_vin — valoare curenta vin
|
||||
form_data — valoare curenta data_prestatie (YYYY-MM-DD sau brut)
|
||||
form_odo_final — valoare curenta odometru_final
|
||||
form_odo_initial — valoare curenta odometru_initial
|
||||
err_map — dict {field_name: mesaj_eroare} (poate fi {})
|
||||
fix_map — dict {field_name: hint_fix} (poate fi {})
|
||||
vin_context — string VIN pentru aria-label (poate fi '')
|
||||
btn_label — eticheta butonului primar (ex. 'Salveaza si retrimite')
|
||||
#}
|
||||
{% from "_macros.html" import camp %}
|
||||
|
||||
{# Nr. inmatriculare pe rand propriu, VIN dedesubt — ambele latime plina. #}
|
||||
{{ camp('nr_inmatriculare', 'Numar inmatriculare', form_nr,
|
||||
err_map=err_map, fix_map=fix_map, vin_context=vin_context) }}
|
||||
{{ camp('vin', 'VIN (serie sasiu)', form_vin,
|
||||
err_map=err_map, fix_map=fix_map, vin_context=vin_context) }}
|
||||
|
||||
{# Restul campurilor in grila responsiva existenta. #}
|
||||
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:0 16px;">
|
||||
{{ camp('data_prestatie', 'Data prestatie', form_data, tip='date',
|
||||
err_map=err_map, fix_map=fix_map, vin_context=vin_context) }}
|
||||
{{ camp('odometru_final', 'Odometru final', form_odo_final,
|
||||
err_map=err_map, fix_map=fix_map, vin_context=vin_context) }}
|
||||
{{ camp('odometru_initial', 'Odometru initial (daca e cerut)', form_odo_initial,
|
||||
err_map=err_map, fix_map=fix_map, vin_context=vin_context) }}
|
||||
</div>
|
||||
|
||||
{# Buton primar parametrizat. #}
|
||||
<div style="margin-top:14px;">
|
||||
<button type="submit">{{ btn_label or 'Salveaza' }}</button>
|
||||
</div>
|
||||
@@ -4,3 +4,49 @@
|
||||
Simbolul pastrat (apelat in _mapari.html, _preview_import.html, _trimitere_detaliu.html)
|
||||
dar intoarce string gol. Coloanele DB raman (default 1, ne-citite pentru hold). #}
|
||||
{% macro autosend_toggle(form_id='', checked=True, label='') -%}{%- endmacro %}
|
||||
|
||||
{# US-005 (PRD 5.12): macro `camp` partajat — extras din _trimitere_detaliu.html si
|
||||
_preview_rand.html. Suporta tip='date' (calendar nativ, D#10/R3) si tip='text' (default).
|
||||
|
||||
Parametri:
|
||||
nome — name="" al input-ului (si cheie in err_map/fix_map)
|
||||
eticheta — text pentru label
|
||||
valoare — valoarea curenta (pre-fill)
|
||||
tip — type="" al input-ului: 'text' (default) sau 'date' (calendar nativ)
|
||||
err_map — dict {field_name: mesaj_eroare}; default {}
|
||||
fix_map — dict {field_name: hint_fix}; default {}
|
||||
vin_context — string VIN pentru aria-label cu context (default '')
|
||||
id_prefix — prefix pentru id="" al input-ului (default 'c'; preview poate folosi 'e-N')
|
||||
#}
|
||||
{% macro camp(nome, eticheta, valoare, tip='text', err_map={}, fix_map={}, vin_context='', id_prefix='c') %}
|
||||
<div style="margin-bottom:10px;">
|
||||
<label for="{{ id_prefix }}-{{ nome }}" class="muted" style="font-size:12px; display:block;">{{ eticheta }}</label>
|
||||
{% if tip == 'date' %}
|
||||
{# D#10/R3: degradare grijulie pentru valori ne-YYYY-MM-DD.
|
||||
Daca valoarea nu e in formatul corect, inputul ramane gol + hint + hidden cu valoarea bruta
|
||||
(ca sa nu se piarda tacut la submit). #}
|
||||
{%- set _dp_ok = (valoare and valoare|length == 10 and valoare[4:5] == '-' and valoare[7:8] == '-') -%}
|
||||
<input id="{{ id_prefix }}-{{ nome }}" type="date" name="{{ nome }}"
|
||||
value="{{ valoare if _dp_ok else '' }}"
|
||||
style="width:100%; {% if err_map.get(nome) %}border-color:var(--err);{% endif %}"
|
||||
aria-label="{{ eticheta }}{% if vin_context %} (VIN: {{ vin_context }}){% endif %}"
|
||||
{% if err_map.get(nome) %}aria-invalid="true"{% endif %}>
|
||||
{% if not _dp_ok and valoare %}
|
||||
<input type="hidden" name="data_prestatie_raw" value="{{ valoare }}">
|
||||
<span class="camp-fix" style="font-size:12px;">Valoarea originala: {{ valoare }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<input id="{{ id_prefix }}-{{ nome }}" type="{{ tip }}" name="{{ nome }}"
|
||||
value="{{ valoare or '' }}"
|
||||
style="width:100%; {% if err_map.get(nome) %}border-color:var(--err);{% endif %}"
|
||||
{% if vin_context %}aria-label="{{ eticheta }} (VIN: {{ vin_context }})"{% endif %}
|
||||
{% if err_map.get(nome) %}aria-invalid="true"{% endif %}>
|
||||
{% endif %}
|
||||
{% if err_map.get(nome) %}
|
||||
<div class="s-error" style="font-size:12px; margin-top:2px;">{{ err_map.get(nome) }}</div>
|
||||
{% endif %}
|
||||
{% if fix_map.get(nome) %}
|
||||
<span class="camp-fix" style="font-size:12px;">{{ fix_map.get(nome) }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<div id="import-section">
|
||||
{% set pas = 2 %}{% include '_stepper.html' %}
|
||||
{% from '_eroare.html' import card_erori %}
|
||||
{# prima_inregistrare poate veni din context (web_upload_import) sau derivat din sample_rows #}
|
||||
{%- set prima_inreg = prima_inregistrare if prima_inregistrare is defined else (sample_rows[0] if sample_rows else none) -%}
|
||||
<div class="card">
|
||||
<h2 style="font-size:15px; margin:0 0 12px;">
|
||||
Mapare coloane —
|
||||
@@ -23,6 +25,44 @@
|
||||
Maparea se retine automat pentru fisiere cu acelasi antet.
|
||||
</p>
|
||||
|
||||
{# Tabel orizontal preview: antet + prima inregistrare (US-003) #}
|
||||
<div class="tablewrap" style="margin-bottom:16px;">
|
||||
<table class="preview-antet" style="border-collapse:collapse; font-size:12px; width:100%; min-width:max-content;">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for col in columns %}
|
||||
<th style="padding:4px 10px; text-align:left; background:var(--card); border:1px solid var(--line);
|
||||
white-space:nowrap; font-weight:600; font-size:12px; color:var(--ink);">
|
||||
{{ col }}
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if prima_inreg %}
|
||||
<tr>
|
||||
{% for col in columns %}
|
||||
{%- set val = prima_inreg.get(col, '') | string -%}
|
||||
<td style="padding:4px 10px; border:1px solid var(--line); white-space:nowrap;
|
||||
font-size:11px; color:var(--muted); max-width:160px; overflow:hidden; text-overflow:ellipsis;"
|
||||
title="{{ val }}">
|
||||
{{ val[:40] }}{% if val | length > 40 %}…{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="{{ columns | length }}"
|
||||
style="padding:6px 10px; border:1px solid var(--line); font-size:12px;
|
||||
color:var(--muted); font-style:italic; text-align:center;">
|
||||
Antet fara randuri de date
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<form hx-post="/_import/{{ import_id }}/mapare-coloane"
|
||||
hx-target="#import-section"
|
||||
hx-swap="outerHTML">
|
||||
@@ -87,12 +127,19 @@
|
||||
|
||||
<div style="margin-top:16px; display:flex; align-items:center; gap:12px; flex-wrap:wrap;">
|
||||
<button type="submit"
|
||||
style="min-height:44px; padding:10px 24px; font-size:14px;">
|
||||
{% if not prima_inreg %}disabled aria-disabled="true"{% endif %}
|
||||
style="min-height:44px; padding:10px 24px; font-size:14px;{% if not prima_inreg %} opacity:0.5; cursor:not-allowed;{% endif %}">
|
||||
Salveaza si continua la preview
|
||||
</button>
|
||||
{% if not prima_inreg %}
|
||||
<span style="font-size:12px; color:var(--err);">
|
||||
Fisierul nu contine randuri de date — incarca un fisier cu cel putin o inregistrare.
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="muted" style="font-size:12px;">
|
||||
maparea se retine pentru fisiere cu acelasi antet
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -54,7 +54,8 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Panou inline: operatii fara cod RAR, mapabile in flux (fara re-upload) -->
|
||||
<!-- Panou inline: operatii fara cod RAR, mapabile in flux (fara re-upload).
|
||||
US-004: un singur <form> cu un select per operatie + un singur buton Salveaza. -->
|
||||
{% if unmapped_ops %}
|
||||
<div class="card" style="border-color:var(--err); background:color-mix(in srgb, var(--err) 12%, var(--card)); margin-bottom:14px;">
|
||||
<h3 style="font-size:14px; margin:0 0 6px;">Operatii de mapat la cod RAR</h3>
|
||||
@@ -63,51 +64,68 @@
|
||||
preselectata) si salveaza — randurile blocate trec automat in
|
||||
<span class="s-ok">ok</span> si maparea se retine pentru fisierele viitoare.
|
||||
</p>
|
||||
{% for e in unmapped_ops %}
|
||||
{%- set top = e.suggestions[0] if e.suggestions else None -%}
|
||||
{%- set preselect = top.cod_prestatie if (top and top.score >= 60) else '' -%}
|
||||
<form class="maprow" hx-post="/_import/{{ import_id }}/mapare-operatie"
|
||||
hx-target="#import-section" hx-swap="outerHTML"
|
||||
style="align-items:flex-end;">
|
||||
<form hx-post="/_import/{{ import_id }}/mapare-operatii"
|
||||
hx-target="#import-section" hx-swap="outerHTML">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token or '' }}">
|
||||
<input type="hidden" name="cod_op_service" value="{{ e.cod_op_service }}">
|
||||
<div class="mapcol grow">
|
||||
<div><strong>{{ e.cod_op_service }}</strong>
|
||||
<span class="pill" title="randuri blocate">{{ e.blocked }} randuri</span></div>
|
||||
{% if e.denumire and e.denumire != e.cod_op_service %}
|
||||
<div class="muted">{{ e.denumire }}</div>
|
||||
{% endif %}
|
||||
{% if e.suggestions %}
|
||||
<div class="muted" style="font-size:12px; margin-top:4px;">
|
||||
sugestii:
|
||||
{% for s in e.suggestions[:3] %}
|
||||
<span class="sugg">{{ s.cod_prestatie }} ({{ s.score|round|int }}%)</span>{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
{% for e in unmapped_ops %}
|
||||
{%- set top = e.suggestions[0] if e.suggestions else None -%}
|
||||
{%- set preselect = top.cod_prestatie if (top and top.score >= 60) else '' -%}
|
||||
<div class="maprow" style="align-items:flex-end; margin-bottom:10px;">
|
||||
<input type="hidden" name="cod_op_service" value="{{ e.cod_op_service }}">
|
||||
<div class="mapcol grow">
|
||||
<div><strong>{{ e.cod_op_service }}</strong>
|
||||
<span class="pill" title="randuri blocate">{{ e.blocked }} randuri</span></div>
|
||||
{% if e.denumire and e.denumire != e.cod_op_service %}
|
||||
<div class="muted">{{ e.denumire }}</div>
|
||||
{% endif %}
|
||||
{% if e.suggestions %}
|
||||
<div class="muted" style="font-size:12px; margin-top:4px;">
|
||||
sugestii:
|
||||
{% for s in e.suggestions[:3] %}
|
||||
<span class="sugg">{{ s.cod_prestatie }} ({{ s.score|round|int }}%)</span>{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mapcol">
|
||||
<select name="cod_prestatie" aria-label="Cod RAR pentru {{ e.cod_op_service }}">
|
||||
<option value="">— alege cod RAR —</option>
|
||||
{% for n in nomenclator %}
|
||||
<option value="{{ n.cod_prestatie }}" {% if n.cod_prestatie == preselect %}selected{% endif %}>
|
||||
{{ n.cod_prestatie }} — {{ n.nume_prestatie }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mapcol">
|
||||
<select name="cod_prestatie" required aria-label="Cod RAR pentru {{ e.cod_op_service }}">
|
||||
<option value="">— alege cod RAR —</option>
|
||||
{% for n in nomenclator %}
|
||||
<option value="{{ n.cod_prestatie }}" {% if n.cod_prestatie == preselect %}selected{% endif %}>
|
||||
{{ n.cod_prestatie }} — {{ n.nume_prestatie }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mapcol">
|
||||
<button type="submit" style="min-height:44px;">Salveaza</button>
|
||||
{% endfor %}
|
||||
<div style="margin-top:12px;">
|
||||
<button type="submit" style="min-height:44px;">Salveaza maparile</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Banner discoverability (T1, US-007): vizibil cand exista randuri needs_review.
|
||||
Explica operatorului ca randurile cu 'Verifica valori' nu pleaca la RAR
|
||||
pana le deschide in modal si apasa 'Confirma valorile'. Dispare via OOB
|
||||
cand summary.needs_review == 0. -->
|
||||
<div id="preview-needs-review-banner">
|
||||
{% if summary.get('needs_review', 0) %}
|
||||
<div class="banner warn" role="note" aria-live="polite"
|
||||
style="margin-bottom:12px; padding:8px 14px; border-radius:6px;
|
||||
background:color-mix(in srgb, var(--warn, #e6b34a) 12%, var(--card));
|
||||
border:1px solid var(--warn, #e6b34a); font-size:13px;">
|
||||
Randurile cu <span class="pill s-needs_review" style="font-size:11px;">Verifica valori</span>
|
||||
nu pleaca la RAR pana le deschizi in modal si confirmi in modal
|
||||
cu butonul <strong>Confirma valorile</strong>.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Tabel preview in format identic cu tabelul Trimiteri (.tabel-trimiteri).
|
||||
Randurile au FORM PROPRIU pentru editare (NU sunt in #confirm-form,
|
||||
altfel Enter intr-un camp ar declansa trimiterea ireversibila). Bifele
|
||||
needs_review se asociaza la #confirm-form prin atributul form=. -->
|
||||
US-007: 8 coloane (coloana de verificare eliminata).
|
||||
Randurile au FORM PROPRIU pentru editare (NU sunt in #confirm-form). -->
|
||||
<div class="tablewrap tabel-trimiteri">
|
||||
<table>
|
||||
<thead>
|
||||
@@ -119,7 +137,6 @@
|
||||
<th class="col-data">Data</th>
|
||||
<th class="col-km">KM final</th>
|
||||
<th class="col-note">Note</th>
|
||||
<th class="col-verificat">Verificat?</th>
|
||||
<th class="col-actiuni">Actiuni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -164,10 +181,7 @@
|
||||
style="max-width:80px;"
|
||||
aria-describedby="n-hint">
|
||||
<span id="n-hint" class="muted" style="font-size:12px; margin-left:6px;">
|
||||
(<span id="n-hint-ok">{{ summary.get('ok', 0) }}</span> ok
|
||||
{% if summary.get('needs_review', 0) %}
|
||||
+ pana la {{ summary.get('needs_review', 0) }} verificate manual
|
||||
{% endif %})
|
||||
(<span id="n-hint-ok">{{ summary.get('ok', 0) }}</span> ok)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -226,20 +240,19 @@
|
||||
return el ? parseInt(el.dataset.ok || '0', 10) : 0;
|
||||
}
|
||||
|
||||
/* Actualizeaza N si bannerul cand se bifeaza needs_review SAU cand se editeaza un rand. */
|
||||
/* Actualizeaza N dupa editare/confirmare rand (OOB).
|
||||
US-007: reviewed_rows (checkboxe) eliminate; N = randurile ok din DB,
|
||||
actualizate via OOB (#preview-ok-count[data-ok]) dupa /confirma-review sau /editeaza. */
|
||||
function updateN() {
|
||||
var checked = document.querySelectorAll('input[name="reviewed_rows"]:checked').length;
|
||||
var total = getOk() + checked;
|
||||
var total = getOk();
|
||||
var inp = document.getElementById('n-confirmat');
|
||||
var disp = document.getElementById('n-display');
|
||||
var btn = document.getElementById('confirm-btn');
|
||||
/* Nu re-activa confirm cat un rand e in editare (mutual-exclusion). */
|
||||
var editing = document.querySelector('tr[data-editing="1"]') !== null;
|
||||
if (inp) inp.value = total;
|
||||
if (disp) disp.textContent = total;
|
||||
var hintOk = document.getElementById('n-hint-ok');
|
||||
if (hintOk) hintOk.textContent = getOk();
|
||||
if (btn) btn.disabled = (total === 0) || editing;
|
||||
if (hintOk) hintOk.textContent = total;
|
||||
if (btn) btn.disabled = (total === 0);
|
||||
}
|
||||
|
||||
/* Filtrare randuri dupa stare.
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
{#
|
||||
_preview_rand.html — un singur rand de preview import.
|
||||
Doua moduri:
|
||||
- display (editing falsy): <tr> normal cu 9 coloane in format .tabel-trimiteri.
|
||||
- edit (editing truthy): <tr class="preview-edit"> (display:block) cu un singur
|
||||
<td> ce contine un FORM PROPRIU (NU #confirm-form). Escapa grila table-layout:fixed.
|
||||
Swap pe RAND (hx-target pe #preview-row-N, outerHTML), NU pe #import-section.
|
||||
La save, optional OOB pe rezumat + contor "gata de trimis" (include_oob).
|
||||
US-006 (PRD 5.12): editarea inline (tr.preview-edit + mutual-exclusion script)
|
||||
a fost eliminata. Butonul Editeaza deschide MODALUL global (#detaliu-modal-body).
|
||||
|
||||
Parametri:
|
||||
editing — ELIMINAT (ignorat, pastrat pentru compatibilitate apeluri vechi)
|
||||
include_oob — True: randeaza OOB rezumat + contor + script recalc (swap dupa save)
|
||||
oob_tr — True: adauga hx-swap-oob pe <tr> insusi (pentru raspunsul POST succes)
|
||||
summary — dict cu contoarele per status
|
||||
|
||||
Campuri pre-computate de _web_compute_preview (NOT din template raw):
|
||||
row.prez — prezentare_din_payload(resolved): vehicul_nr, vin_scurt,
|
||||
@@ -16,87 +18,10 @@
|
||||
#}
|
||||
{%- set res = row.resolved -%}
|
||||
{%- set status = row.resolved_status -%}
|
||||
{% if editing %}
|
||||
{%- set err_map = {} -%}
|
||||
{%- set fix_map = {} -%}
|
||||
{%- for e in row.errors -%}{%- if e is mapping and e.get('field') -%}{%- set _ = err_map.update({e.field: (e.get('message') or e.get('msg'))}) -%}{%- if e.get('fix') -%}{%- set _ = fix_map.update({e.field: e.get('fix')}) -%}{%- endif -%}{%- endif -%}{%- endfor -%}
|
||||
<tr id="preview-row-{{ row.row_index }}" data-status="{{ status }}" data-editing="1"
|
||||
class="preview-edit">
|
||||
<td data-eticheta="" style="padding:0; border:none;">
|
||||
<form class="rand-editare"
|
||||
hx-post="/_import/{{ import_id }}/rand/{{ row.row_index }}/editeaza"
|
||||
hx-target="#preview-row-{{ row.row_index }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-indicator="#rand-spinner-{{ row.row_index }}"
|
||||
hx-disabled-elt="find button"
|
||||
hx-on::response-error="this.querySelector('.rand-eroare-banner').style.display='block';"
|
||||
style="padding:12px; background:rgba(91,141,239,.06); border-radius:4px;">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token or '' }}">
|
||||
|
||||
<div style="display:flex; align-items:center; gap:8px; margin-bottom:8px;">
|
||||
<strong style="font-size:13px;">Editare rand {{ row.row_index + 1 }}</strong>
|
||||
<span class="pill {{ row.stare_css }}" style="font-size:11px;">{{ row.stare_eticheta }}</span>
|
||||
</div>
|
||||
|
||||
{% if message %}
|
||||
<div class="flash" style="border-color:var(--err); background:color-mix(in srgb, var(--err) 12%, var(--card)); margin-bottom:10px;"
|
||||
role="alert">{{ message }}</div>
|
||||
{% endif %}
|
||||
<div class="rand-eroare-banner" role="alert"
|
||||
style="display:none; margin-bottom:10px; padding:8px 12px; border:1px solid var(--err);
|
||||
background:color-mix(in srgb, var(--err) 12%, var(--card)); border-radius:6px; font-size:13px;">
|
||||
Salvarea nu a reusit (retea / sesiune). Valorile introduse sunt pastrate — reincearca.
|
||||
</div>
|
||||
|
||||
{% macro camp(nume, eticheta, valoare, tip='text') %}
|
||||
<div>
|
||||
<label for="e-{{ row.row_index }}-{{ nume }}" class="muted" style="font-size:12px; display:block;">{{ eticheta }}</label>
|
||||
<input id="e-{{ row.row_index }}-{{ nume }}" type="{{ tip }}" name="{{ nume }}" value="{{ valoare or '' }}"
|
||||
style="width:100%; {% if err_map.get(nume) %}border-color:var(--err);{% endif %}"
|
||||
aria-label="{{ eticheta }} — randul {{ row.row_index + 1 }} (VIN: {{ res.get('vin') or '' }})"
|
||||
{% if err_map.get(nume) %}aria-invalid="true"{% endif %}>
|
||||
{% if err_map.get(nume) %}
|
||||
<div class="s-error" style="font-size:12px; margin-top:2px;">{{ err_map.get(nume) }}</div>
|
||||
{% endif %}
|
||||
{% if fix_map.get(nume) %}
|
||||
<span class="camp-fix">{{ fix_map.get(nume) }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:10px 16px;">
|
||||
{{ camp('nr_inmatriculare', 'Numar inmatriculare', res.get('nr_inmatriculare')) }}
|
||||
{{ camp('vin', 'VIN (serie sasiu)', res.get('vin')) }}
|
||||
{{ camp('data_prestatie', 'Data prestatie (YYYY-MM-DD)', res.get('data_prestatie')) }}
|
||||
{{ camp('odometru_final', 'Odometru final', res.get('odometru_final')) }}
|
||||
{{ camp('odometru_initial', 'Odometru initial (daca e cerut)', res.get('odometru_initial')) }}
|
||||
</div>
|
||||
|
||||
<div style="margin-top:10px; display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
|
||||
<button type="submit" style="min-height:44px; padding:8px 18px;">Salveaza</button>
|
||||
<button type="button" style="min-height:44px; padding:8px 18px;
|
||||
background:var(--card); color:var(--muted); border-color:var(--line);"
|
||||
hx-get="/_import/{{ import_id }}/rand/{{ row.row_index }}"
|
||||
hx-target="#preview-row-{{ row.row_index }}" hx-swap="outerHTML">Anuleaza</button>
|
||||
<span id="rand-spinner-{{ row.row_index }}" class="htmx-indicator muted" style="font-size:13px;">
|
||||
se salveaza…
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<script>
|
||||
(function() {
|
||||
/* Mutual-exclusion: cat un rand e in editare, dezactiveaza confirm + alte Editeaza. */
|
||||
var btn = document.getElementById('confirm-btn');
|
||||
if (btn) { btn.disabled = true; btn.title = 'Termina editarea randului inainte de a trimite.'; }
|
||||
document.querySelectorAll('.btn-editeaza').forEach(function(b) { b.disabled = true; });
|
||||
})();
|
||||
</script>
|
||||
{% else %}
|
||||
{%- set disp_fix_map = {} -%}
|
||||
{%- for e in row.errors -%}{%- if e is mapping and e.get('field') and e.get('fix') -%}{%- set _ = disp_fix_map.update({e.field: e.get('fix')}) -%}{%- endif -%}{%- endfor -%}
|
||||
<tr id="preview-row-{{ row.row_index }}" data-status="{{ status }}"
|
||||
{% if oob_tr %}hx-swap-oob="outerHTML:#preview-row-{{ row.row_index }}"{% endif %}
|
||||
style="{% if status == 'needs_review' %}background:rgba(230,179,74,.04);{% elif status in ('already_sent', 'duplicate_in_file') %}opacity:.65;{% endif %}">
|
||||
<td class="col-id muted" data-eticheta="#">{{ row.row_index + 1 }}</td>
|
||||
<td class="col-stare" data-eticheta="Stare">
|
||||
@@ -105,7 +30,7 @@
|
||||
<td class="col-vehicul" data-eticheta="Vehicul">
|
||||
{{ row.prez.vehicul_nr }}
|
||||
{% if row.prez.vin_scurt and row.prez.vin_scurt != '—' %}
|
||||
<div class="muted" style="font-size:12px;">{{ row.prez.vin_scurt }}</div>
|
||||
<div class="muted" style="font-size:12px; white-space:nowrap;">{{ row.prez.vin_scurt }}</div>
|
||||
{% endif %}
|
||||
{# Fix-uri de validare pe vehicul #}
|
||||
{% if disp_fix_map.get('vin') %}<span class="camp-fix">{{ disp_fix_map.get('vin') }}</span>{% endif %}
|
||||
@@ -140,24 +65,13 @@
|
||||
{{ row.nota_umana or '' }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="col-verificat" data-eticheta="Verificat?" style="text-align:center;">
|
||||
{% if status == 'needs_review' %}
|
||||
<label class="chk" style="min-height:44px; justify-content:center; cursor:pointer;"
|
||||
title="Bifat inseamna ca ai verificat valorile si le incluzi in trimitere">
|
||||
<input type="checkbox" form="confirm-form" name="reviewed_rows" value="{{ row.row_index }}"
|
||||
onchange="window.updateN && window.updateN()"
|
||||
aria-label="Verificat — randul {{ row.row_index + 1 }} (VIN: {{ res.get('vin', '') }})">
|
||||
verif.
|
||||
</label>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="col-actiuni" data-eticheta="Actiuni" style="text-align:center;">
|
||||
{% if status not in ('already_sent', 'duplicate_in_file') %}
|
||||
<button type="button" class="btn-editeaza"
|
||||
style="min-height:44px; padding:6px 14px; font-size:13px;
|
||||
background:transparent; border-color:var(--line); color:var(--ink);"
|
||||
hx-get="/_import/{{ import_id }}/rand/{{ row.row_index }}/editare"
|
||||
hx-target="#preview-row-{{ row.row_index }}" hx-swap="outerHTML"
|
||||
hx-get="/_import/{{ import_id }}/rand/{{ row.row_index }}/editare-modal"
|
||||
hx-target="#detaliu-modal-body" hx-swap="innerHTML"
|
||||
aria-label="Editeaza randul {{ row.row_index + 1 }} (VIN: {{ res.get('vin', '') }})">
|
||||
Editeaza
|
||||
</button>
|
||||
@@ -165,7 +79,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
{% if include_oob %}
|
||||
{# OOB: actualizeaza rezumatul si contorul "gata de trimis" dupa save, fara a re-randa sectiunea. #}
|
||||
{# OOB: actualizeaza rezumatul, contorul, bannerul needs_review dupa save/confirma-review. #}
|
||||
{% set status_labels = [
|
||||
('ok','Gata de trimis'), ('needs_review','Verifica valori'), ('needs_mapping','Cod RAR lipsa'),
|
||||
('needs_data','Date incomplete'), ('already_sent','Deja trimis'), ('duplicate_in_file','Duplicat in fisier')] %}
|
||||
@@ -177,6 +91,19 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
<span id="preview-ok-count" hx-swap-oob="true" data-ok="{{ summary.get('ok', 0) }}" hidden></span>
|
||||
{# Banner discoverability: OOB swap dupa confirmare/editare → dispare cand needs_review==0. #}
|
||||
<div id="preview-needs-review-banner" hx-swap-oob="true">
|
||||
{% if summary.get('needs_review', 0) %}
|
||||
<div class="banner warn" role="note" aria-live="polite"
|
||||
style="margin-bottom:12px; padding:8px 14px; border-radius:6px;
|
||||
background:color-mix(in srgb, var(--warn, #e6b34a) 12%, var(--card));
|
||||
border:1px solid var(--warn, #e6b34a); font-size:13px;">
|
||||
Randurile cu <span class="pill s-needs_review" style="font-size:11px;">Verifica valori</span>
|
||||
nu pleaca la RAR pana le deschizi in modal si confirmi in modal
|
||||
cu butonul <strong>Confirma valorile</strong>.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
/* Editare incheiata: re-activeaza confirm + butoanele Editeaza, recalculeaza N.
|
||||
@@ -192,4 +119,3 @@
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -82,6 +82,12 @@
|
||||
{% if editabil %}
|
||||
{% set err_map = {} %}
|
||||
{% for e in corectie_errors %}{% if e.field %}{% set _ = err_map.update({e.field: e.message}) %}{% endif %}{% endfor %}
|
||||
{# fix_map gol pentru Trimiteri (fix-hints vin din preview, nu din corectii de trimitere). #}
|
||||
{% set fix_map = {} %}
|
||||
{# vin_context pentru aria-label cu context VIN (D#6). #}
|
||||
{%- set vin_context = form_vin -%}
|
||||
{# btn_label pentru butonul primar al partial-ului. #}
|
||||
{%- set btn_label = 'Salveaza si retrimite' -%}
|
||||
|
||||
{% if corectie_msg %}
|
||||
<div class="flash" style="{% if corectie_error %}border-color:var(--err); background:color-mix(in srgb, var(--err) 12%, var(--card));{% endif %} margin:0 0 12px;"
|
||||
@@ -90,30 +96,19 @@
|
||||
|
||||
{# Erori fara camp (field None) nu dispar silentios in editare —
|
||||
cardul 3n e ascuns, deci adaugam un rezumat simplu top-of-form.
|
||||
Erori cu camp raman afisate per-camp de macro-ul `camp` de mai jos. #}
|
||||
Erori cu camp raman afisate per-camp de macro-ul `camp` din _form_editare.html. #}
|
||||
{% for e in erori_3n if not e.field %}
|
||||
<div class="s-error" style="font-size:13px; margin:0 0 10px;" role="alert">{{ e.problema }}</div>
|
||||
{% endfor %}
|
||||
|
||||
{% macro camp(nume, eticheta, valoare, tip='text') %}
|
||||
<div style="margin-bottom:10px;">
|
||||
<label for="c-{{ nume }}" class="muted" style="font-size:12px; display:block;">{{ eticheta }}</label>
|
||||
<input id="c-{{ nume }}" type="{{ tip }}" name="{{ nume }}" value="{{ valoare }}"
|
||||
style="width:100%; {% if err_map.get(nume) %}border-color:var(--err);{% endif %}"
|
||||
{% if err_map.get(nume) %}aria-invalid="true"{% endif %}>
|
||||
{% if err_map.get(nume) %}
|
||||
<div class="s-error" style="font-size:12px; margin-top:2px;">{{ err_map.get(nume) }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
<form hx-post="/trimitere/{{ id }}/corecteaza"
|
||||
hx-target="#detaliu-modal-body" hx-swap="innerHTML"
|
||||
hx-disabled-elt="find button">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
|
||||
{# Select cod RAR pe stari editabile (needs_data/needs_mapping), cu nomenclator.
|
||||
Read-only pe sent/sending/queued/error (nomenclator_rar gol → ramura else). #}
|
||||
Read-only pe sent/sending/queued/error (nomenclator_rar gol → ramura else).
|
||||
RAMANE in _trimitere_detaliu.html (D#5 — logica specifica acestui modal). #}
|
||||
{% if nomenclator_rar %}
|
||||
<div style="margin:0 0 12px;">
|
||||
<label for="c-cod-prestatie" class="muted" style="font-size:12px; display:block;">Operatie RAR (cod prestatie)</label>
|
||||
@@ -139,7 +134,8 @@
|
||||
{% endif %}
|
||||
|
||||
{# Operatie service (cod intern + denumire venita prin API/import), distinct de
|
||||
operatia RAR mapata. op_service_cod="" cand lipseste → randul absent. #}
|
||||
operatia RAR mapata. op_service_cod="" cand lipseste → randul absent.
|
||||
RAMANE in _trimitere_detaliu.html (D#5). #}
|
||||
{% if prez.op_service_cod %}
|
||||
<div style="margin:0 0 12px;">
|
||||
<div class="muted" style="font-size:12px;">Operatie service</div>
|
||||
@@ -147,22 +143,8 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Nr. inmatriculare pe rand propriu, VIN dedesubt — ambele latime plina. #}
|
||||
{{ camp('nr_inmatriculare', 'Numar inmatriculare', form_nr) }}
|
||||
{{ camp('vin', 'VIN (serie sasiu)', form_vin) }}
|
||||
|
||||
{# Restul campurilor in grila. #}
|
||||
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:0 16px;">
|
||||
{{ camp('data_prestatie', 'Data prestatie (YYYY-MM-DD)', form_data) }}
|
||||
{{ camp('odometru_final', 'Odometru final', form_odo_final) }}
|
||||
{{ camp('odometru_initial', 'Odometru initial (daca e cerut)', form_odo_initial) }}
|
||||
</div>
|
||||
|
||||
{# === Actiune primara conditionata de stare. needs_data/needs_mapping
|
||||
-> „Salveaza si retrimite" pe /corecteaza. UN SINGUR buton primar per stare. === #}
|
||||
<div style="margin-top:14px;">
|
||||
<button type="submit">Salveaza si retrimite</button>
|
||||
</div>
|
||||
{# === Campurile vehicul/data/odo + erori/fix + buton — partial DRY (US-005). === #}
|
||||
{% include "_form_editare.html" %}
|
||||
</form>
|
||||
{% else %}
|
||||
{# Context read-only pentru randuri ne-editabile (sent/sending/queued/error). #}
|
||||
|
||||
@@ -60,7 +60,13 @@
|
||||
{% if v == 'delete' %}onsubmit="return confirm('Stergi acest cont? (stergere soft)');"{% endif %}>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<input type="hidden" name="account_id" value="{{ acct.id }}">
|
||||
{% if v == 'activate' and not acct.is_complete %}
|
||||
<button type="submit"{% if cls == 'danger' %} class="danger"{% endif %}
|
||||
disabled
|
||||
title="Completeaza datele firmei (companie + email + CUI) inainte de activare">{{ label }}</button>
|
||||
{% else %}
|
||||
<button type="submit"{% if cls == 'danger' %} class="danger"{% endif %}>{{ label }}</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -345,15 +345,28 @@
|
||||
@media (max-width:1024px) {
|
||||
.tabel-trimiteri .col-actualizat { display:none; }
|
||||
}
|
||||
/* Tableta (768–1024px): header compact fara suprapuneri.
|
||||
Grila 3-coloane se pastreaza; logo si titlu mai mici; versiunea ascunsa
|
||||
(informatia secundara elibereaza spatiu in celula dreapta: comutator tema +
|
||||
hamburger raman vizibili). min-height:92px din regula de baza e resetat —
|
||||
inaltimea header-ului e determinata de continut, nu de un prag fix. */
|
||||
@media (min-width:768px) and (max-width:1024px) {
|
||||
header { min-height:0; padding:10px 16px; gap:6px; }
|
||||
.brand-logo { height:44px; }
|
||||
header h1 { font-size:16px; }
|
||||
/* Versiunea (ex. "v0.9.3") este informatie secundara pe tableta:
|
||||
ascunsa pentru a elibera spatiu in celula dreapta. */
|
||||
.header-right > .muted { display:none; }
|
||||
}
|
||||
/* === Preview import: coloane extra fata de tabelul Trimiteri.
|
||||
SCOPAT prin .tabel-trimiteri (clasa partajata). Regiune separata —
|
||||
nu atinge coloanele existente (col-chk/id/stare/data/rar/actualizat).
|
||||
US-007: 8 coloane (coloana de verificare manuala eliminata).
|
||||
Suma latimi fixe: col-id(48) + col-stare(104) + col-data(104) +
|
||||
col-km(76) + col-note(176) + col-verificat(80) + col-actiuni(92) = 680px.
|
||||
Restul (~600px la 1280px) revine lui col-vehicul + col-operatie (fluid). === */
|
||||
col-km(76) + col-note(176) + col-actiuni(92) = 600px.
|
||||
Restul (~680px la 1280px) revine lui col-vehicul + col-operatie (fluid). === */
|
||||
.tabel-trimiteri .col-km { width:76px; }
|
||||
.tabel-trimiteri .col-note { width:176px; }
|
||||
.tabel-trimiteri .col-verificat{ width:80px; }
|
||||
.tabel-trimiteri .col-actiuni { width:92px; }
|
||||
/* Randul de editare inline iese din grila table-layout:fixed (display:block),
|
||||
astfel formularul nu e constrans de latimile coloanelor individuale.
|
||||
@@ -814,9 +827,12 @@
|
||||
|
||||
// Deschidere la click pe rand (htmx:beforeRequest): arata modalul cu placeholder
|
||||
// inainte ca raspunsul fragmentului sa fie swap-uit in corp.
|
||||
// Trateaza atat .trimitere-row (Trimiteri) cat si .btn-editeaza (preview import)
|
||||
// → open() instaleaza inert pe <main>, focus-trap si readuce focusul la inchidere (US-006).
|
||||
document.body.addEventListener('htmx:beforeRequest', function(evt) {
|
||||
var elt = evt.detail && evt.detail.elt;
|
||||
if (elt && elt.classList && elt.classList.contains('trimitere-row')) open(elt);
|
||||
if (!elt || !elt.classList) return;
|
||||
if (elt.classList.contains('trimitere-row') || elt.classList.contains('btn-editeaza')) open(elt);
|
||||
});
|
||||
// Dupa swap-ul fragmentului (sau re-render corectie/mapare): muta focusul in modal.
|
||||
body.addEventListener('htmx:afterSettle', function() {
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
<input type="text" name="name" value="{{ name or '' }}" required style="width:100%;">
|
||||
</p>
|
||||
<p>
|
||||
<label>CUI <span style="color:var(--muted);font-size:12px;">(optional)</span></label><br>
|
||||
<input type="text" name="cui" value="{{ cui or '' }}" style="width:100%;">
|
||||
<label>CUI <span style="color:var(--err)">*</span></label><br>
|
||||
<input type="text" name="cui" value="{{ cui or '' }}" required style="width:100%;">
|
||||
</p>
|
||||
<p>
|
||||
<label>Email <span style="color:var(--err)">*</span></label><br>
|
||||
|
||||
Reference in New Issue
Block a user