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:
Claude Agent
2026-06-27 18:52:20 +00:00
parent 283299ff20
commit b26dbb79e1
44 changed files with 4852 additions and 305 deletions

View File

@@ -130,6 +130,7 @@ def _resolve_row_for_preview(
override: dict[str, Any] | None = None,
valid_codes: set[str] | None = None,
text_rules: list[dict] | None = None,
reviewed: bool = False,
) -> dict[str, Any]:
"""Rezolva un rand din import pentru preview: aplica mapare coloane + validare.
@@ -220,8 +221,10 @@ def _resolve_row_for_preview(
# Validare continut
errors = validate_prezentare(mapped)
if all_flags:
# needs_review: validarea a trecut, dar flagurile (date ambigue, formule) cer confirmare manuala
if all_flags and not reviewed:
# needs_review: validarea a trecut, dar flagurile (date ambigue, formule) cer confirmare manuala.
# Daca reviewed=True (operatorul a confirmat explicit valorile in modal), sarim
# acest return si continuam spre ok/needs_data (US-007, PRD 5.12).
return {
"resolved_status": "needs_review",
"resolved": mapped,
@@ -337,7 +340,10 @@ def apply_row_override(
new_override = _merge_override(current, fields)
enc = encrypt_creds(new_override) if new_override else None
conn.execute("UPDATE import_rows SET override_json=? WHERE id=?", (enc, row["rid"]))
# D#9 (PRD 5.12): resetam reviewed=0 la orice schimbare de valoare — operatorul
# trebuie sa reconfirme dupa editare. NU conditionam pe reviewed curent: orice override
# (chiar si revert la valoarea initiala) anuleaza confirmarea implicita.
conn.execute("UPDATE import_rows SET override_json=?, reviewed=0 WHERE id=?", (enc, row["rid"]))
return new_override
@@ -932,10 +938,30 @@ def commit_import(
if batch["status"] == "committed":
raise HTTPException(status_code=409, detail="batch deja comis")
# Incarca randurile cu stare ok sau needs_review
# D#8 (PRD 5.12): gate commit derivat din DB `reviewed` pe AMBELE canale.
# API: reviewed_rows pastrat (contract stabil) dar seteaza reviewed=1 in DB inainte
# de interogare. Randurile needs_review cu reviewed=1 sunt incluse in comit.
if req.reviewed_rows:
conn.execute("BEGIN IMMEDIATE")
try:
for idx in req.reviewed_rows:
conn.execute(
"UPDATE import_rows SET reviewed=1 "
"WHERE batch_id=? AND row_index=? AND resolved_status='needs_review'",
(import_id, idx),
)
conn.execute("COMMIT")
except Exception:
conn.execute("ROLLBACK")
raise
# Incarca randurile ok + needs_review confirmate (reviewed=1)
ok_rows_db = conn.execute(
"SELECT row_index, raw_json, override_json, resolved_status FROM import_rows "
"WHERE batch_id=? AND resolved_status IN ('ok', 'needs_review') ORDER BY row_index",
"SELECT row_index, raw_json, override_json, resolved_status, reviewed "
"FROM import_rows "
"WHERE batch_id=? AND (resolved_status='ok' OR "
"(resolved_status='needs_review' AND reviewed=1)) "
"ORDER BY row_index",
(import_id,),
).fetchall()
@@ -947,8 +973,6 @@ def commit_import(
# Decripteaza randurile ok
ok_rows: list[dict] = []
ok_indices: list[int] = []
review_indices: set[int] = set()
for r in ok_rows_db:
try:
@@ -957,28 +981,12 @@ def commit_import(
continue
except Exception:
continue
if r["resolved_status"] == "ok":
ok_rows.append({"row_index": r["row_index"], "data": row_data,
"override": _override_of(r), "status": "ok"})
ok_indices.append(r["row_index"])
elif r["resolved_status"] == "needs_review":
review_indices.add(r["row_index"])
# needs_review bifate explicit (atestare pe valori)
confirmed_review = [idx for idx in req.reviewed_rows if idx in review_indices]
for idx in confirmed_review:
# Gaseste randul needs_review si il adauga la ok_rows
for r in ok_rows_db:
if r["row_index"] == idx and r["resolved_status"] == "needs_review":
try:
row_data = decrypt_creds(r["raw_json"])
if row_data:
ok_rows.append({"row_index": idx, "data": row_data,
"override": _override_of(r), "status": "needs_review"})
ok_indices.append(idx)
except Exception:
pass
ok_rows.append({
"row_index": r["row_index"],
"data": row_data,
"override": _override_of(r),
"status": r["resolved_status"],
})
# Gate HARD: n_confirmat trebuie sa fie EXACT egal cu numarul de randuri ok
n_total_ok = len(ok_rows)