Promote develop → main (2026-07-25 22:22 UTC) #16
@@ -958,6 +958,65 @@ input[type="date"].filter-select { padding: 6px 10px; }
|
||||
.select-col { display: none; }
|
||||
table.show-select .select-col { display: table-cell; }
|
||||
|
||||
/* === Quellenliste: leichteres Seitwaerts-Scrollen === */
|
||||
/* Name links und Aktionen rechts bleiben beim Scrollen angepinnt.
|
||||
Zeilen sind transparent, angepinnte Zellen brauchen deshalb solide
|
||||
Hintergruende, sonst scheint der Inhalt darunter durch. */
|
||||
#unifiedTable th.sticky-name,
|
||||
#unifiedTable td.sticky-name {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
background: var(--bg-secondary);
|
||||
max-width: 260px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
box-shadow: 6px 0 8px -6px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
#unifiedTable th.sticky-actions,
|
||||
#unifiedTable td.sticky-actions {
|
||||
position: sticky;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
background: var(--bg-secondary);
|
||||
white-space: nowrap;
|
||||
box-shadow: -6px 0 8px -6px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
#unifiedTable.show-select th.select-col,
|
||||
#unifiedTable.show-select td.select-col {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
width: 40px;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
#unifiedTable.show-select th.sticky-name,
|
||||
#unifiedTable.show-select td.sticky-name { left: 40px; }
|
||||
#unifiedTable tr:hover td.sticky-name,
|
||||
#unifiedTable tr:hover td.sticky-actions,
|
||||
#unifiedTable.show-select tr:hover td.select-col {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
/* Kategorie-Zeile und Ausklapp-Inhalte bleiben im sichtbaren Bereich */
|
||||
.cat-header-sticky {
|
||||
position: sticky;
|
||||
left: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
#unifiedTable .src-notes-cell > div {
|
||||
position: sticky;
|
||||
left: 16px;
|
||||
max-width: 1080px;
|
||||
}
|
||||
/* Ziehen-zum-Scrollen */
|
||||
.drag-scroll { cursor: grab; }
|
||||
.drag-scroll.drag-scrolling {
|
||||
cursor: grabbing;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Health-Details in der Ausklapp-Zeile */
|
||||
.src-notes-text { margin-bottom: 8px; }
|
||||
.health-detail-head {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<title>AegisSight Monitor-Verwaltung</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
||||
<link rel="apple-touch-icon" href="/static/favicon.svg">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20260725m">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20260725n">
|
||||
<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>
|
||||
@@ -305,12 +305,12 @@
|
||||
</div>
|
||||
<div class="filter-bar" id="unifiedFilterBar"></div>
|
||||
<div class="card">
|
||||
<div class="table-wrap">
|
||||
<div class="table-wrap drag-scroll" id="unifiedTableWrap">
|
||||
<table id="unifiedTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="select-col"><input type="checkbox" id="unifiedSelectAll" onchange="toggleTenantSelectAll(this.checked)"></th>
|
||||
<th class="sortable" data-sort="name" onclick="sortUnifiedSources('name')">Name <span class="sort-icon"></span></th>
|
||||
<th class="sortable sticky-name" data-sort="name" onclick="sortUnifiedSources('name')">Name <span class="sort-icon"></span></th>
|
||||
<th>URL</th>
|
||||
<th class="sortable" data-sort="domain" onclick="sortUnifiedSources('domain')">Domain <span class="sort-icon"></span></th>
|
||||
<th class="sortable" data-sort="source_type" onclick="sortUnifiedSources('source_type')">Typ <span class="sort-icon"></span></th>
|
||||
@@ -322,7 +322,7 @@
|
||||
<th class="sortable" data-sort="health_status" onclick="sortUnifiedSources('health_status')">Health <span class="sort-icon"></span></th>
|
||||
<th class="sortable" data-sort="status" onclick="sortUnifiedSources('status')">Status <span class="sort-icon"></span></th>
|
||||
<th class="sortable" data-sort="org_name" onclick="sortUnifiedSources('org_name')">Organisation <span class="sort-icon"></span></th>
|
||||
<th>Aktionen</th>
|
||||
<th class="sticky-actions">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="globalSourceTable"></tbody>
|
||||
@@ -987,8 +987,8 @@
|
||||
</div>
|
||||
|
||||
<script src="/static/js/a11y.js?v=20260725a"></script>
|
||||
<script src="/static/js/app.js?v=20260725v"></script>
|
||||
<script src="/static/js/sources.js?v=20260725f"></script>
|
||||
<script src="/static/js/app.js?v=20260725w"></script>
|
||||
<script src="/static/js/sources.js?v=20260725g"></script>
|
||||
<script src="/static/js/aufgaben.js?v=20260725d"></script>
|
||||
<script src="/static/js/x-scraper.js?v=20260725c"></script>
|
||||
<script src="/static/js/audit.js?v=20260725a"></script>
|
||||
|
||||
@@ -1458,6 +1458,44 @@ function closeAllFilterDropdowns() {
|
||||
// Klick irgendwo anders schließt offene Filter-Dropdowns
|
||||
document.addEventListener("click", () => closeAllFilterDropdowns());
|
||||
|
||||
// Ziehen-zum-Scrollen für breite Tabellen: Fläche greifen und seitwärts
|
||||
// ziehen. Interaktive Elemente (Knöpfe, Checkboxen, Links) bleiben normal
|
||||
// bedienbar, nach einem echten Zieh-Vorgang wird der Klick geschluckt.
|
||||
function enableDragScroll(el) {
|
||||
if (!el || el.dataset.dragScroll === "1") return;
|
||||
el.dataset.dragScroll = "1";
|
||||
let startX = 0, startLeft = 0, dragging = false, moved = false;
|
||||
el.addEventListener("mousedown", (e) => {
|
||||
if (e.button !== 0) return;
|
||||
if (e.target.closest("button, a, input, select, textarea, label, .src-info-toggle, .health-badge-btn")) return;
|
||||
dragging = true;
|
||||
moved = false;
|
||||
startX = e.pageX;
|
||||
startLeft = el.scrollLeft;
|
||||
});
|
||||
document.addEventListener("mousemove", (e) => {
|
||||
if (!dragging) return;
|
||||
const dx = e.pageX - startX;
|
||||
if (Math.abs(dx) > 3) {
|
||||
moved = true;
|
||||
el.classList.add("drag-scrolling");
|
||||
}
|
||||
el.scrollLeft = startLeft - dx;
|
||||
});
|
||||
document.addEventListener("mouseup", () => {
|
||||
if (!dragging) return;
|
||||
dragging = false;
|
||||
el.classList.remove("drag-scrolling");
|
||||
});
|
||||
el.addEventListener("click", (e) => {
|
||||
if (moved) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
moved = false;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Beim Page-Load Meta einmalig laden (asynchron, blockiert nicht)
|
||||
if (window.API && (localStorage.getItem("token") || window.location.pathname === "/dashboard")) {
|
||||
|
||||
@@ -57,6 +57,9 @@ function fillSourceModalSelects() {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
setupSourceSubTabs();
|
||||
setupSourceForms();
|
||||
if (typeof enableDragScroll === "function") {
|
||||
enableDragScroll(document.getElementById("unifiedTableWrap"));
|
||||
}
|
||||
|
||||
// Beim Tab-Wechsel auf "Quellen" laden (+ Aufgaben-Badge aktualisieren)
|
||||
document.querySelectorAll('.nav-tab[data-section="sources"]').forEach((tab) => {
|
||||
@@ -302,7 +305,7 @@ function renderUnifiedSources(sources) {
|
||||
order.forEach((cat) => {
|
||||
const label = CATEGORY_LABELS[cat] || cat;
|
||||
const count = grouped[cat].length;
|
||||
html += `<tr class="cat-header-row"><td colspan="${cols}"><span class="cat-header-label">${esc(label)}</span><span class="cat-header-count">${count}</span></td></tr>`;
|
||||
html += `<tr class="cat-header-row"><td colspan="${cols}"><span class="cat-header-sticky"><span class="cat-header-label">${esc(label)}</span><span class="cat-header-count">${count}</span></span></td></tr>`;
|
||||
grouped[cat].forEach((s) => {
|
||||
const isTenant = s.tenant_id != null;
|
||||
const hasNotes = s.notes && s.notes.trim();
|
||||
@@ -334,7 +337,7 @@ function renderUnifiedSources(sources) {
|
||||
<button class="btn btn-danger btn-small" onclick="confirmDeleteGlobalSource(${s.id}, '${esc(s.name)}')">Löschen</button>`;
|
||||
html += `<tr>
|
||||
${selectCell}
|
||||
<td>${infoBtn} ${esc(s.name)}</td>
|
||||
<td class="sticky-name" title="${esc(s.name)}">${infoBtn} ${esc(s.name)}</td>
|
||||
<td class="text-secondary" style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${esc(s.url || '')}">${esc(s.url || "-")}</td>
|
||||
<td>${esc(s.domain || "-")}</td>
|
||||
<td>${typeLabel(s.source_type)}</td>
|
||||
@@ -346,7 +349,7 @@ function renderUnifiedSources(sources) {
|
||||
<td>${healthCell}</td>
|
||||
<td><span class="badge badge-${s.status === "active" ? "active" : "inactive"}">${s.status === "active" ? "Aktiv" : "Inaktiv"}</span></td>
|
||||
<td class="text-secondary">${isTenant ? esc(s.org_name || ("Org " + s.tenant_id)) : '<span class="text-muted">—</span>'}</td>
|
||||
<td>${actions}</td>
|
||||
<td class="sticky-actions">${actions}</td>
|
||||
</tr>${detailRow}`;
|
||||
});
|
||||
});
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren