Files
rar-autopass/app/web/templates/_integrare.html
Claude Agent 5dc963a02c feat(api): rar_credentials optional pe POST /v1/prezentari
Cand `rar_credentials` lipseste din cerere, submission-ul intra fara creds
efemere, iar worker-ul cade pe creds-urile RAR durabile ale contului
(accounts.rar_creds_enc). Identificarea contului ramane pe cheia API.
Trimiterea explicita a creds-urilor suprascrie creds-urile contului pe acea
cerere (back-compat: fluxul vechi ROAAUTO merge identic).

- models.py: rar_credentials: RarCredentials | None = None
- router.py: cripteaza creds doar daca exista (altfel creds_enc=NULL)
- worker NEATINS: avea deja fallback _creds_for(...) or _creds_from_account(...)

Pagina /integrare aliniata: exemplele cod (7 limbaje) + export Postman nu mai
includ rar_credentials in payload; nota noua explica modelul (creds pe cont,
optional in payload). README rescris compact + reflecta optionalitatea.

Test nou: enqueue fara creds -> submission fara creds efemere -> fallback pe
contul cu creds salvate. Suita: 673 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 13:39:53 +00:00

350 lines
26 KiB
HTML

{# Panoul Integrare: exemple de cod multi-limbaj + export & referinta. #}
{# Variabile context: account_id, base_url, exemple, are_cheie, are_creds, csrf_token #}
<div id="integrare-section">
{# Empty-state: lipsesc cheie API sau credentiale RAR #}
{% if not are_cheie or not are_creds %}
<div class="banner warn" style="margin-bottom:16px;" role="alert" aria-live="polite">
<strong>Configurare incompleta.</strong>
{% if not are_creds and not are_cheie %}
Lipsesc atat credentialele RAR cat si o cheie API activa.
{% elif not are_creds %}
Lipsesc credentialele RAR pentru trimitere.
{% else %}
Lipseste o cheie API activa.
{% endif %}
Mergi la <a href="/?tab=cont">tab-ul Cont</a> pentru a le configura.
</div>
{% endif %}
{# Card: ID cont si endpoint de baza #}
<div class="card" style="margin-bottom:16px;">
<div style="display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:8px;">
<span class="muted" style="font-size:13px;">Cont ID:</span>
<strong style="font-size:13px;">{{ account_id }}</strong>
<span class="muted" style="font-size:13px; margin-left:16px;">Endpoint:</span>
<code style="font-size:12px; color:var(--accent);">{{ base_url }}</code>
</div>
<p class="muted" style="font-size:12px; margin:10px 0 0;">
Cererile trimit doar cheia API + datele prezentarii. Credentialele RAR se configureaza
o data in <a href="/?tab=cont">Cont</a> si sunt folosite automat la trimitere. Optional,
poti include <code>rar_credentials</code> in payload ca sa le suprascrii pe acea cerere.
</p>
</div>
{# Tab-list PRIMAR: limbaje #}
<div class="card" style="margin-bottom:16px;">
<div role="tablist" class="tab-bar" aria-label="Limbaje de programare" id="tl-limbaje" style="margin-bottom:0; border-bottom:1px solid var(--line);">
<button role="tab" id="tab-curl" aria-selected="true" aria-controls="panel-curl" tabindex="0" class="tab-link tab-activ" style="border:none; background:none; cursor:pointer;">curl</button>
<button role="tab" id="tab-python" aria-selected="false" aria-controls="panel-python" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer;">Python</button>
<button role="tab" id="tab-php" aria-selected="false" aria-controls="panel-php" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer;">PHP</button>
<button role="tab" id="tab-csharp" aria-selected="false" aria-controls="panel-csharp" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer;">C#</button>
<button role="tab" id="tab-node" aria-selected="false" aria-controls="panel-node" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer;">Node</button>
<button role="tab" id="tab-vfp" aria-selected="false" aria-controls="panel-vfp" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer;">VFP</button>
</div>
{# Panel curl #}
<div role="tabpanel" id="panel-curl" aria-labelledby="tab-curl" style="padding:16px 0 0;">
{% set ex = exemple["curl"] %}
<div role="tablist" class="tab-bar" aria-label="Canal curl" id="tl-curl-canal" style="margin-bottom:8px; border-bottom:1px solid var(--line);">
<button role="tab" id="tab-curl-prez" aria-selected="true" aria-controls="panel-curl-prez" tabindex="0" class="tab-link tab-activ" style="border:none; background:none; cursor:pointer; font-size:13px;">Prezentari JSON</button>
<button role="tab" id="tab-curl-import" aria-selected="false" aria-controls="panel-curl-import" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer; font-size:13px;">Import fisier</button>
</div>
<div role="tabpanel" id="panel-curl-prez" aria-labelledby="tab-curl-prez">
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet curl prezentari" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["prezentari"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
<div role="tabpanel" id="panel-curl-import" aria-labelledby="tab-curl-import" hidden>
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet curl import" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["import"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
</div>
{# Panel python #}
<div role="tabpanel" id="panel-python" aria-labelledby="tab-python" hidden style="padding:16px 0 0;">
{% set ex = exemple["python"] %}
<div role="tablist" class="tab-bar" aria-label="Canal python" id="tl-python-canal" style="margin-bottom:8px; border-bottom:1px solid var(--line);">
<button role="tab" id="tab-python-prez" aria-selected="true" aria-controls="panel-python-prez" tabindex="0" class="tab-link tab-activ" style="border:none; background:none; cursor:pointer; font-size:13px;">Prezentari JSON</button>
<button role="tab" id="tab-python-import" aria-selected="false" aria-controls="panel-python-import" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer; font-size:13px;">Import fisier</button>
</div>
<div role="tabpanel" id="panel-python-prez" aria-labelledby="tab-python-prez">
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet python prezentari" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["prezentari"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
<div role="tabpanel" id="panel-python-import" aria-labelledby="tab-python-import" hidden>
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet python import" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["import"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
</div>
{# Panel PHP #}
<div role="tabpanel" id="panel-php" aria-labelledby="tab-php" hidden style="padding:16px 0 0;">
{% set ex = exemple["php"] %}
<div role="tablist" class="tab-bar" aria-label="Canal php" id="tl-php-canal" style="margin-bottom:8px; border-bottom:1px solid var(--line);">
<button role="tab" id="tab-php-prez" aria-selected="true" aria-controls="panel-php-prez" tabindex="0" class="tab-link tab-activ" style="border:none; background:none; cursor:pointer; font-size:13px;">Prezentari JSON</button>
<button role="tab" id="tab-php-import" aria-selected="false" aria-controls="panel-php-import" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer; font-size:13px;">Import fisier</button>
</div>
<div role="tabpanel" id="panel-php-prez" aria-labelledby="tab-php-prez">
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet php prezentari" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["prezentari"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
<div role="tabpanel" id="panel-php-import" aria-labelledby="tab-php-import" hidden>
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet php import" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["import"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
</div>
{# Panel C# #}
<div role="tabpanel" id="panel-csharp" aria-labelledby="tab-csharp" hidden style="padding:16px 0 0;">
{% set ex = exemple["csharp"] %}
<div role="tablist" class="tab-bar" aria-label="Canal csharp" id="tl-csharp-canal" style="margin-bottom:8px; border-bottom:1px solid var(--line);">
<button role="tab" id="tab-csharp-prez" aria-selected="true" aria-controls="panel-csharp-prez" tabindex="0" class="tab-link tab-activ" style="border:none; background:none; cursor:pointer; font-size:13px;">Prezentari JSON</button>
<button role="tab" id="tab-csharp-import" aria-selected="false" aria-controls="panel-csharp-import" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer; font-size:13px;">Import fisier</button>
</div>
<div role="tabpanel" id="panel-csharp-prez" aria-labelledby="tab-csharp-prez">
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet csharp prezentari" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["prezentari"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
<div role="tabpanel" id="panel-csharp-import" aria-labelledby="tab-csharp-import" hidden>
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet csharp import" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["import"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
</div>
{# Panel Node #}
<div role="tabpanel" id="panel-node" aria-labelledby="tab-node" hidden style="padding:16px 0 0;">
{% set ex = exemple["node"] %}
<div role="tablist" class="tab-bar" aria-label="Canal node" id="tl-node-canal" style="margin-bottom:8px; border-bottom:1px solid var(--line);">
<button role="tab" id="tab-node-prez" aria-selected="true" aria-controls="panel-node-prez" tabindex="0" class="tab-link tab-activ" style="border:none; background:none; cursor:pointer; font-size:13px;">Prezentari JSON</button>
<button role="tab" id="tab-node-import" aria-selected="false" aria-controls="panel-node-import" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer; font-size:13px;">Import fisier</button>
</div>
<div role="tabpanel" id="panel-node-prez" aria-labelledby="tab-node-prez">
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet node prezentari" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["prezentari"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
<div role="tabpanel" id="panel-node-import" aria-labelledby="tab-node-import" hidden>
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet node import" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["import"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
</div>
{# Panel VFP: tab-list SECUNDAR pentru dialecte #}
<div role="tabpanel" id="panel-vfp" aria-labelledby="tab-vfp" hidden style="padding:16px 0 0;">
<p class="muted" style="font-size:13px; margin:0 0 8px;">Visual FoxPro — alege dialectul COM:</p>
{# Dialecte VFP #}
<div role="tablist" class="tab-bar" aria-label="Dialecte VFP" id="tl-vfp-dialect" style="margin-bottom:12px; border-bottom:1px solid var(--line);">
<button role="tab" id="tab-vfp-msxml" aria-selected="true" aria-controls="panel-vfp-msxml" tabindex="0" class="tab-link tab-activ" style="border:none; background:none; cursor:pointer; font-size:13px;">MSXML2</button>
<button role="tab" id="tab-vfp-winhttp" aria-selected="false" aria-controls="panel-vfp-winhttp" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer; font-size:13px;">WinHttp</button>
</div>
{# MSXML2 #}
<div role="tabpanel" id="panel-vfp-msxml" aria-labelledby="tab-vfp-msxml">
{% set ex = exemple["vfp_msxml"] %}
<div role="tablist" class="tab-bar" aria-label="Canal vfp msxml" id="tl-vfp-msxml-canal" style="margin-bottom:8px; border-bottom:1px solid var(--line);">
<button role="tab" id="tab-vfp-msxml-prez" aria-selected="true" aria-controls="panel-vfp-msxml-prez" tabindex="0" class="tab-link tab-activ" style="border:none; background:none; cursor:pointer; font-size:12px;">Prezentari JSON</button>
<button role="tab" id="tab-vfp-msxml-import" aria-selected="false" aria-controls="panel-vfp-msxml-import" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer; font-size:12px;">Import fisier</button>
</div>
<div role="tabpanel" id="panel-vfp-msxml-prez" aria-labelledby="tab-vfp-msxml-prez">
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet VFP MSXML2 prezentari" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["prezentari"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
<div role="tabpanel" id="panel-vfp-msxml-import" aria-labelledby="tab-vfp-msxml-import" hidden>
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet VFP MSXML2 import" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["import"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
</div>
{# WinHttp #}
<div role="tabpanel" id="panel-vfp-winhttp" aria-labelledby="tab-vfp-winhttp" hidden>
{% set ex = exemple["vfp_winhttp"] %}
<div role="tablist" class="tab-bar" aria-label="Canal vfp winhttp" id="tl-vfp-winhttp-canal" style="margin-bottom:8px; border-bottom:1px solid var(--line);">
<button role="tab" id="tab-vfp-winhttp-prez" aria-selected="true" aria-controls="panel-vfp-winhttp-prez" tabindex="0" class="tab-link tab-activ" style="border:none; background:none; cursor:pointer; font-size:12px;">Prezentari JSON</button>
<button role="tab" id="tab-vfp-winhttp-import" aria-selected="false" aria-controls="panel-vfp-winhttp-import" tabindex="-1" class="tab-link" style="border:none; background:none; cursor:pointer; font-size:12px;">Import fisier</button>
</div>
<div role="tabpanel" id="panel-vfp-winhttp-prez" aria-labelledby="tab-vfp-winhttp-prez">
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet VFP WinHttp prezentari" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["prezentari"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
<div role="tabpanel" id="panel-vfp-winhttp-import" aria-labelledby="tab-vfp-winhttp-import" hidden>
<div style="position:relative;">
<button class="btn-copiaza" aria-label="Copiaza snippet VFP WinHttp import" style="position:absolute; top:8px; right:8px; font-size:12px; padding:4px 10px; background:var(--line); color:var(--ink); border:1px solid var(--line); border-radius:4px; cursor:pointer;">Copiaza</button>
<pre style="background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; margin:0;"><code style="font-size:12px; font-family:ui-monospace,monospace; color:var(--ink);">{{ ex["import"] | e }}</code></pre>
</div>
<div aria-live="polite" class="copiaza-feedback muted" style="font-size:12px; min-height:20px; margin-top:4px;"></div>
</div>
</div>
</div>
</div>
{# Card: Export & referinta #}
<div class="card" style="margin-bottom:16px;">
<h3 style="margin:0 0 12px; font-size:15px;">Export &amp; referinta</h3>
<div style="display:flex; flex-wrap:wrap; gap:8px;">
<a class="cardlink" href="/docs" target="_blank" rel="noopener">Swagger UI — /docs</a>
<a class="cardlink" href="/openapi.json" target="_blank" rel="noopener">Schema OpenAPI — /openapi.json</a>
<a class="cardlink" href="/v1/integrare/postman.json" download>Colectie Postman — /v1/integrare/postman.json</a>
</div>
</div>
{# Formular test conexiune #}
<div class="card" style="margin-bottom:16px;">
<h3 style="margin:0 0 12px; font-size:15px;">Testeaza conexiunea</h3>
<form hx-post="/integrare/test-cheie"
hx-target="#integrare-test-rezultat"
hx-swap="innerHTML"
style="display:flex; gap:8px; flex-wrap:wrap; align-items:flex-end;">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div>
<label for="test-api-key" style="display:block; font-size:13px; color:var(--muted); margin-bottom:4px;">Cheie API (rfak_...)</label>
<input type="password" id="test-api-key" name="api_key" placeholder="rfak_..."
style="width:280px;" autocomplete="off">
<p class="muted" style="font-size:12px; margin:4px 0 0;">Verificam doar daca cheia e valida. Nu o salvam si nu o memoram — cheia se gestioneaza in Cont.</p>
</div>
<button type="submit">Testeaza</button>
</form>
<div id="integrare-test-rezultat" style="margin-top:8px;"></div>
</div>
</div>
<script>
(function() {
/* Navigare ARIA pentru tab-uri multiple (scoped pe containerul propriu). */
var root = document.getElementById('integrare-section');
if (!root) return;
root.querySelectorAll('[role="tablist"]').forEach(function(tablist) {
var tabs = Array.from(tablist.querySelectorAll('[role="tab"]'));
if (!tabs.length) return;
/* Navigare cu sageti, Home, End */
tablist.addEventListener('keydown', function(e) {
var idx = tabs.indexOf(document.activeElement);
if (idx === -1) return;
var next = -1;
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
next = (idx + 1) % tabs.length;
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
next = (idx - 1 + tabs.length) % tabs.length;
} else if (e.key === 'Home') {
next = 0;
} else if (e.key === 'End') {
next = tabs.length - 1;
}
if (next !== -1) {
e.preventDefault();
tabs[next].focus();
}
});
/* Click pe tab: activeaza panelul corespunzator */
tabs.forEach(function(tab) {
tab.addEventListener('click', function() {
var panelId = tab.getAttribute('aria-controls');
if (!panelId) return;
/* Dezactiveaza toate tab-urile din acest tablist */
tabs.forEach(function(t) {
t.setAttribute('aria-selected', 'false');
t.setAttribute('tabindex', '-1');
t.classList.remove('tab-activ');
var pid = t.getAttribute('aria-controls');
if (pid) {
var p = document.getElementById(pid);
if (p) p.hidden = true;
}
});
/* Activeaza tab-ul curent */
tab.setAttribute('aria-selected', 'true');
tab.setAttribute('tabindex', '0');
tab.classList.add('tab-activ');
var panel = document.getElementById(panelId);
if (panel) panel.hidden = false;
});
});
});
/* Buton Copiaza: citeste textul din <pre><code> sibling, nu din data-* */
root.querySelectorAll('.btn-copiaza').forEach(function(btn) {
btn.addEventListener('click', function() {
var wrapper = btn.closest('div[style*="position:relative"]') || btn.parentElement;
var code = wrapper ? wrapper.querySelector('pre code') : null;
if (!code) return;
var text = code.innerText || code.textContent;
var feedback = wrapper.parentElement && wrapper.parentElement.querySelector('.copiaza-feedback');
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function() {
btn.textContent = 'Copiat';
setTimeout(function() { btn.textContent = 'Copiaza'; }, 2000);
if (feedback) {
feedback.textContent = 'Copiat!';
setTimeout(function() { feedback.textContent = ''; }, 2000);
}
}).catch(function() {
if (feedback) feedback.textContent = 'Eroare la copiere.';
});
} else {
/* Fallback pentru browsere fara Clipboard API */
var ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try {
document.execCommand('copy');
btn.textContent = 'Copiat';
setTimeout(function() { btn.textContent = 'Copiaza'; }, 2000);
if (feedback) {
feedback.textContent = 'Copiat!';
setTimeout(function() { feedback.textContent = ''; }, 2000);
}
} catch(err) {
if (feedback) feedback.textContent = 'Eroare la copiere.';
}
document.body.removeChild(ta);
}
});
});
})();
</script>