fix(web): kebab anti-clipping partajat + panou admin redenumit + tabel mapari compact
- "Panou admin" -> "Conturi clienti" (titlu, antet, link meniu hamburger)
- Kebab actiuni mutat in component partajat (base.html) cu position:fixed
pozitionat din JS: .tablewrap{overflow-x:auto} inducea overflow-y:auto care
taia dropdown-ul pe ultimul rand (meniul admin nu se vedea). Sters CSS local.
- Mapari salvate: Salveaza/Sterge mutate in kebab (legate prin form=); coloana
"In coada" doar checkbox (macro autosend_toggle compact, semantica de prezenta
pastrata); select cod RAR limitat la 240px -> tabelul incape fara scroll.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,16 +13,15 @@
|
||||
Manual<->Auto peste checkbox, NU doua radio-uri. Zero atingere backend.
|
||||
- form_id: leaga input-ul de un <form> extern (necesar in celulele de tabel).
|
||||
- checked: starea STOCATA per mapare (H4) — bifat = Auto. #}
|
||||
{% macro autosend_toggle(form_id='', checked=True) -%}
|
||||
{% macro autosend_toggle(form_id='', checked=True, label='') -%}
|
||||
<label class="autosend-toggle"
|
||||
title="Auto = pune automat in coada la fisierele viitoare cu aceasta operatie. Manual = tine pentru verificare; nimic nu pleaca la RAR pana confirmi."
|
||||
style="display:inline-flex; align-items:center; gap:6px; white-space:nowrap; min-height:36px; cursor:pointer; font-size:13px;">
|
||||
<span class="muted">Manual</span>
|
||||
title="Bifat = Auto: pune automat in coada la fisierele viitoare cu aceasta operatie. Nebifat = Manual: tine pentru verificare; nimic nu pleaca la RAR pana confirmi."
|
||||
style="display:inline-flex; align-items:center; justify-content:center; gap:8px; min-height:36px; cursor:pointer;">
|
||||
{%- if label %}<span class="muted" style="font-size:13px;">{{ label }}</span>{% endif %}
|
||||
<input type="checkbox" name="auto_send" value="true"
|
||||
{%- if form_id %} form="{{ form_id }}"{% endif %}
|
||||
{%- if checked %} checked{% endif %}
|
||||
aria-label="In coada: Auto (bifat) sau Manual (nebifat), pentru aceasta operatie"
|
||||
style="width:32px; height:18px; cursor:pointer; accent-color:var(--accent);">
|
||||
<span><strong>Auto</strong></span>
|
||||
aria-label="In coada automat (Auto) pentru aceasta operatie"
|
||||
style="width:18px; height:18px; cursor:pointer; accent-color:var(--accent);">
|
||||
</label>
|
||||
{%- endmacro %}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{% import '_macros.html' as ui %}
|
||||
<div id="mapari-section">
|
||||
<style>
|
||||
/* Selectul de cod RAR e principalul vinovat de latimea tabelelor de mapari. Il limitam ca
|
||||
tabelul sa incapa in card fara scroll orizontal -> coloana Actiuni (kebab) ramane vizibila. */
|
||||
#mapari-section td select { width:100%; max-width:240px; min-width:150px; }
|
||||
</style>
|
||||
|
||||
{% if message %}
|
||||
<div class="flash" style="margin-bottom:12px;">{{ message }}</div>
|
||||
@@ -139,12 +144,16 @@
|
||||
<td>
|
||||
{{ ui.autosend_toggle(form_id="map-salv-" ~ loop.index, checked=m.auto_send) }}
|
||||
</td>
|
||||
<td style="white-space:nowrap;">
|
||||
<button type="submit" form="map-salv-{{ loop.index }}">Salveaza</button>
|
||||
<button type="submit" form="map-del-{{ loop.index }}"
|
||||
style="background:var(--card); color:var(--err); border-color:var(--err);">
|
||||
Sterge
|
||||
</button>
|
||||
<td style="text-align:right; white-space:nowrap;">
|
||||
{# Salveaza/Sterge in meniu contextual (kebab) — randul ramane ingust. Butoanele se
|
||||
leaga prin form= de cele doua form-uri hx-post definite in prima celula a randului. #}
|
||||
<details class="kebab">
|
||||
<summary aria-label="Actiuni pentru {{ m.cod_op_service }}">⋯</summary>
|
||||
<div class="kebab-menu">
|
||||
<button type="submit" form="map-salv-{{ loop.index }}">Salveaza</button>
|
||||
<button type="submit" form="map-del-{{ loop.index }}" class="danger">Sterge</button>
|
||||
</div>
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="mapcol">
|
||||
{{ ui.autosend_toggle(checked=True) }}
|
||||
{{ ui.autosend_toggle(checked=True, label="In coada automat") }}
|
||||
</div>
|
||||
<div class="mapcol">
|
||||
<button type="submit" style="min-height:44px;">Salveaza</button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Panou admin — Gateway RAR AUTOPASS{% endblock %}
|
||||
{% block title %}Conturi clienti — Gateway RAR AUTOPASS{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{# US-009 (5.5): metadate verbe de ciclu de viata (eticheta, ruta, clasa). #}
|
||||
@@ -50,8 +50,7 @@
|
||||
<td class="muted">{{ acct.created_at or "—" }}</td>
|
||||
<td style="white-space:nowrap;">
|
||||
<details class="kebab">
|
||||
<summary class="cardlink" style="list-style:none; cursor:pointer; display:inline-flex;
|
||||
padding:4px 10px;" aria-label="Actiuni pentru {{ acct.name }}">⋯</summary>
|
||||
<summary aria-label="Actiuni pentru {{ acct.name }}">⋯</summary>
|
||||
<div class="kebab-menu">
|
||||
{% for v in row_verbs %}
|
||||
{% set label, action, cls = VERBS[v] %}
|
||||
@@ -61,7 +60,7 @@
|
||||
{% if v == 'delete' %}onsubmit="return confirm('Stergi acest cont? (stergere soft)');"{% endif %}>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<input type="hidden" name="account_id" value="{{ acct.id }}">
|
||||
<button type="submit" {% if cls == 'danger' %}style="color:var(--err);"{% endif %}>{{ label }}</button>
|
||||
<button type="submit"{% if cls == 'danger' %} class="danger"{% endif %}>{{ label }}</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -85,22 +84,11 @@
|
||||
padding:8px 10px; border:1px solid var(--line); border-radius:8px;
|
||||
background:color-mix(in srgb, var(--accent) 8%, var(--card)); }
|
||||
.bulk-bar[hidden] { display:none; }
|
||||
/* Kebab per-rand (reuseaza estetica meniului de cont) */
|
||||
.kebab { position:relative; display:inline-block; }
|
||||
.kebab > summary::-webkit-details-marker { display:none; }
|
||||
.kebab-menu { position:absolute; right:0; top:calc(100% + 4px); min-width:140px; z-index:40;
|
||||
background:var(--card); border:1px solid var(--line); border-radius:8px; padding:6px;
|
||||
box-shadow:0 8px 24px rgba(0,0,0,.18); display:flex; flex-direction:column; gap:2px; }
|
||||
.kebab[open] > summary { background:var(--line); }
|
||||
.kebab-menu form { margin:0; }
|
||||
.kebab-menu button { display:block; width:100%; text-align:left; background:transparent; border:none;
|
||||
color:var(--ink); font:inherit; padding:7px 10px; border-radius:6px; cursor:pointer;
|
||||
min-height:36px; }
|
||||
.kebab-menu button:hover { background:var(--line); }
|
||||
/* Kebab per-rand: stiluri partajate in base.html (position:fixed, anti-clipping tablewrap). */
|
||||
</style>
|
||||
|
||||
<div style="display:flex;align-items:center;gap:16px;margin-bottom:20px;">
|
||||
<h2 style="margin:0;">Panou admin</h2>
|
||||
<h2 style="margin:0;">Conturi clienti</h2>
|
||||
<a href="/" class="cardlink muted">Inapoi la dashboard</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -132,6 +132,24 @@
|
||||
.cont-menu a:hover, .cont-menu button:hover { background:var(--line); }
|
||||
.cont-menu hr { border:none; border-top:1px solid var(--line); margin:4px 0; }
|
||||
.cont-menu form { margin:0; }
|
||||
/* Kebab partajat (actiuni per-rand in tabele). Meniul e position:fixed si pozitionat de JS:
|
||||
altfel `.tablewrap { overflow-x:auto }` induce overflow-y:auto si TAIE dropdown-ul pe ultimul
|
||||
rand (bug 5.5 — meniul nu se vedea). fixed scoate meniul din contextul de clipping al tabelului. */
|
||||
.kebab { position:relative; display:inline-block; }
|
||||
.kebab > summary { list-style:none; cursor:pointer; display:inline-flex; align-items:center;
|
||||
justify-content:center; min-height:32px; min-width:32px; padding:4px 10px;
|
||||
border-radius:6px; color:var(--ink); }
|
||||
.kebab > summary::-webkit-details-marker { display:none; }
|
||||
.kebab > summary:hover, .kebab[open] > summary { background:var(--line); }
|
||||
.kebab-menu { position:fixed; z-index:1000; min-width:160px; background:var(--card);
|
||||
border:1px solid var(--line); border-radius:8px; padding:6px;
|
||||
box-shadow:0 8px 24px rgba(0,0,0,.18); display:flex; flex-direction:column; gap:2px; }
|
||||
.kebab-menu form { margin:0; }
|
||||
.kebab-menu button, .kebab-menu a { display:block; width:100%; text-align:left; background:transparent;
|
||||
border:none; color:var(--ink); text-decoration:none; font:inherit; padding:7px 10px;
|
||||
border-radius:6px; cursor:pointer; min-height:36px; white-space:nowrap; }
|
||||
.kebab-menu button:hover, .kebab-menu a:hover { background:var(--line); }
|
||||
.kebab-menu button.danger { color:var(--err); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -154,7 +172,7 @@
|
||||
<a role="menuitem" href="/?tab=cont">Cont</a>
|
||||
<a role="menuitem" href="/?tab=integrare">Integrare</a>
|
||||
<a role="menuitem" href="/?tab=nomenclator">Nomenclator</a>
|
||||
{% if is_admin|default(false) %}<a role="menuitem" href="/admin">Panou admin</a>{% endif %}
|
||||
{% if is_admin|default(false) %}<a role="menuitem" href="/admin">Conturi clienti</a>{% endif %}
|
||||
<hr>
|
||||
<form method="post" action="/logout">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token|default('') }}">
|
||||
@@ -232,5 +250,50 @@
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
// Kebab partajat (actiuni per-rand). `<details class="kebab">` + `.kebab-menu` position:fixed.
|
||||
// Pozitionarea se face in JS la deschidere (eveniment `toggle`, captat pe document fiindca nu
|
||||
// bubble-uie), ancorat sub buton si aliniat la dreapta; flip in sus daca nu incape jos. Delegare
|
||||
// pe document → supravietuieste swap-urilor HTMX (#mapari-section se re-randeaza la fiecare salvare).
|
||||
(function() {
|
||||
function position(d) {
|
||||
var btn = d.querySelector('summary');
|
||||
var menu = d.querySelector('.kebab-menu');
|
||||
if (!btn || !menu) return;
|
||||
var r = btn.getBoundingClientRect();
|
||||
menu.style.visibility = 'hidden';
|
||||
var mw = menu.offsetWidth, mh = menu.offsetHeight;
|
||||
var left = Math.max(8, r.right - mw);
|
||||
var top = (r.bottom + mh > window.innerHeight - 8 && r.top - mh - 4 > 8)
|
||||
? r.top - mh - 4 : r.bottom + 4;
|
||||
menu.style.left = left + 'px';
|
||||
menu.style.top = top + 'px';
|
||||
menu.style.visibility = '';
|
||||
}
|
||||
function closeAll(except) {
|
||||
document.querySelectorAll('details.kebab[open]').forEach(function(d) {
|
||||
if (d !== except) d.removeAttribute('open');
|
||||
});
|
||||
}
|
||||
// `toggle` nu bubble-uie -> ascultam in faza de capturare pe document.
|
||||
document.addEventListener('toggle', function(e) {
|
||||
var d = e.target;
|
||||
if (!d.classList || !d.classList.contains('kebab')) return;
|
||||
if (d.open) { closeAll(d); position(d); }
|
||||
}, true);
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('details.kebab')) closeAll(null);
|
||||
});
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') closeAll(null);
|
||||
});
|
||||
// La scroll/resize repozitionam meniul deschis (position:fixed nu urmareste ancora singur).
|
||||
window.addEventListener('scroll', function() {
|
||||
var open = document.querySelector('details.kebab[open]');
|
||||
if (open) position(open);
|
||||
}, true);
|
||||
window.addEventListener('resize', function() { closeAll(null); });
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user