Promote develop → main (2026-07-25 22:22 UTC) #16
@@ -928,47 +928,75 @@ input[type="date"].filter-select { padding: 6px 10px; }
|
|||||||
.health-run-item { display: inline-flex; align-items: center; gap: 8px; }
|
.health-run-item { display: inline-flex; align-items: center; gap: 8px; }
|
||||||
.health-run-item #healthRunInfo { font-size: 12px; }
|
.health-run-item #healthRunInfo { font-size: 12px; }
|
||||||
|
|
||||||
/* Checkbox-Filterleisten (Standard fuer alle Filter im Portal) */
|
/* Checkbox-Dropdowns (Standard fuer alle Filter im Portal) */
|
||||||
.filter-bar {
|
.filter-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: flex-start;
|
||||||
gap: 4px;
|
gap: 8px;
|
||||||
padding: 10px 14px;
|
flex-wrap: wrap;
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
.filter-dd { position: relative; }
|
||||||
|
.filter-dd-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 7px 12px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.filter-dd-btn:hover { background: var(--bg-tertiary); }
|
||||||
|
.filter-dd-btn.has-selection { border-color: var(--accent); }
|
||||||
|
.filter-dd-count {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #1A1A1A; /* Text auf Gold immer dunkel (ON_ACCENT) */
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0 6px;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
.filter-dd-caret { font-size: 10px; color: var(--text-secondary); }
|
||||||
|
.filter-dd-panel {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 4px);
|
||||||
|
left: 0;
|
||||||
|
z-index: 60;
|
||||||
|
min-width: 210px;
|
||||||
|
max-height: 320px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px;
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
}
|
}
|
||||||
.filter-group {
|
.filter-dd.open .filter-dd-panel { display: flex; }
|
||||||
display: flex;
|
[data-theme="light"] .filter-dd-panel { box-shadow: 0 8px 24px rgba(26, 26, 26, 0.18); }
|
||||||
align-items: baseline;
|
|
||||||
gap: 4px 10px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
.filter-group-label {
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.4px;
|
|
||||||
min-width: 92px;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
.filter-check {
|
.filter-check {
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 7px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 2px 7px;
|
padding: 4px 8px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.filter-check:hover { background: var(--bg-tertiary); }
|
.filter-check:hover { background: var(--bg-tertiary); }
|
||||||
.filter-check input {
|
.filter-check input {
|
||||||
accent-color: var(--accent);
|
accent-color: var(--accent);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Checkbox-Spalte: nur sichtbar bei Herkunfts-Filter "Kundenquellen" */
|
/* Checkbox-Spalte: nur sichtbar bei Herkunfts-Filter "Kundenquellen" */
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>AegisSight Monitor-Verwaltung</title>
|
<title>AegisSight Monitor-Verwaltung</title>
|
||||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
||||||
<link rel="apple-touch-icon" href="/static/favicon.svg">
|
<link rel="apple-touch-icon" href="/static/favicon.svg">
|
||||||
<link rel="stylesheet" href="/static/css/style.css?v=20260725k">
|
<link rel="stylesheet" href="/static/css/style.css?v=20260725l">
|
||||||
<script>(function(){var t=localStorage.getItem('portal_theme');if(t==='light')document.documentElement.setAttribute('data-theme','light');try{var a=JSON.parse(localStorage.getItem('osint_a11y')||'{}');Object.keys(a).forEach(function(k){if(a[k])document.documentElement.setAttribute('data-a11y-'+k,'true');});}catch(e){}})()</script>
|
<script>(function(){var t=localStorage.getItem('portal_theme');if(t==='light')document.documentElement.setAttribute('data-theme','light');try{var a=JSON.parse(localStorage.getItem('osint_a11y')||'{}');Object.keys(a).forEach(function(k){if(a[k])document.documentElement.setAttribute('data-a11y-'+k,'true');});}catch(e){}})()</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -970,7 +970,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/a11y.js?v=20260725a"></script>
|
<script src="/static/js/a11y.js?v=20260725a"></script>
|
||||||
<script src="/static/js/app.js?v=20260725t"></script>
|
<script src="/static/js/app.js?v=20260725u"></script>
|
||||||
<script src="/static/js/sources.js?v=20260725e"></script>
|
<script src="/static/js/sources.js?v=20260725e"></script>
|
||||||
<script src="/static/js/aufgaben.js?v=20260725b"></script>
|
<script src="/static/js/aufgaben.js?v=20260725b"></script>
|
||||||
<script src="/static/js/x-scraper.js?v=20260522a"></script>
|
<script src="/static/js/x-scraper.js?v=20260522a"></script>
|
||||||
|
|||||||
@@ -1375,18 +1375,30 @@ function populateSelect(el, items, allLabel) {
|
|||||||
if (current && items.some(it => it.key === current)) el.value = current;
|
if (current && items.some(it => it.key === current)) el.value = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checkbox-Filtergruppe (Standard für alle Filter im Portal, statt Dropdowns).
|
// Checkbox-Dropdown (Standard für alle Filter im Portal). Ein Knopf mit dem
|
||||||
|
// Filternamen klappt eine Liste von Checkboxen auf (Mehrfachauswahl).
|
||||||
// selected ist ein Set mit den angehakten Keys. Nichts angehakt = keine
|
// selected ist ein Set mit den angehakten Keys. Nichts angehakt = keine
|
||||||
// Einschränkung. onChange wird nach jedem Toggle aufgerufen.
|
// Einschränkung. onChange wird nach jedem Toggle aufgerufen, das Panel
|
||||||
|
// bleibt dabei offen.
|
||||||
function renderFilterGroup(container, title, items, selected, onChange) {
|
function renderFilterGroup(container, title, items, selected, onChange) {
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
const wrap = document.createElement("div");
|
const wrap = document.createElement("div");
|
||||||
wrap.className = "filter-group";
|
wrap.className = "filter-dd";
|
||||||
const lbl = document.createElement("span");
|
|
||||||
lbl.className = "filter-group-label";
|
const btn = document.createElement("button");
|
||||||
lbl.textContent = title;
|
btn.type = "button";
|
||||||
lbl.title = "Nichts angehakt = alle";
|
btn.className = "filter-dd-btn";
|
||||||
wrap.appendChild(lbl);
|
btn.title = "Nichts angehakt = alle";
|
||||||
|
const updateBtn = () => {
|
||||||
|
btn.innerHTML = esc(title)
|
||||||
|
+ (selected.size ? ` <span class="filter-dd-count">${selected.size}</span>` : "")
|
||||||
|
+ ' <span class="filter-dd-caret">▾</span>';
|
||||||
|
btn.classList.toggle("has-selection", selected.size > 0);
|
||||||
|
};
|
||||||
|
updateBtn();
|
||||||
|
|
||||||
|
const panel = document.createElement("div");
|
||||||
|
panel.className = "filter-dd-panel";
|
||||||
(items || []).forEach((it) => {
|
(items || []).forEach((it) => {
|
||||||
const label = document.createElement("label");
|
const label = document.createElement("label");
|
||||||
label.className = "filter-check";
|
label.className = "filter-check";
|
||||||
@@ -1396,15 +1408,41 @@ function renderFilterGroup(container, title, items, selected, onChange) {
|
|||||||
cb.addEventListener("change", () => {
|
cb.addEventListener("change", () => {
|
||||||
if (cb.checked) selected.add(it.key);
|
if (cb.checked) selected.add(it.key);
|
||||||
else selected.delete(it.key);
|
else selected.delete(it.key);
|
||||||
|
updateBtn();
|
||||||
onChange();
|
onChange();
|
||||||
});
|
});
|
||||||
label.appendChild(cb);
|
label.appendChild(cb);
|
||||||
label.appendChild(document.createTextNode(it.label));
|
label.appendChild(document.createTextNode(it.label));
|
||||||
wrap.appendChild(label);
|
panel.appendChild(label);
|
||||||
});
|
});
|
||||||
|
if (!(items || []).length) {
|
||||||
|
const empty = document.createElement("div");
|
||||||
|
empty.className = "text-muted";
|
||||||
|
empty.style.fontSize = "12px";
|
||||||
|
empty.textContent = "Keine Werte vorhanden";
|
||||||
|
panel.appendChild(empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.addEventListener("click", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const wasOpen = wrap.classList.contains("open");
|
||||||
|
closeAllFilterDropdowns();
|
||||||
|
if (!wasOpen) wrap.classList.add("open");
|
||||||
|
});
|
||||||
|
panel.addEventListener("click", (e) => e.stopPropagation());
|
||||||
|
|
||||||
|
wrap.appendChild(btn);
|
||||||
|
wrap.appendChild(panel);
|
||||||
container.appendChild(wrap);
|
container.appendChild(wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeAllFilterDropdowns() {
|
||||||
|
document.querySelectorAll(".filter-dd.open").forEach((el) => el.classList.remove("open"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Klick irgendwo anders schließt offene Filter-Dropdowns
|
||||||
|
document.addEventListener("click", () => closeAllFilterDropdowns());
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
// Beim Page-Load Meta einmalig laden (asynchron, blockiert nicht)
|
// Beim Page-Load Meta einmalig laden (asynchron, blockiert nicht)
|
||||||
if (window.API && (localStorage.getItem("token") || window.location.pathname === "/dashboard")) {
|
if (window.API && (localStorage.getItem("token") || window.location.pathname === "/dashboard")) {
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren