docs: Etapa 5 (ergonomie & integrare) + PRD 5.1 hub integrare aprobat

Directie noua: prioritate pe usurinta de integrare/ergonomie peste Etapa 4
(deprioritizata). ROADMAP: Etapa 5 cu 4 livrabile (5.1 hub integrare, 5.2
dry-run, 5.3 light/dark, 5.4 erori 3 niveluri).

PRD 5.1 (hub /integrare: exemple multi-limbaj + retetar VFP MSXML2/WinHttp +
GET /v1/ping readiness + export Postman/OpenAPI + test conexiune): 4 stories
atomice in 2 valuri, fundamentat cu lentila DX gstack pe codul real. 3 review-uri
de plan rulate (CEO/eng/design, toate APROB CU MODIFICARI) si incorporate.
Stare: aprobat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-06-22 11:16:44 +00:00
parent 35f35d03cc
commit be36c2c53b
2 changed files with 272 additions and 2 deletions

View File

@@ -86,11 +86,26 @@ Reguli de contract (detalii in `docs/api-rar-contract.md`): `FINALIZATA` e termi
| 3.5 | Dashboard compact: import pe prima pagina, status cu bife, Trimiteri lizibile, Mapari complete | DONE | 2026-06-19 | 11 stories (4 valuri), 3 review-uri de plan facute. Acasa=ecran de import (scoate tab Import); bara status compacta font normal + bife accesibile (auto-send/RAR) + data `dd.mm.yyyy hh24:mi:ss`; "Coada"→"Trimiteri" cu coloane RO + detalii comanda din `payload_json` (helper partajat `payload_view.py`) + detaliu la click in panou dedicat; filtrare Trimiteri (US-009); corectie inline `needs_data` cu re-enqueue + detectie coliziune idempotency (US-010); badge contoare pe tab-uri (US-011); "Mapari" 3 sectiuni (de rezolvat / op salvate cu re-rezolvare auto / formate coloane), "Cont"=doar cheie+creds; feedback `needs_data` la import. Backend trimitere neatins. PRD: [prd-3.5](prd/prd-3.5-dashboard-compact-trimiteri-mapari.md) |
| 3.6 | Editare celule in preview + Acasa unificata (Trimiteri inline, upload slim, Mapari tabelar, comutator coada) | DONE | 2026-06-22 | 7 stories (3 valuri), 2 echipe in paralel (TeamCreate) pe fisiere disjuncte + US-007 secvential. US-003/004 tab "Trimiteri" eliminat→sectiune "Trimiterile tale" sub upload pe Acasa; upload bara slim (hero la first-run); `?tab=coada`+`/_fragments/coada`→Acasa; poll gated visibilityState. US-001 `import_rows.override_json` (Approach B, Fernet, `_migrate` defensiv) aplicat ULTIMUL in `_resolve_row_for_preview`+`commit_import` (mutatie pura; completeaza si coloane ABSENTE din fisier); ruta `.../rand/{i}/editeaza` scoped JOIN→404, guard committed→409, empty=clear. US-002 buton Editeaza pe rand, swap pe `<tr>`+OOB contoare (nu pe sectiune), form propriu (confirm dezactivat la editare), mutual-exclusion, reuse grila `_trimitere_detaliu.html`. US-005/006 Mapari/formate tabelar (.tablewrap, H4 auto_send stocat). US-007 bifa auto-send→comutator pe COADA ("Pune automat in coada"/"Tine pentru verificare"), scoped operatie, `name=auto_send` pastrat (zero backend). 523 teste pass. VERIFY E2E browser + LIVE RAR test: import fara coloana data→editare completeaza data (override)→commit→worker login RAR test→`postPrezentare`→sent `idPrezentare=68696` (confirmat in finalizate RAR). 3 bug-uri JS prinse la E2E (invizibile la TestClient) reparate: htmx `useTemplateFragments` (raspuns `<tr>`+OOB parsat in context tabel→swapError), re-activare confirm-btn deferita pe tick, `n-hint` actualizat. `/code-review` high (2026-06-22): 1 bug real reparat — decriptarea `override_json` era in afara `try/except`-ului care protejeaza `raw_json` in ambele cai de preview (`import_router.preview_import` + `routes._web_compute_preview`); la rotatie cheie Fernet / token corupt `raw_json` degrada gratios la `{}` dar `override_json` arunca 500 pe tot batch-ul — acum protejat identic. Notat ca cleanup viitor (nereparat, disciplina backend-neatins): `_override_of` + blocul canonicalize-dupa-override duplicate in 3-4 locuri (preview/commit API vs web). Backend trimitere (worker/masina stari/idempotenta/mapare) NEATINS. PRD: [prd-3.6](prd/prd-3.6-editare-preview-acasa-unificata.md) |
### Etapa 4Viitor (Treapta 3)
### Etapa 5Ergonomie & Integrare (FOCUS CURENT)
> Directie noua (2026-06-22, decizie utilizator): produsul e functional, dar greu de adoptat de
> service-urile care vin din Visual FoxPro / soft propriu. Prioritatea trece pe **usurinta de
> integrare + ergonomie**, peste Etapa 4 (vezi nota de mai jos). Propunere fundamentata cu lentila
> DX gstack (`/devex-review`) pe codul real: lipseste suprafata de onboarding API self-service,
> exemplele de cod, puntea VFP, dry-run-ul si tema light.
| # | Livrabila | Status | Data | Detalii |
|---|-----------|--------|------|---------|
| 4.1 | Mapare AI / conector MCP (sugestie peste fuzzy) | TODO | | |
| 5.1 | Hub de integrare (pagina `/integrare` autentificata): exemple cod multi-limbaj (curl/Python/PHP/C#/Node) + retetar **Visual FoxPro** (POST via `MSXML2.ServerXMLHTTP` + upload CSV) + export OpenAPI/Postman + buton "Testeaza conexiunea" | TODO | | cel mai mare wedge de adoptie; absoarbe optiunea "doar exemple markdown" |
| 5.2 | Endpoint dry-run `POST /v1/prezentari/valideaza` — valideaza payload + mapare, intoarce erorile reale FARA enqueue | TODO | | "magical moment" pt integratori; refoloseste `validation.py`+`mapping.py`, NU atinge coada |
| 5.3 | Light/Dark mode — toggle in header, persistat (cookie/localStorage); CSS deja pe variabile `:root` | TODO | | efort mic, cerut explicit |
| 5.4 | Erori pe 3 niveluri (problema + cauza + fix) pe API si UI | TODO | | DX: fight uncertainty; reduce suportul |
### Etapa 4 — Deprioritizat (post Etapa 5, daca apare nevoia din uz real)
| # | Livrabila | Status | Data | Detalii |
|---|-----------|--------|------|---------|
| 4.1 | Mapare AI / conector MCP (sugestie peste fuzzy) | AMANAT | | deprioritizat 2026-06-22 in favoarea ergonomiei/integrarii (Etapa 5) |
| 4.2 | Editare/anulare prezentari trimise | BLOCAT | | `FINALIZATA` terminal la RAR — fara flux API; corectia = suport RAR |
### Amanat / taiat (cu motiv)

View File

@@ -0,0 +1,255 @@
# PRD 5.1 — Hub de integrare (pagina /integrare cu exemple de cod + retetar VFP + ping + export)
**Stare**: aprobat (2026-06-22 — intrebari deschise rezolvate; 3 review-uri de plan rulate si incorporate (CEO/eng/design, toate APROB CU MODIFICARI); aprobat la poarta umana. Urmeaza EXECUTE)
> Proces complet: `docs/ROADMAP.md` §5. Contract RAR (sursa de adevar): `docs/api-rar-contract.md`.
> Starea trece: `draft → aprobat → in-executie → verify-pass → inchis` (actualizata de lead).
> **Backend trimitere (worker, masina stari, idempotenta-logica, mapping-rezolvare) NU se atinge.**
> Aceasta livrabila adauga DOAR suprafata de onboarding/integrare (citire) peste API-ul existent + stratul web.
> Schema neatinsa (zero coloane noi).
## 1. Obiectiv
Produsul e functional pe ambele canale (API + import web), dar un service nou care vine din **Visual FoxPro**
sau soft propriu nu are de unde sa inceapa integrarea: cheia API se creeaza/roteste din web (`/cont`), insa nu
exista nicio pagina cu **cum chem API-ul**, ce payload, ce header, in ce limbaj. Singurele referinte sunt
`README.md` (curl) si Swagger `/docs` — invizibile din aplicatie si fara exemple in limbajul integratorului.
Livram o pagina **`/integrare`** (tab nou, autentificat, scoped pe contul din sesiune) care:
- afiseaza exemple de cod **pre-completate cu endpoint-ul real + account_id-ul utilizatorului** pentru ambele
canale (`POST /v1/prezentari` JSON si `POST /v1/import` upload fisier), in **curl, Python, PHP, C#, Node**;
- include un **retetar Visual FoxPro** (POST via `MSXML2.ServerXMLHTTP.6.0` SI `WinHttp.WinHttpRequest.5.1`,
plus upload CSV);
- ofera **export OpenAPI** (`/openapi.json` din FastAPI) + **colectie Postman** + link Swagger `/docs`;
- are un buton **"Testeaza conexiunea"**: utilizatorul lipeste cheia salvata, iar serverul confirma ca e valida
si mapeaza pe contul lui (acelasi drum de auth ca API-ul real).
Constrangere de securitate (din `app/auth.py`): stocam DOAR SHA-256 al cheii API, deci **nu putem reafisa cheia**.
Exemplele folosesc placeholder `rfak_...`; cheia reala se obtine o singura data la creare/rotire din `/cont`.
## 2. Non-Goals (anti scope-creep)
- **Nu reafisam cheia API** — exemplele au placeholder, niciodata cheia in clar (SHA-256-only).
- **Fara markdown versionat in `docs/`** (decis cu utilizatorul: doar pagina in-app dinamica). Daca apare nevoia
de referinta indexabila, e o livrabila viitoare separata.
- **Fara generare automata de SDK-uri** — OpenAPI + Postman sunt punctul de plecare; integratorul genereaza singur.
- **Fara dry-run** — validarea de payload fara enqueue e livrabila 5.2 (separata). `GET /v1/ping` verifica DOAR
autentificarea (cheie valida + cont activ), nu valideaza continut.
- **Fara editarea exemplelor de catre user** — continut read-only generat din schema.
- **Nu atinge backend-ul de trimitere** (worker/masina stari/idempotenta-logica/mapping-rezolvare) si **nici schema**.
- **Fara i18n** — pagina e in romana, ca restul UI-ului.
## 3. Stories atomice
> Backend + UI pentru acelasi comportament = stories separate. Toate rutele web noi sub `require_login` +
> **scoped pe contul din sesiune**. CSRF pe toate POST-urile web. Endpoint-urile `/v1/*` folosesc `resolve_account_id`.
### US-001: Backend — endpoint-uri de integrare (`GET /v1/ping` + export Postman)
**Ca** integrator extern **vreau** un endpoint care confirma ca cheia mea functioneaza si o colectie Postman gata
de importat **pentru ca** sa-mi validez conexiunea inainte sa trimit prezentari reale.
- **Depinde de**: —
- **Fisiere**: `app/api/v1/integrare_router.py` (NOU), montare in `app/main.py` (`app.include_router`),
`tests/test_integrare_api.py` (~3 fisiere)
- **Montare (decizie eng-review P1)**: router nou cu `prefix="/v1"`, inclus in `app/main.py` cu `app.include_router(...)`
(consistent cu `import_v1_router`). NU sub-include in `app/api/v1/router.py` (acela are deja `prefix="/v1"` → ar dubla prefixul).
- **Test intai (RED)**: `tests/test_integrare_api.py`
`test_ping_cu_cheie_valida_200` (raspuns: `account_id`, `mediu` test/prod, `autentificat_cu_cheie=true`, `are_creds_rar`, `ts`),
`test_ping_cu_bearer_valid_200` (`Authorization: Bearer rfak_...`),
`test_ping_fara_cheie_dev_cont_implicit` (flag off → cont 1, `autentificat_cu_cheie=false`),
`test_ping_x_api_key_gol_in_dev_cont_implicit` (whitespace tratat ca lipsa),
`test_ping_are_creds_rar_reflecta_contul` (cont cu `accounts.rar_creds_enc``true`; fara → `false`),
`test_ping_cheie_invalida_401`,
`test_ping_prod_fara_cheie_401` (flag on),
`test_ruta_ping_inregistrata_o_singura_data` (assert pe `app.routes` — fara dubla inregistrare),
`test_postman_export_json_valid` (Content-Type JSON, parseaza, schema Postman v2.1.0),
`test_postman_contine_exact_trei_requesturi` (prezentari, import, ping; header `X-API-Key: {{api_key}}`; `{{base_url}}`),
`test_postman_nu_deriva_din_app_routes` (allowlist hardcodat — NU expune `/v1/conturi/rar-creds` etc.).
- **Acceptance criteria**:
- [ ] `GET /v1/ping` (dependinta `resolve_account_id`) = **readiness check**, nu doar auth-echo: cheie valida → 200
`{account_id, mediu, autentificat_cu_cheie, are_creds_rar, ts}`; cheie invalida → 401; fara cheie cu
`require_api_key=true` → 401; fara cheie in dev → cont 1 cu `autentificat_cu_cheie=false`. (`are_creds_rar` = contul are
`accounts.rar_creds_enc` setate; citire-only, raspunde la "esti gata sa trimiti?", nu doar "cheia merge". CEO-review P1/P2.)
- [ ] Suporta si `Authorization: Bearer rfak_...` (al doilea header documentat in pagina).
- [ ] `GET /v1/integrare/postman.json`: colectie Postman v2.1.0 cu variabile `base_url` + `api_key`, **exact 3 requesturi**
(allowlist hardcodat, NU derivat din `app.routes`): `POST /v1/prezentari`, `POST /v1/import`, `GET /v1/ping`, fiecare cu
header `X-API-Key: {{api_key}}` si body exemplu (pt prezentari: payload din schema reala `PrezentareRequest`, inclusiv `prestatii`).
- [ ] `/openapi.json` ramane accesibil (FastAPI implicit) — niciun regres.
- [ ] NU atinge `submissions`, worker, schema. `are_creds_rar` = SELECT read-only pe `accounts`.
- **Verificare E2E**: `curl -H "X-API-Key: rfak_..." http://localhost:8010/v1/ping` pe instanta locala → 200 cu contul corect +
`are_creds_rar`; import `postman.json` in Postman/Swagger.
### US-002: Helper pur — generator de exemple de cod multi-limbaj
**Ca** dezvoltator al gateway-ului **vreau** un modul pur care produce snippet-urile de integrare dintr-un
`(base_url, account_id)` **pentru ca** pagina sa fie testabila si snippet-urile sa ramana langa schema reala.
- **Depinde de**: —
- **Fisiere**: `app/web/integrare_examples.py` (NOU), `tests/test_integrare_examples.py` (~2 fisiere)
- **Test intai (RED)**: `tests/test_integrare_examples.py`
`test_snippet_curl_prezentari_contine_endpoint_si_header` (`{base_url}/v1/prezentari`, `X-API-Key: rfak_...`),
`test_snippet_python_import_upload_multipart` (foloseste `files=` / multipart pe `/v1/import`),
`test_toate_limbajele_prezente` (curl, python, php, csharp, node, vfp_msxml, vfp_winhttp),
`test_ambele_canale_per_limbaj` (fiecare limbaj are si prezentari JSON, si upload fisier),
`test_vfp_msxml_si_winhttp_distincte` (MSXML2.ServerXMLHTTP.6.0 vs WinHttp.WinHttpRequest.5.1),
`test_payload_acopera_campurile_obligatorii_din_model` (snippet-ul prezentari contine TOATE campurile obligatorii
derivate prin `field.is_required()` din `PrezentareIn` — INCLUSIV `prestatii` cu ≥1 item — si `RarCredentials{email,password}`;
drift-test corect: `{c for c,f in PrezentareIn.model_fields.items() if f.is_required()}` ⊆ campurile din snippet; campurile cu
default — `odometru_initial`, `obs`, `b64_image`, `sistem_reparat` — NU dau drift fals),
`test_prestatii_in_snippet_are_cod` (item-ul `prestatii` are `cod_prestatie` SAU `cod_op_service` — vezi `PrestatieItem._require_one`),
`test_placeholder_cheie_nu_e_valoare_reala` (mereu `rfak_...`, niciodata o cheie injectata).
- **Acceptance criteria**:
- [ ] Functie pura `exemple(base_url: str, account_id: int) -> dict` care intoarce, per limbaj, snippet pt ambele canale.
- [ ] Placeholder cheie = `rfak_...` (constant), endpoint-ul derivat din `base_url`.
- [ ] Payload-ul JSON reflecta schema reala (`PrezentareRequest`): `rar_credentials{email,password}` + `prezentari[]` cu
**toate** campurile obligatorii prin `is_required()`, **inclusiv `prestatii` cu cel putin un item valid** (eng-review P1:
`prestatii` e fara default → obligatoriu; un payload fara el ar fi nevalid, exact ce pagina trebuie sa previna).
- [ ] Drift-test foloseste `field.is_required()` (nu doar prezenta cheii), ca sa nu raporteze fals campurile cu default.
- [ ] Fara I/O, fara DB, fara stare globala — pur (usor de testat).
- **Verificare E2E**: acoperita de UI (US-003) + unit teste.
### US-003: UI — tab "Integrare" cu exemple, retetar VFP si export
**Ca** utilizator **vreau** o pagina cu exemple gata de copiat in limbajul meu + linkuri de export **pentru ca**
sa integrez gateway-ul fara sa caut prin README/Swagger.
- **Depinde de**: US-001, US-002, US-004
- **Fisiere**: `app/web/routes.py` (`_render_integrare` + `/_fragments/integrare` + `integrare` in `_TABS_VALIDE` +
**branch nou in `_render_panel_for_tab`** — necesar pt deep-link server-side, altfel `/?tab=integrare` cade pe Acasa),
`app/web/templates/dashboard.html` (tuplu nav nou + generalizarea scriptului ARIA tablist), `app/web/templates/_integrare.html` (NOU),
`tests/test_web_integrare.py` (~4 fisiere)
- **Test intai (RED)**: `tests/test_web_integrare.py`
`test_tab_integrare_in_nav`,
`test_deeplink_tab_integrare_randeaza_panou_server_side` (`/?tab=integrare` → panou randat, NU Acasa — prinde branch-ul lipsa din `_render_panel_for_tab`),
`test_fragment_integrare_necesita_login` (`require_login`),
`test_pagina_contine_account_id_si_endpoint_real` (din `request.base_url`),
`test_pagina_are_tab_limbaje_si_vfp_cu_dialecte` (limbaj = tablist primar; VFP = un tab cu sub-tablist MSXML2/WinHttp),
`test_canal_secundar_prezentari_si_import`,
`test_export_card_openapi_postman_swagger`,
`test_buton_copiaza_citeste_din_pre_code` (markup copy citeste din `<pre><code>`, nu din atribut),
`test_empty_state_cta_cont_cand_fara_cheie_sau_creds`,
`test_fara_culori_hardcodate_doar_tokens` (snippet-ul template-ului nu contine `#`-hex; doar `var(--...)`).
- **Acceptance criteria**:
- [ ] Tab nou "Integrare" in nav (`dashboard.html`), `integrare` in `_TABS_VALIDE`, **branch nou in `_render_panel_for_tab`**,
deep-link `/?tab=integrare` randat server-side, fragment lazy pe click (pattern identic cu Mapari/Cont). NU atinge `_render_panel_import`/`coada`.
- [ ] **IA pe doua niveluri (design-review P1):** tab primar = LIMBAJ (curl/Python/PHP/C#/Node/VFP) — VFP e UN tab, nu sectiune
separata; in panelul VFP, al doilea tablist = dialect (MSXML2 / WinHttp). Tab secundar (in fiecare panel de limbaj) = CANAL
(Prezentari JSON / Import fisier). Se vede UN singur snippet o data (fara produs cartezian 14-snippet pe ecran).
- [ ] **Refolosire ARIA (design-review P1):** generalizeaza scriptul de keyboard-nav din `dashboard.html` (sageti/Home/End,
roving `tabindex`, sync `aria-selected`+`aria-controls`) ca sa prinda ORICE `[role=tablist]`, scoped pe containerul propriu
(nu primul `querySelector` global) — altfel tab-urile imbricate (limbaj/canal/VFP-dialect) intra in conflict pe `ArrowRight`.
- [ ] **Copy-to-clipboard (design-review P2):** buton per snippet care citeste textul din `<pre><code>` asociat (NU din `data-*`);
feedback in `aria-live="polite"` + label "Copiat" cu revenire la "Copiaza" dupa ~2s; `navigator.clipboard` cu `.catch()` +
fallback pt context ne-securizat (selecteaza textul + "Ctrl+C") — butonul nu ramane blocat fals pe "Copiat".
- [ ] **Empty-state onboarding (design-review P2 + CEO P1):** cand sesiunea NU are cheie rotita sau NU are creds RAR, afiseaza
deasupra snippet-urilor un empty-state cu CTA direct `href="/?tab=cont"` ("Genereaza cheia din Cont; o vezi o singura data").
Snippet-urile raman vizibile (placeholder-based). Flux de onboarding continuu, nu doar o nota text.
- [ ] Card separat "Export & referinta" (componenta `.cardlink`): Swagger `/docs`, `GET /openapi.json`, `GET /v1/integrare/postman.json`
(Postman = `download`; restul `target="_blank" rel="noopener"`).
- [ ] Integreaza formularul "Testeaza conexiunea" din US-004 (primul card al panoului; markup-ul tinteste ruta US-004).
- [ ] **Tokens de tema (design-review P3, pregatire 5.3):** template-ul foloseste DOAR variabilele din `:root`
(`--bg`/`--card`/`--ink`/`--muted`/`--line`/`--accent`), zero culori hex hardcodate; fara highlight de sintaxa colorat
(ar introduce paleta noua). Cod copiabil pe `--ink`, nu `--muted`.
- **Verificare E2E**: browser pe `/integrare` — comuta limbaj (inclusiv VFP cu dialect), comuta canal, copiaza un snippet (vezi
"Copiat" + revenire), vede account_id-ul + endpoint-ul reale, deschide `/openapi.json` si importul Postman.
### US-004: Backend web — "Testeaza conexiunea" (verifica cheia lipita, scoped pe sesiune)
**Ca** utilizator **vreau** sa lipesc cheia mea si sa primesc confirmare ca e valida **pentru ca** sa stiu ca
integrarea va autentifica corect inainte sa scriu cod.
- **Depinde de**: —
- **Fisiere**: `app/web/routes.py` (`POST /integrare/test-cheie`, `require_login` + CSRF),
`app/web/templates/_integrare_test_rezultat.html` (NOU — fragment de raspuns dedicat, NU inline in `_integrare.html` care
apartine US-003; eng-review P2: evita fisier partajat intre valuri), `tests/test_integrare_test_cheie.py` (~3 fisiere)
- **Test intai (RED)**: `tests/test_integrare_test_cheie.py`
`test_cheie_valida_a_contului_curent_ok` (mesaj succes + account_id),
`test_cheie_a_altui_cont_respinsa` (cheia mapeaza pe alt cont → mesaj "nu apartine contului tau", NU dezvaluie care),
`test_cheie_invalida_mesaj_clar`,
`test_cheie_revocata_dupa_rotire_respinsa` (eng-review P2: cel mai probabil caz de suport — lipesti cheia veche dupa rotire),
`test_cheie_goala_nu_da_fals_pozitiv_in_dev` (eng-review P2: foloseste `account_for_key` direct, NU `resolve_account_id` cu fallback cont 1),
`test_fara_login_redirect_sau_401`,
`test_csrf_lipsa_respinsa`,
`test_cheia_nu_apare_in_raspuns_sau_log` (no echo).
- **Acceptance criteria**:
- [ ] `POST /integrare/test-cheie` (`require_login`, CSRF): valideaza cheia lipita prin **`account_for_key` direct** (NU
`resolve_account_id` — altfel in dev o cheie goala/gunoi ar cadea pe cont 1 si ar raporta fals "valida"); confirma DOAR
daca mapeaza pe **contul sesiunii**; altfel mesaj neutru ("nu apartine contului tau", fara sa dezvaluie care cont).
- [ ] Cheie revocata (dupa rotire) → tratata ca invalida (`account_for_key` filtreaza `active=1`).
- [ ] Niciun echo al cheii in raspuns/log (regula 422-no-echo din `main.py`).
- [ ] Raspuns HTML fragment dedicat (`_integrare_test_rezultat.html`), htmx swap intr-un container `aria-live="polite"`:
succes pe `.flash` (border `--ok`, "Cheie valida — cont X"), eroare pe `.banner` (border `--err`). Label uman, fara emoji.
- [ ] Input `type=password` + `autocomplete="off"`; microcopy anti-confuzie langa camp: "Verificam doar daca cheia e valida.
Nu o salvam si nu o memoram — cheia se gestioneaza in Cont." Buton "Testeaza conexiunea" (NU "Salveaza").
- [ ] NU creeaza/roteste chei (doar verifica) — fara efecte secundare.
- **Verificare E2E**: browser — lipeste cheia reala (din rotire `/cont`) → "valida, cont X"; lipeste gunoi → eroare clara;
lipeste cheia veche dupa rotire → respinsa.
## 4. Graful de valuri
- **Val 1 (paralel, fisiere disjuncte):** US-001 (`integrare_router.py` NOU) ‖ US-002 (`integrare_examples.py` NOU)
‖ US-004 (`routes.py` + test). US-001 si US-002 sunt fisiere noi; US-004 atinge `routes.py` (ruta noua, fara
suprapunere cu US-003 care vine in Val 2). Atentie: US-003 si US-004 ating ambele `routes.py` → NU in acelasi val.
- **Val 2:** US-003 (UI: `routes.py` + `dashboard.html` + `_integrare.html`) — depinde de US-001/US-002/US-004.
Nota (eng-review P2): US-004 isi pune raspunsul in `_integrare_test_rezultat.html` (fisier propriu), NU in `_integrare.html`
(al US-003) — asa cele doua valuri nu impart un template.
**Prioritate de descope (CEO-review P2/P3)** — daca timpul se scurteaza, taie in aceasta ordine inversa de valoare:
1. (pastreaza ultimul) **US-002 + US-003 exemple + retetar VFP in pagina** = wedge-ul de adoptie, ne-negociabil.
2. US-004 (test cheie) — potentator.
3. US-001 ping — potentator.
4. (primul taiat) **export Postman/OpenAPI** — audienta moderna (Python/Node), public secundar fata de VFP. Retetarul VFP
ramane prim-clas indiferent de descope.
## 5. Considerații tehnice
- **Auth**: header `X-API-Key: rfak_...` (sau `Authorization: Bearer rfak_...`), dependinta `resolve_account_id`
(`app/auth.py`). In dev (`require_api_key=false`) lipsa cheii → cont 1; `ping` semnaleaza asta cu `autentificat_cu_cheie`.
- **Schema request** (din `app/models.py`): `PrezentareRequest{rar_credentials{email,password}, prezentari[PrezentareIn]}`;
`PrezentareIn{vin, nr_inmatriculare, data_prestatie(YYYY-MM-DD), odometru_final(str), odometru_initial?, sistem_reparat="null",
obs?, b64_image?, prestatii?[{cod_prestatie|cod_op_service, denumire}]}`.
- **Import**: `POST /v1/import`, multipart, camp `file` (`app/api/v1/import_router.py`).
- **base_url** derivat din `request.base_url`; documenteaza override in spatele proxy (X-Forwarded). Risc minor de
base_url gresit in spatele reverse-proxy — mitigat prin nota in pagina.
- **Pattern UI**: tab nou = tuplu in `dashboard.html` + `integrare` in `_TABS_VALIDE` + `_render_integrare` +
`/_fragments/integrare` (identic cu Mapari/Cont/Nomenclator).
- **OpenAPI/Postman**: `/openapi.json` e gratuit din FastAPI; colectia Postman e construita o data din endpoint-urile cunoscute.
## 6. Riscuri
- **Drift de schema** intre snippet-uri si `PrezentareIn` real → test care leaga snippet-ul de `model_fields` (US-002).
- **base_url** gresit in spatele proxy → nota + derivare din request.
- **Confuzie dev "merge fara cheie"** (cont 1) → `ping` raporteaza `autentificat_cu_cheie=false`.
- **Scurgere cheie in test-cheie** → no-echo enforce + test dedicat (US-004).
## 7. Intrebari deschise
Rezolvate cu utilizatorul (2026-06-22): test conexiune = `GET /v1/ping` nou (independent de 5.2); exemple pe ambele
canale (prezentari + import); continut doar in-app dinamic (fara markdown versionat); VFP = ambele dialecte (MSXML2 + WinHttp).
Niciuna ramasa deschisa.
## 8. Verificare (rezumat E2E per canal)
- API: `curl GET /v1/ping` cu cheie reala pe instanta locala (8010) → 200 cont corect; import `postman.json`.
- Web: browser pe `/integrare` — comuta limbaj, copiaza snippet, vede account_id+endpoint reale, testeaza cheia.
- Regresia de aur (neatinsa, dar de confirmat ca nu s-a stricat): `POST /v1/prezentari` (sau import → commit) →
worker → `FINALIZATA` la RAR test.
**Criteriu de succes al wedge-ului (CEO-review P3):** un service nou (stil VFP) ajunge de la zero la o prezentare
`FINALIZATA` la RAR test folosind DOAR pagina `/integrare` (copiaza snippet → testeaza cheia → vede `are_creds_rar`
trimite), fara suport uman si fara sa atinga README/Swagger separat.
---
## Review-uri de plan (§5.3 — rulate 2026-06-22)
- [x] **CEO** (valoare/scope) — APROB CU MODIFICARI. Aplicat: ping/test ca readiness-check (`are_creds_rar`), CTA continuu spre
`/cont` in empty-state, VFP prim-clas + prioritate de descope (§4), criteriu de succes masurabil (§8). (P2 "ping redundant"
rezolvat prin diferentierea payload-ului: ping poarta `are_creds_rar`+`mediu`, deci e readiness, nu auth-echo.)
- [x] **Eng** (fezabilitate/teste) — APROB CU MODIFICARI. Aplicat: montare router `prefix="/v1"` in `main.py` fara dublare +
test inregistrare unica; drift-test cu `is_required()` + `prestatii` obligatoriu (US-002); template dedicat
`_integrare_test_rezultat.html` (US-004, anti fisier-partajat intre valuri); teste Bearer/cheie-goala/cheie-revocata;
`account_for_key` nu `resolve_account_id`; Postman allowlist exact 3 (nu `app.routes`); `_render_panel_for_tab` in fisierele US-003.
- [x] **Design** (UI) — APROB CU MODIFICARI, claritate plan UI 6/10 → adresat. Aplicat: IA pe 2 niveluri (limbaj primar, canal
secundar, VFP cu sub-dialect); refolosire + generalizare script ARIA tablist (scoped per container); copy din `<pre><code>` +
`aria-live` + fallback non-HTTPS; empty-state cu CTA `/?tab=cont`; card "Export & referinta" cu `.cardlink`; doar tokens de
tema (pregatire 5.3); microcopy anti-confuzie + `type=password` la test-cheie.
Toate cele trei: **APROB CU MODIFICARI**, modificarile incorporate mai sus. Asteapta poarta umana de aprobare (§5.8) → `aprobat`.