fix(5.11): tabel trimiteri stabil — bug status=None, pills in bara de filtre, nudge "Date noi" in loc de poll 15s, logo ROMFAST marit
- Fix bug: campul hidden de filtru randa literal "None" (status_filtru None +
Jinja default('')) -> poll-ul trimitea status=None -> tabel gol. status or "".
- Pills de stare mutate din bara de status in bara de filtre (filtreazaStare scrie
campul hidden + re-trimite form-ul; filtrul persista la reincarcari). Re-randate
OOB cu contoare proaspete la fiecare reincarcare a tabelului.
- Polling redesign: tabelul nu se mai reincarca singur (fara every 15s). Poller usor
JSON (/_fragments/trimiteri-versiune) detecteaza schimbari -> nudge "Date noi —
Reincarca". Reincarcarea (nudge / actiune) pastreaza filtrul+pagina. Scroll/selectia
nu se mai pierd. Poll-guard eliminat (nu mai exista poll periodic de pauzat).
- Logo ROMFAST 32px -> 60px (ca pe romfast.ro), header min-height 92px, 44px pe mobil.
Regresie: 896 passed, 1 deselected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,7 @@ from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, File, Form, HTTPException, Request, UploadFile
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from .. import __version__
|
||||
@@ -113,6 +113,18 @@ def _status_counts(conn, account_id: int) -> dict[str, int]:
|
||||
return {r["status"]: int(r["n"]) for r in rows}
|
||||
|
||||
|
||||
def _trimiteri_versiune(conn, account_id: int) -> str:
|
||||
"""Semnatura ieftina a starii trimiterilor contului: numar randuri + cel mai recent
|
||||
updated_at. Se schimba la orice insert/update/delete -> nudge-ul "Date noi" o compara
|
||||
fara a re-randa tabelul."""
|
||||
row = conn.execute(
|
||||
"SELECT COUNT(*) AS n, COALESCE(MAX(updated_at), '') AS m FROM submissions "
|
||||
"WHERE (account_id = ? OR (? = 1 AND account_id IS NULL))",
|
||||
(account_id, account_id),
|
||||
).fetchone()
|
||||
return f"{row['n']}:{row['m']}"
|
||||
|
||||
|
||||
def _account_active(conn, account_id: int) -> bool:
|
||||
"""True daca contul e activ (sau legacy cu NULL/absent active)."""
|
||||
row = conn.execute("SELECT active FROM accounts WHERE id=?", (account_id,)).fetchone()
|
||||
@@ -196,6 +208,10 @@ def _get_acasa_context(request: Request, conn, account_id: int) -> dict:
|
||||
"are_trimiteri": are_trimiteri,
|
||||
"are_cheie_folosita": are_cheie_folosita,
|
||||
"blocate_total": blocate_total,
|
||||
# Pill-uri de filtrare a starii, randate in bara de filtre (nu in bara de status).
|
||||
"pills_categorii": _pills_categorii(counts),
|
||||
# Semnatura datelor: nudge-ul "Date noi" o compara la fiecare poll usor.
|
||||
"versiune_trimiteri": _trimiteri_versiune(conn, account_id),
|
||||
# US-002: Acasa include caseta de upload -> are nevoie de csrf_token
|
||||
"csrf_token": get_csrf_token(request),
|
||||
}
|
||||
@@ -212,7 +228,9 @@ def _render_panel_acasa(request: Request, conn=None, account_id: int = 1, status
|
||||
{"request": request, "csrf_token": get_csrf_token(request)}
|
||||
)
|
||||
ctx = _get_acasa_context(request, conn, account_id)
|
||||
ctx["status_filtru"] = status
|
||||
# `status or ""`: campul hidden de filtru ar randa literal "None" cu un None Python
|
||||
# (Jinja `default('')` inlocuieste doar undefined), trimitand status=None la poll.
|
||||
ctx["status_filtru"] = status or ""
|
||||
return templates.get_template("_acasa.html").render(ctx)
|
||||
|
||||
|
||||
@@ -621,6 +639,19 @@ def fragment_status(request: Request) -> HTMLResponse:
|
||||
conn.close()
|
||||
|
||||
|
||||
@router.get("/_fragments/trimiteri-versiune", response_class=JSONResponse)
|
||||
def fragment_trimiteri_versiune(request: Request) -> JSONResponse:
|
||||
"""Semnatura curenta a trimiterilor contului (JSON usor). Pollerul "Date noi" o
|
||||
compara cu versiunea cu care s-a randat tabelul; daca difera, arata nudge-ul de
|
||||
reincarcare — tabelul nu se mai schimba singur."""
|
||||
account_id = require_login(request)
|
||||
conn = get_connection()
|
||||
try:
|
||||
return JSONResponse({"v": _trimiteri_versiune(conn, account_id)})
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _iso_date_prefix(value: object) -> str | None:
|
||||
"""Intoarce primele 10 caractere (YYYY-MM-DD) daca incep cu o data ISO valida, altfel None.
|
||||
|
||||
@@ -795,6 +826,10 @@ def fragment_submissions(
|
||||
"f_vehicul": vehicul_q or "",
|
||||
"f_data_de": data_de or "",
|
||||
"f_data_pana": data_pana or "",
|
||||
# Pill-uri (OOB) + stare activa + versiune pentru nudge-ul "Date noi".
|
||||
"pills_categorii": _pills_categorii(_status_counts(conn, account_id)),
|
||||
"status_filtru": status or "",
|
||||
"versiune_trimiteri": _trimiteri_versiune(conn, account_id),
|
||||
})
|
||||
finally:
|
||||
conn.close()
|
||||
@@ -823,6 +858,9 @@ def _render_submissions(request: Request, conn, account_id: int) -> HTMLResponse
|
||||
"rows": view,
|
||||
"filtru_activ": False,
|
||||
"csrf_token": get_csrf_token(request),
|
||||
"pills_categorii": _pills_categorii(_status_counts(conn, account_id)),
|
||||
"status_filtru": "",
|
||||
"versiune_trimiteri": _trimiteri_versiune(conn, account_id),
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -21,19 +21,16 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Filtre (US-009): reincarca tabelul; poll-ul re-trimite filtrul curent prin hx-include -->
|
||||
<!-- Bara de filtre: vehicul/data + pill-uri de stare pe acelasi rand. Pill-urile scriu
|
||||
campul hidden status si re-trimit form-ul (filtreazaStare) -> filtrul persista la reincarcari. -->
|
||||
<form id="filtre-trimiteri"
|
||||
hx-get="/_fragments/submissions"
|
||||
hx-target="#submissions-wrap"
|
||||
hx-swap="innerHTML"
|
||||
hx-trigger="submit, change, keyup delay:400ms from:input[name='vehicul']"
|
||||
style="display:flex; gap:10px; flex-wrap:wrap; align-items:flex-end; margin-bottom:12px;">
|
||||
{# US-003 (PRD 5.10): dropdown status eliminat — inlocuit cu pill-uri in bara de status.
|
||||
Filtrul de stare vine de la pill-uri (/_fragments/submissions?status=X direct).
|
||||
Camp hidden permite reset stare la submit manual din form (Filtreaza). #}
|
||||
<input type="hidden" name="status" value="{{ status_filtru | default('') }}">
|
||||
{# US-004 (PRD 5.10): pagina curenta — actualizata prin OOB swap din _submissions.html.
|
||||
Poll-ul (hx-include="#filtre-trimiteri") include automat pagina curenta (L2 PRD). #}
|
||||
style="display:flex; gap:10px 14px; flex-wrap:wrap; align-items:flex-end; margin-bottom:12px;">
|
||||
<input type="hidden" id="f-status" name="status" value="{{ status_filtru | default('', true) }}">
|
||||
{# Pagina curenta — actualizata prin OOB swap din _submissions.html; inclusa la reincarcari. #}
|
||||
<input type="hidden" id="f-page" name="page" value="1">
|
||||
<div>
|
||||
<label for="f-vehicul" class="muted" style="display:block; font-size:12px;">Vehicul (nr/VIN)</label>
|
||||
@@ -48,12 +45,24 @@
|
||||
<input id="f-data-pana" type="date" name="data_pana">
|
||||
</div>
|
||||
<button type="submit">Filtreaza</button>
|
||||
{# Pill-uri de stare pe acelasi rand cu filtrele; re-randate prin OOB la reincarcarea tabelului. #}
|
||||
<span id="pills-categorii" class="pills-categorii" style="margin-left:auto;">
|
||||
{% include '_pills.html' %}
|
||||
</span>
|
||||
</form>
|
||||
|
||||
<!-- Poll aliniat la 15s ca status-ul (M5: nu doua timere perpetue pe pagina mereu deschisa) -->
|
||||
<!-- Nudge "Date noi": tabelul nu se reimprospateaza singur; bannerul apare doar cand
|
||||
pollerul usor detecteaza schimbari, iar utilizatorul reincarca cand vrea. -->
|
||||
<div id="nudge-trimiteri" hidden role="status" aria-live="polite">
|
||||
<span>Sunt trimiteri actualizate.</span>
|
||||
<button type="button" onclick="reincarcaTrimiteri()">Reincarca</button>
|
||||
</div>
|
||||
|
||||
<!-- Tabelul se reincarca DOAR la: incarcarea paginii, actiunile tale (trimiteriChanged)
|
||||
sau apasarea pe Reincarca (reincarcaTrimiteri). Fara poll periodic care sa-l reseteze. -->
|
||||
<div id="submissions-wrap"
|
||||
hx-get="/_fragments/submissions"
|
||||
hx-trigger="load, every 15s, trimiteriChanged from:body"
|
||||
hx-trigger="load, trimiteriChanged from:body, reincarcaTrimiteri"
|
||||
hx-include="#filtre-trimiteri" hx-swap="innerHTML">
|
||||
<div class="empty">se incarca…</div>
|
||||
</div>
|
||||
|
||||
15
app/web/templates/_pills.html
Normal file
15
app/web/templates/_pills.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{# Pill-uri de filtrare a starii, randate in bara de filtre (_coada.html) si re-randate
|
||||
prin OOB la fiecare reincarcare a tabelului (_submissions.html). Stare activa =
|
||||
status_filtru. "Toate" reseteaza filtrul; categoriile apar doar cand au n>0. #}
|
||||
<button type="button" class="pill-cat pill-cat-reset" data-status=""
|
||||
aria-pressed="{{ 'true' if not status_filtru else 'false' }}"
|
||||
onclick="filtreazaStare(this, '')">Toate</button>
|
||||
{% for pill in pills_categorii %}
|
||||
<button type="button" class="pill-cat" data-status="{{ pill.status }}"
|
||||
aria-pressed="{{ 'true' if status_filtru == pill.status else 'false' }}"
|
||||
style="color:var({{ pill.color_var }}); border-color:var({{ pill.color_var }});"
|
||||
onclick="filtreazaStare(this, '{{ pill.status }}')">
|
||||
{{ pill.label }}
|
||||
<span class="pill-cat-n" style="background:var({{ pill.color_var }});">{{ pill.n }}</span>
|
||||
</button>
|
||||
{% endfor %}
|
||||
@@ -1,15 +1,3 @@
|
||||
<style>
|
||||
/* Pill-uri categorii blocate (US-003 PRD 5.10)
|
||||
Culoarea e injectata inline (color_var: --warn / --err) dupa DESIGN.md:
|
||||
Lipsa cod = --warn (chihlimbar), Date incomplete + Eroare = --err (rosu).
|
||||
Activ = fundal pe culoarea categoriei (NU accent albastru — S1/A5). */
|
||||
.pill-cat { transition: background 0.15s, color 0.15s; }
|
||||
.pill-cat:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
||||
/* Activ: background = culoarea categoriei (currentColor = var(--err/--warn) din inline style),
|
||||
text = var(--card) (contrast AA). NU accent albastru (S1/A5 DESIGN.md). */
|
||||
.pill-cat[aria-pressed="true"] { background: currentColor !important; color: var(--card) !important; border-color: currentColor !important; }
|
||||
.pill-cat[aria-pressed="true"] span { background: var(--card) !important; color: currentColor; }
|
||||
</style>
|
||||
<div id="status-bar" class="status-bar card"
|
||||
hx-get="/_fragments/status"
|
||||
hx-trigger="every 15s"
|
||||
@@ -59,51 +47,6 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Pill-uri categorii blocate (US-003 PRD 5.10): inlocuiesc lista de ID-uri.
|
||||
<button> reale cu aria-pressed, focalizabile, activare Enter/Space.
|
||||
Inactiv = contur+text pe culoarea categoriei; activ = umplere pe culoarea categoriei.
|
||||
Pill ascuns cand n=0 (lista pills_categorii filtreaza deja). -->
|
||||
{% if pills_categorii %}
|
||||
<div style="margin-top:12px; padding-top:10px; border-top:1px solid var(--line);">
|
||||
<div style="display:flex; gap:8px; flex-wrap:wrap; align-items:center;">
|
||||
<span style="font-size:12px; color:var(--muted);">Necesita atentie:</span>
|
||||
{% for pill in pills_categorii %}
|
||||
<button type="button"
|
||||
class="pill-cat"
|
||||
aria-pressed="false"
|
||||
hx-get="/_fragments/submissions?status={{ pill.status }}"
|
||||
hx-target="#submissions-wrap"
|
||||
hx-swap="innerHTML"
|
||||
onclick="(function(b){
|
||||
var pressed=b.getAttribute('aria-pressed')==='true';
|
||||
document.querySelectorAll('.pill-cat').forEach(function(x){x.setAttribute('aria-pressed','false');});
|
||||
if(!pressed){b.setAttribute('aria-pressed','true');}
|
||||
var s=document.getElementById('trimiteri-section');
|
||||
if(s){s.scrollIntoView({behavior:'smooth'});}
|
||||
})(this)"
|
||||
style="display:inline-flex; align-items:center; gap:5px;
|
||||
padding:3px 10px; border-radius:99px; font-size:12px; font-weight:600;
|
||||
cursor:pointer; border:1.5px solid var({{ pill.color_var }}); color:var({{ pill.color_var }});
|
||||
background:transparent; transition:background 0.15s, color 0.15s;">
|
||||
{{ pill.label }}
|
||||
<span style="font-size:11px; font-weight:700; background:var({{ pill.color_var }}); color:var(--card);
|
||||
padding:0 5px; border-radius:99px; min-width:18px; text-align:center;">{{ pill.n }}</span>
|
||||
</button>
|
||||
{% endfor %}
|
||||
{# Buton "Toate" — reseteaza filtrul de categorie #}
|
||||
<button type="button"
|
||||
class="pill-cat-reset"
|
||||
aria-label="Arata toate trimiterile"
|
||||
hx-get="/_fragments/submissions"
|
||||
hx-target="#submissions-wrap"
|
||||
hx-swap="innerHTML"
|
||||
onclick="document.querySelectorAll('.pill-cat').forEach(function(x){x.setAttribute('aria-pressed','false');})"
|
||||
style="padding:3px 10px; border-radius:99px; font-size:12px; cursor:pointer;
|
||||
border:1px solid var(--line); background:transparent; color:var(--muted);">
|
||||
Toate
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{# Pill-urile de stare s-au mutat in bara de filtre din sectiunea Trimiteri (_coada.html). #}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,13 @@
|
||||
#}
|
||||
<input type="hidden" id="f-page" name="page" value="{{ page | default(1) }}" hx-swap-oob="true">
|
||||
|
||||
{# OOB: re-randeaza pill-urile de stare (in bara de filtre, in afara #submissions-wrap) cu
|
||||
contoarele si starea activa proaspete la fiecare reincarcare a tabelului. #}
|
||||
<span hx-swap-oob="innerHTML:#pills-categorii">{% include '_pills.html' %}</span>
|
||||
|
||||
{# Versiunea datelor cu care s-a randat tabelul; pollerul "Date noi" o compara. #}
|
||||
<span id="trimiteri-versiune" data-v="{{ versiune_trimiteri | default('') }}" hidden></span>
|
||||
|
||||
{% if rows %}
|
||||
{# US-011: form de stergere bulk. Selectia opereaza DOAR pe randuri blocate
|
||||
(gestionabil); sent/sending/queued nu au checkbox (read-only). #}
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
background:var(--bg); color:var(--ink); -webkit-font-smoothing:antialiased; }
|
||||
/* US-012c (PRD 5.10): grila 3 coloane — stanga (logo ROMFAST) | centru (titlu+env) | dreapta (controale). */
|
||||
header { padding:16px 24px; border-bottom:1px solid var(--line);
|
||||
display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:8px; }
|
||||
display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:8px; min-height:92px; }
|
||||
.header-left { display:flex; align-items:center; }
|
||||
.header-center { display:flex; flex-direction:column; align-items:center; text-align:center; }
|
||||
.header-right { display:flex; align-items:center; justify-content:flex-end; gap:8px; }
|
||||
@@ -131,7 +131,8 @@
|
||||
32px inaltime — usor mai mare decat in header-center (28px) pentru vizibilitate ca brand anchor.
|
||||
margin:0 — aliniat stanga, NU centrat (era `margin:3px auto 0` cand era sub titlu).
|
||||
Logo transparent: ok pe dark/light/petrol fara filtre de culoare. */
|
||||
.brand-logo { height:32px; width:auto; display:block; margin:0; }
|
||||
/* Logo ROMFAST la dimensiunea de pe romfast.ro (~60px inaltime), aliniat stanga. */
|
||||
.brand-logo { height:60px; width:auto; display:block; margin:0; }
|
||||
/* Env badge mic sub titlu in header-center (US-012c): nu mai echilibreaza optic dreapta
|
||||
(logo-ul face asta), ci identifica mediul langa titlu. Pastrat mic, color:var(--muted). */
|
||||
.header-center .env { font-size:11px; margin-top:2px; }
|
||||
@@ -149,6 +150,28 @@
|
||||
th { color:var(--muted); font-weight:500; font-size:12px; text-transform:uppercase; letter-spacing:.04em; }
|
||||
.empty { color:var(--muted); padding:24px; text-align:center; }
|
||||
.pill { font-size:12px; padding:2px 8px; border-radius:99px; border:1px solid var(--line); }
|
||||
/* Pill-uri de filtrare a starii (bara de filtre Trimiteri). Inactiv = contur+text pe
|
||||
culoarea categoriei (injectata inline); activ = umplere pe acea culoare. */
|
||||
.pills-categorii { display:inline-flex; gap:8px; flex-wrap:wrap; align-items:center; }
|
||||
.pill-cat { display:inline-flex; align-items:center; gap:5px; padding:4px 11px; border-radius:99px;
|
||||
font-size:12px; font-weight:600; cursor:pointer; background:transparent;
|
||||
border:1.5px solid var(--line); color:var(--muted); min-height:30px;
|
||||
transition:background .15s, color .15s; }
|
||||
.pill-cat:hover { filter:brightness(1.1); }
|
||||
.pill-cat:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
|
||||
.pill-cat-n { font-size:11px; font-weight:700; color:var(--card); padding:0 5px;
|
||||
border-radius:99px; min-width:18px; text-align:center; }
|
||||
.pill-cat[aria-pressed="true"] { background:currentColor; color:var(--card); border-color:currentColor; }
|
||||
.pill-cat[aria-pressed="true"] .pill-cat-n { background:var(--card) !important; color:currentColor; }
|
||||
.pill-cat-reset[aria-pressed="true"] { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||
/* Nudge "Date noi": apare doar cand pollerul usor detecteaza schimbari; tabelul nu se
|
||||
schimba singur niciodata, utilizatorul reincarca cand vrea. */
|
||||
#nudge-trimiteri { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin:0 0 12px;
|
||||
padding:8px 12px; border-radius:8px; font-size:13px;
|
||||
border:1px solid var(--accent);
|
||||
background:color-mix(in srgb, var(--accent) 12%, var(--card)); }
|
||||
#nudge-trimiteri[hidden] { display:none; }
|
||||
#nudge-trimiteri button { font-size:13px; padding:5px 12px; min-height:32px; }
|
||||
.s-queued{color:var(--accent);} .s-sending{color:var(--warn);} .s-sent{color:var(--ok);}
|
||||
.s-error,.s-needs_data,.s-needs_mapping{color:var(--err);}
|
||||
.s-ok{color:var(--ok);}
|
||||
@@ -349,7 +372,8 @@
|
||||
/* Header + nav colapsate: pe mobil trece de la grid la flex wrap.
|
||||
Randul 1: [logo ROMFAST stanga] [controale dreapta] (margin-left:auto pe .header-right).
|
||||
Randul 2: [titlu + env mic centrat, full-width]. Fara scroll orizontal, tinte >=44px. */
|
||||
header { display:flex; flex-wrap:wrap; padding:12px 16px; gap:8px; align-items:center; }
|
||||
header { display:flex; flex-wrap:wrap; padding:12px 16px; gap:8px; align-items:center; min-height:0; }
|
||||
.brand-logo { height:44px; }
|
||||
.header-left { order:0; flex:0 0 auto; }
|
||||
.header-center { order:2; width:100%; text-align:center; }
|
||||
.header-right { order:1; margin-left:auto; flex:0 0 auto; }
|
||||
@@ -789,45 +813,49 @@
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
// Poll-guard (PRD 5.9 US-005, R6). Inlocuieste vechea pauza pe „rand expandat" (5.8):
|
||||
// randul-sibling de detaliu nu mai exista (US-003 l-a mutat in modalul global, care
|
||||
// traieste in afara #submissions-wrap -> un swap de poll nu-l atinge). Aici oprim
|
||||
// poll-ul de 15s de a REINCARCA lista cat timp (a) modalul e deschis SAU (b) exista
|
||||
// cel putin un checkbox de bulk bifat — altfel modalul s-ar reseta / bifele s-ar sterge.
|
||||
//
|
||||
// CRITIC (F5): blocam DOAR trigger-ul periodic. In htmx `load`/`every 15s` declanseaza
|
||||
// requestul FARA `triggeringEvent`; `trimiteriChanged` (HX-Trigger dupa corectie/stergere)
|
||||
// si submit-ul/filtrul AU `triggeringEvent` -> TREC MEREU. Asa evitam blocajul permanent:
|
||||
// daca randul bifat paraseste filtrul, pauza nu ramane lipita (pauza e legata strict de
|
||||
// trigger-ul periodic, nu de o stare „sticky"). Anularea unui `htmx:beforeRequest` NU
|
||||
// opreste timer-ul htmx (se reprogrameaza singur) -> poll-ul reia automat la urmatorul
|
||||
// tic cand ambele conditii dispar; nu se pierde scroll, focus sau selectia de bife.
|
||||
// Filtrare stare prin pill-uri + reincarcare manuala a tabelului. Tabelul NU se mai
|
||||
// schimba singur (fara poll periodic pe #submissions-wrap): un poller usor verifica
|
||||
// doar versiunea datelor si arata nudge-ul "Date noi" cand difera. Reincarcarea
|
||||
// (pill, nudge sau actiune) trece prin form -> pastreaza filtrul/pagina curenta.
|
||||
(function() {
|
||||
function modalDeschis() {
|
||||
var o = document.getElementById('modal-detaliu');
|
||||
return !!(o && !o.hidden);
|
||||
// Pill de stare: scrie campul hidden, reseteaza pagina la 1 si re-trimite filtrul.
|
||||
window.filtreazaStare = function(btn, status) {
|
||||
var form = document.getElementById('filtre-trimiteri');
|
||||
if (!form) return;
|
||||
var hs = document.getElementById('f-status'); if (hs) hs.value = status || '';
|
||||
var hp = document.getElementById('f-page'); if (hp) hp.value = '1';
|
||||
document.querySelectorAll('#pills-categorii .pill-cat').forEach(function(b) {
|
||||
b.setAttribute('aria-pressed', 'false');
|
||||
});
|
||||
if (btn) btn.setAttribute('aria-pressed', 'true');
|
||||
if (form.requestSubmit) form.requestSubmit(); else form.submit();
|
||||
};
|
||||
// Reincarca tabelul pastrand filtrul curent (hx-include #filtre-trimiteri) si ascunde nudge-ul.
|
||||
window.reincarcaTrimiteri = function() {
|
||||
var n = document.getElementById('nudge-trimiteri'); if (n) n.hidden = true;
|
||||
if (window.htmx) htmx.trigger('#submissions-wrap', 'reincarcaTrimiteri');
|
||||
};
|
||||
|
||||
// Poller "Date noi": compara versiunea datelor cu cea cu care s-a randat tabelul.
|
||||
// Daca difera, arata nudge-ul; daca nu, nu atinge nimic. JSON usor, fara re-render.
|
||||
var INTERVAL = 20000;
|
||||
function versiuneCurenta() {
|
||||
var e = document.getElementById('trimiteri-versiune');
|
||||
return e ? e.getAttribute('data-v') : null;
|
||||
}
|
||||
function existaBifa() {
|
||||
return !!document.querySelector('#submissions-wrap input[name="submission_id"]:checked');
|
||||
function verifica() {
|
||||
if (versiuneCurenta() === null) return; // tabelul nu e pe ecran (alt tab)
|
||||
var nudge = document.getElementById('nudge-trimiteri');
|
||||
if (!nudge || !nudge.hidden) return; // deja afisat -> nu re-cere
|
||||
fetch('/_fragments/trimiteri-versiune', { headers: { 'X-Requested-With': 'fetch' } })
|
||||
.then(function(r) { return r.ok ? r.json() : null; })
|
||||
.then(function(d) {
|
||||
if (!d) return;
|
||||
if (d.v !== versiuneCurenta()) nudge.hidden = false;
|
||||
})
|
||||
.catch(function() {});
|
||||
}
|
||||
document.body.addEventListener('htmx:beforeRequest', function(evt) {
|
||||
var d = evt.detail || {};
|
||||
if (!d.elt || d.elt.id !== 'submissions-wrap') return; // doar poll-ul listei
|
||||
var rc = d.requestConfig || {};
|
||||
if (rc.triggeringEvent) return; // trimiteriChanged / filtru: TREC MEREU
|
||||
if (modalDeschis() || existaBifa()) evt.preventDefault(); // pauza scopata pe periodic
|
||||
});
|
||||
// Resume pe checkbox `change`->gol: delegare pe body ca sa prinda si checkbox-urile
|
||||
// randate dupa swap. Cand modalul e inchis si nu mai exista nicio bifa, fortam un
|
||||
// refresh imediat (nu mai asteptam ticul de 15s) prin `trimiteriChanged from:body`,
|
||||
// care pastreaza filtrul curent (hx-include #filtre-trimiteri) si trece de guard.
|
||||
document.body.addEventListener('change', function(evt) {
|
||||
var t = evt.target;
|
||||
if (!(t && t.name === 'submission_id')) return;
|
||||
if (!modalDeschis() && !existaBifa() && window.htmx) {
|
||||
htmx.trigger(document.body, 'trimiteriChanged');
|
||||
}
|
||||
});
|
||||
setInterval(verifica, INTERVAL);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user