Phase 3c: Kundenquellen-Tab mit Filter + Sort + Bulk-Promote
Backend
- routers/sources.py: POST /api/sources/tenant/bulk-promote NEU
Nimmt Liste von source_ids, promotet jede einzeln zur Grundquelle.
Returns {promoted, skipped[{id,name,reason}], failed[{id,error}]}.
Ueberspringt Quellen die schon Grundquellen sind oder deren URL bereits
als Grundquelle existiert.
Frontend
- dashboard.html sub-tenant-sources: action-bar erweitert um
3 Filter-Selects (Typ, Kategorie, Org), Bulk-Promote-Button.
Tabelle bekommt Checkbox-Spalte + sortable Spalten (Sort-Icons).
- sources.js: tenant-Tab komplett refactored
- State: tenantFilters, tenantSort, tenantSelected (Set)
- applyTenantFilterAndSort: zentraler Render-Pfad mit allen Filtern + Sort
- populateTenantFilters: Org-Liste aus Daten, Typ/Kategorie aus META
- toggleTenantSelect / toggleTenantSelectAll: Selection-Logik
- bulkPromoteSelected: showConfirm -> POST -> Toast mit Ergebnis
- renderTenantSources: Checkbox-Spalte, dynamische typeLabel/categoryLabel
- Counter zeigt jetzt N gefiltert / Gesamt
Dieser Commit ist enthalten in:
@@ -340,21 +340,34 @@
|
||||
<!-- Kundenquellen -->
|
||||
<div class="section" id="sub-tenant-sources">
|
||||
<div class="action-bar">
|
||||
<div style="display:flex;align-items:center;gap:12px;">
|
||||
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
||||
<input type="text" class="search-input" id="tenantSourceSearch" placeholder="Kundenquelle suchen...">
|
||||
<select class="filter-select" id="tenantFilterType" onchange="filterTenantSources()">
|
||||
<option value="">Alle Typen</option>
|
||||
</select>
|
||||
<select class="filter-select" id="tenantFilterCategory" onchange="filterTenantSources()">
|
||||
<option value="">Alle Kategorien</option>
|
||||
</select>
|
||||
<select class="filter-select" id="tenantFilterOrg" onchange="filterTenantSources()">
|
||||
<option value="">Alle Organisationen</option>
|
||||
</select>
|
||||
<span class="text-secondary" id="tenantSourceCount"></span>
|
||||
</div>
|
||||
<button class="btn btn-primary" id="tenantBulkPromoteBtn" disabled onclick="bulkPromoteSelected()">
|
||||
Ausgewählte übernehmen (0)
|
||||
</button>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Domain</th>
|
||||
<th>Typ</th>
|
||||
<th>Kategorie</th>
|
||||
<th>Organisation</th>
|
||||
<th style="width:32px;"><input type="checkbox" id="tenantSelectAll" onchange="toggleTenantSelectAll(this.checked)"></th>
|
||||
<th class="sortable" data-sort="name" onclick="sortTenantSources('name')">Name <span class="sort-icon"></span></th>
|
||||
<th class="sortable" data-sort="domain" onclick="sortTenantSources('domain')">Domain <span class="sort-icon"></span></th>
|
||||
<th class="sortable" data-sort="source_type" onclick="sortTenantSources('source_type')">Typ <span class="sort-icon"></span></th>
|
||||
<th class="sortable" data-sort="category" onclick="sortTenantSources('category')">Kategorie <span class="sort-icon"></span></th>
|
||||
<th class="sortable" data-sort="org_name" onclick="sortTenantSources('org_name')">Organisation <span class="sort-icon"></span></th>
|
||||
<th>Hinzugefügt von</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
@@ -365,18 +378,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quellen-Health -->
|
||||
<div class="section" id="sub-source-health">
|
||||
<div class="action-bar">
|
||||
<h2 style="font-size:16px;font-weight:600;">Quellen-Health & Vorschläge</h2>
|
||||
<button class="btn btn-primary" id="runHealthCheckBtn" onclick="runHealthCheck()">Jetzt prüfen</button>
|
||||
</div>
|
||||
<div id="healthContent">
|
||||
<div class="text-muted" style="padding:20px;">Tab auswählen um Health-Daten zu laden...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Audit-Log Section -->
|
||||
<div class="section" id="sec-audit">
|
||||
<div class="action-bar" style="flex-wrap:wrap;gap:8px;">
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren