Files
rar-autopass/app/web/templates/_status.html
Claude Agent 3b5cf7a7d9 feat(5.19): auto-send toggle per cont + tinere manuala randuri (held)
Comutator accounts.auto_send_enabled per cont: Auto OFF (default) tine randurile
la ingestie (submissions.held=1), worker-ul (claim_one AND held=0) le sare pana la
eliberare umana (per rand/bulk/auto-release OFF->ON). Snapshot held prin chokepoint
unic held_for_account pe toate caile de ingestie (API, import, reresolve, reactivare).

- schema/migrare: coloana held + index partial idx_submissions_held; auto_send_enabled
- API: echo onest held+motiv (US-010), ruta /prezentari/{id}/trimite-acum
- web: toggle header, modal confirmare tipata, buton Trimite per rand + Trimite toate,
  banner coada tinuta imbatranita (L.142), contor "In asteptare (manual)"
- worker: expire_held (US-008, inchide gaura retentie PII), metrics held gauges
- ops: tools/carantina_held + runbook rollback (R4)

Nota review (/code-review high): re-snapshot held lipseste pe caile repune/corectie
(requeue_submission, post_corectie, bulk-fix) — de aliniat separat cu create_prezentari.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 08:34:40 +00:00

228 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<div id="status-bar" class="status-bar card"
hx-get="/_fragments/status?tab={{ tab_activ | default('acasa') }}"
hx-trigger="every 15s, trimiteriChanged from:body"
hx-swap="outerHTML"
{% if oob %}hx-swap-oob="outerHTML"{% endif %}>
{# Banner cont in asteptare de activare (mereu vizibil cand contul e inactiv) #}
{% if not account_active %}
<div style="margin-bottom:12px; padding:8px 10px; border-left:3px solid var(--warn);
background:color-mix(in srgb, var(--warn) 12%, var(--card)); border-radius:6px; font-size:13px;">
<strong>Cont in asteptare de activare.</strong>
Configureaza credentialele RAR si pregateste importul acum; trimiterea catre RAR
porneste automat dupa activare de catre administrator.
</div>
{% endif %}
{# === US-007 (PRD 5.19): banner coada tinuta imbatranita — mitigare OBLIGATORIE pt. default OFF.
Cand exista randuri `queued AND held=1` mai vechi de held_aged_days, semnalul de conformitate
L.142 (declarare obligatorie). Derivat (zero stare noua), varsta pe created_at.
Prioritate bannere (D7 — un-singur-banner "notice", ca sa nu impinga contoarele sub fold pe
mobil): cont inactiv (sus) + strip RAR jos raman separate; intre notice-uri, held-aged >
trial (risc legal > notita trial) -> trial e suprimat cand held-aged e activ. #}
{% set _held_aged_activ = (held_aged_count|default(0) > 0) and account_active %}
{% if _held_aged_activ %}
<div id="banner-held-aged" role="alert"
style="margin-bottom:10px; padding:8px 12px; border-left:3px solid var(--warn);
background:color-mix(in srgb, var(--warn) 12%, var(--card)); border-radius:6px;
font-size:var(--fs-sm); display:flex; align-items:center; gap:8px; flex-wrap:wrap;">
<span><strong>{{ held_aged_count }}</strong>
{{ 'prezentare tinuta' if held_aged_count == 1 else 'prezentari tinute' }}
de peste {{ held_aged_days }} zile — declarare obligatorie (L.142).</span>
<button type="button"
hx-get="/_fragments/submissions?held=1"
hx-target="#submissions-wrap" hx-swap="innerHTML"
style="margin-left:auto; padding:2px 10px; font-size:var(--fs-xs); border-radius:99px;
border:1px solid var(--warn); background:transparent; color:var(--warn);
cursor:pointer; font-weight:600;">
Vezi tinute
</button>
</div>
{% endif %}
{# US-006 (5.17) — Banner one-time trial->Gratuit (T-DES-1): afisat la prima incarcare
dupa expirarea trial-ului. Discret, non-blocant; dismissibil via sessionStorage.
Nu acopera stripul de sanatate (apare inainte de health strip, la acelasi nivel). #}
{% if trial_expirat_recent|default(false) and not _held_aged_activ %}
<div id="banner-trial-expirat"
role="status"
style="margin-bottom:10px; padding:7px 12px;
border-left:3px solid var(--warn);
background:color-mix(in srgb, var(--warn) 10%, var(--card));
border-radius:6px; font-size:var(--fs-sm);
display:flex; align-items:center; justify-content:space-between; gap:8px;">
<span>Trial Pro expirat — esti pe Gratuit, 60/luna</span>
<button onclick="sessionStorage.setItem('tfx','1'); document.getElementById('banner-trial-expirat').style.display='none';"
style="background:transparent; border:none; color:var(--muted); cursor:pointer;
font-size:18px; padding:0 4px; line-height:1; flex-shrink:0;"
aria-label="Inchide bannerul">×</button>
</div>
<script>(function(){ if(sessionStorage.getItem('tfx')){ var el=document.getElementById('banner-trial-expirat'); if(el) el.style.display='none'; } })();</script>
{% endif %}
{# === US-003 (PRD 5.16): Banda de stare RAR — NUMAI cand BLOCAT (rosu, lat de 100%).
OK = dot verde in antet (base.html); banda nu mai apare cand totul e ok.
Elementul id="strip-sanatate" ramane in DOM mereu, dar goleste continutul cand OK,
astfel "hidden" + fara continut eroare in sursa = nu pica testele de prezenta id-ului.
#}
{% if sanatate_ok %}
<div id="strip-sanatate" role="status" aria-live="polite" hidden></div>
{% else %}
<div id="strip-sanatate"
role="status"
aria-live="polite"
style="display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;
padding:10px 14px; border-radius:8px; margin-bottom:14px;
background:color-mix(in srgb, var(--err) 16%, var(--card)); border:1px solid color-mix(in srgb, var(--err) 40%, transparent);">
<div style="display:flex; align-items:center; gap:9px;">
<span aria-hidden="true" style="font-weight:700; font-size:15px; color:var(--err);">&#10007;</span>
<span style="font-weight:700; font-size:13px;">{{ sanatate_text }}</span>
</div>
<span style="font-family:var(--font-mono); font-size:var(--fs-xs); color:var(--muted); white-space:nowrap;">
{{ eticheta_ultima_auth }}: {{ last_login }}
</span>
</div>
{% endif %}
{# === US-002 (PRD 5.16): 5 carduri-contor separate (desktop) + bara compacta (mobil <=560px).
Total / Luna asta / Azi / In coada / De corectat.
#}
{# Desktop: 5 carduri side-by-side. display:flex + layout stau in CSS (.contoare-desktop in
base.html), NU inline, ca media query-ul <=560px sa le poata ascunde pe mobil (bara compacta). #}
<div class="contoare-desktop">
{# Total trimise (all-time) #}
<div class="contor-card" style="flex:1; min-width:100px;">
<div class="contor-cifra">{{ counts_sent }}</div>
<div class="contor-label">Total</div>
</div>
{# Luna asta #}
<div class="contor-card" style="flex:1; min-width:100px;">
<div class="contor-cifra s-accent">{{ sent_month }}</div>
<div class="contor-label">Luna asta</div>
</div>
{# Azi #}
<div class="contor-card" style="flex:1; min-width:80px;">
<div class="contor-cifra s-accent">{{ sent_today }}</div>
<div class="contor-label">Azi</div>
</div>
{# In coada — contorul "In asteptare (manual)" se pliaza aici (US-006/D6): nu adaugam a 6-a
celula, ci o sub-linie amber cand exista randuri tinute (queued AND held=1). #}
<div class="contor-card" style="flex:1; min-width:80px;">
<div class="contor-cifra s-queued">{{ counts_queued }}</div>
<div class="contor-label">In coada</div>
{% if held_manual_count|default(0) %}
<div class="contor-sub" style="font-size:var(--fs-xs); color:var(--warn); font-weight:600; margin-top:2px;"
title="Prezentari tinute manual — asteapta eliberarea ta">
{{ held_manual_count }} manual
</div>
{% endif %}
</div>
{# De corectat (rosu daca >0, muted la 0; link catre lista) #}
<a href="/" class="contor-card"
style="flex:1; min-width:80px; text-decoration:none; display:block; cursor:pointer;"
aria-label="De corectat: {{ blocate_total }} — click pentru lista de trimiteri">
<div class="contor-cifra {{ 's-error' if blocate_total else 'muted' }}">{{ blocate_total }}</div>
<div class="contor-label">De corectat</div>
</a>
</div>
{# Mobil (<=560px): bara compacta — numerele + etichete scurte in-line #}
<div class="contoare-compact">
<div class="compact-item">
<div class="compact-nr">{{ counts_sent }}</div>
<div class="compact-lbl">Total</div>
</div>
<div class="compact-item">
<div class="compact-nr s-accent">{{ sent_month }}</div>
<div class="compact-lbl">Luna</div>
</div>
<div class="compact-item">
<div class="compact-nr s-accent">{{ sent_today }}</div>
<div class="compact-lbl">Azi</div>
</div>
<div class="compact-item">
<div class="compact-nr s-queued">{{ counts_queued }}</div>
<div class="compact-lbl">Coada{% if held_manual_count|default(0) %} <span style="color:var(--warn); font-weight:700;">·{{ held_manual_count }}m</span>{% endif %}</div>
</div>
<a class="compact-item" href="/" style="text-decoration:none; color:inherit;">
<div class="compact-nr {{ 's-error' if blocate_total else 'muted' }}">{{ blocate_total }}</div>
<div class="compact-lbl">Erori</div>
</a>
</div>
{# === US-011 (5.20): Indicator mediu RAR activ per cont.
La 0 medii: nu afisa (cont fara configuratie RAR).
La 1 mediu: eticheta statica (fara toggle).
La 2 medii: buton de comutare HTMX (toggle conditionat).
US-010: culori prin eticheta_env/clase .env-badge-* (consistenta cu badge-urile din liste).
#}
{% set _medii = medii_disponibile | default([]) %}
{% set _env = env_default | default('prod') %}
{% set _eb = eticheta_env(_env) %}
{% if _medii | length >= 1 %}
<div class="env-indicator"
style="display:flex; align-items:center; gap:8px; margin-bottom:10px;
padding:6px 10px; border-radius:6px; font-size:var(--fs-sm);
background:color-mix(in srgb, {% if _env == 'prod' %}var(--err){% else %}var(--line){% endif %} 10%, var(--card));
border:1px solid color-mix(in srgb, {% if _env == 'prod' %}var(--err){% else %}var(--line){% endif %} 30%, transparent);">
<span style="font-weight:600;">Mediu RAR:</span>
<span class="{{ _eb[1] }}">{{ _eb[0] }}</span>
{% if _medii | length >= 2 %}
{# Toggle: apare DOAR cand sunt cel putin 2 medii disponibile #}
<form method="post" action="/_fragments/status/toggle-env"
hx-post="/_fragments/status/toggle-env"
hx-target="#status-bar"
hx-swap="outerHTML"
style="margin:0; padding:0; display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token | default('') }}">
<button type="submit"
style="padding:2px 10px; font-size:var(--fs-xs); border-radius:99px;
border:1px solid currentColor; background:transparent; cursor:pointer;
color:var(--muted); line-height:1.4;"
title="Comuta la {{ 'PRODUCȚIE' if _env == 'test' else 'Testare' }}">
Comuta
</button>
</form>
{% endif %}
</div>
{% endif %}
{# === Navigatie rapida: Trimiteri + Mapari cu badge needs_mapping ===
Pastrata exact ca inainte (US-005): tab_activ determina marcajul activ.
#}
{% set _tab = tab_activ | default('acasa') %}
<nav class="status-nav" aria-label="Navigatie rapida"
style="display:flex; gap:8px 16px; flex-wrap:wrap; font-size:13px; border-top:1px solid var(--line); padding-top:8px;">
<a href="/"
{% if _tab == 'acasa' or _tab == '' %}aria-current="page"{% endif %}
class="status-nav-link{% if _tab == 'acasa' or _tab == '' %} status-nav-activ{% endif %}">Trimiteri</a>
<a href="/?tab=mapari"
{% if _tab == 'mapari' %}aria-current="page"{% endif %}
class="status-nav-link{% if _tab == 'mapari' %} status-nav-activ{% endif %}">Mapari{% if mapari_badge %}<span class="tab-badge" aria-hidden="true" style="display:inline-flex; align-items:center; justify-content:center; min-width:16px; height:16px; margin-left:4px; padding:0 4px; border-radius:99px; background:var(--err); color:#fff; font-size:11px; font-weight:700;">{{ mapari_badge }}</span>{% endif %}</a>
</nav>
{# US-006 (5.17) + T-6 (5.16): linia de plan in CORP apare DOAR in starea de avertizare
(>=80% -> --warn; limita atinsa -> --err). Consumul normal (N/60) traieste in badge-ul
din antet + linia din meniul burger, nu ca rand permanent in corp (densitate redusa).
Ierarhie: nu concureaza cu stripul de sanatate (zero-silent-failures pastrat). #}
{% if plan_linie and (plan_warn|default(false) or plan_limita_atinsa|default(false)) %}
<div class="plan-status-line"
style="font-size:var(--fs-sm); margin-top:6px; padding-top:6px;
border-top:1px solid var(--line2);
color:{% if plan_limita_atinsa|default(false) %}var(--err){% elif plan_warn|default(false) %}var(--warn){% else %}var(--muted){% endif %};
{% if plan_warn|default(false) %}font-weight:600;{% endif %}">
{{ plan_linie }}
{% if plan_limita_atinsa|default(false) or plan_warn|default(false) %}
&nbsp;<a href="/?tab=cont" style="font-size:var(--fs-xs); font-weight:400; color:var(--accent);">Detalii plan</a>
{% endif %}
</div>
{% endif %}
</div>