Phase 15: language + bias als Spalten, Filter, Edit-Form
Bisher waren die DB-Felder sources.language und sources.bias zwar gepflegt (254/275 Quellen mit bias, 254 mit language), aber in der Verwaltung nicht sichtbar. Der Admin konnte nicht filtern oder editieren. Backend (routers/sources.py) - GlobalSourceCreate + GlobalSourceUpdate Pydantic-Modelle: language + bias als Optional[str] erweitert (max 100 / 500 Zeichen). - SOURCE_UPDATE_COLUMNS: language + bias hinzu. - INSERT in create_global_source: schreibt language + bias mit. - Neuer Endpoint GET /api/sources/global/languages: distinct language-Werte fuer Frontend-Filter-Dropdown. Frontend HTML (dashboard.html) - Grundquellen-Filter-Bar: Sprachen-Dropdown ergaenzt. - Grundquellen-Tabellenkopf: 2 neue Spalten Sprache (sortable) + Bias. - modalSource: 2 neue Felder language (mit datalist Vorschlaegen) + bias. - Kundenquellen-Filter-Bar: Sprachen-Dropdown. - Kundenquellen-Tabellenkopf: Sprache (sortable) + Bias. Frontend JS (sources.js) - loadGlobalSources lädt /languages parallel zu /global + /global/stats, populiert beide Sprache-Dropdowns + datalist im Edit-Modal. - renderGlobalSources: cols 11 -> 13, language+bias-Zellen (Bias mit Tooltip fuer Lang-Texte). - filterGlobalSources: Sprache-Filter, Bias in Suche. - editGlobalSource: language + bias laden. - Form-Submit: language + bias mitgesendet. - renderTenantSources: cols 8 -> 10, language+bias-Zellen. - tenantFilters um language erweitert, applyTenantFilterAndSort prueft. Cache-Buster ?v=20260509 (heute) bleibt - Tag wechselt erst morgen.
Dieser Commit ist enthalten in:
@@ -24,7 +24,7 @@ logger = logging.getLogger("verwaltung.sources")
|
||||
|
||||
router = APIRouter(prefix="/api/sources", tags=["sources"])
|
||||
|
||||
SOURCE_UPDATE_COLUMNS = {"name", "url", "domain", "source_type", "category", "status", "notes"}
|
||||
SOURCE_UPDATE_COLUMNS = {"name", "url", "domain", "source_type", "category", "status", "notes", "language", "bias"}
|
||||
|
||||
|
||||
@router.get("/meta")
|
||||
@@ -46,6 +46,8 @@ class GlobalSourceCreate(BaseModel):
|
||||
category: str = Field(default="sonstige")
|
||||
status: str = Field(default="active", pattern="^(active|inactive)$")
|
||||
notes: Optional[str] = None
|
||||
language: Optional[str] = Field(default=None, max_length=100)
|
||||
bias: Optional[str] = Field(default=None, max_length=500)
|
||||
|
||||
|
||||
class GlobalSourceUpdate(BaseModel):
|
||||
@@ -56,6 +58,8 @@ class GlobalSourceUpdate(BaseModel):
|
||||
category: Optional[str] = None
|
||||
status: Optional[str] = Field(default=None, pattern="^(active|inactive)$")
|
||||
notes: Optional[str] = None
|
||||
language: Optional[str] = Field(default=None, max_length=100)
|
||||
bias: Optional[str] = Field(default=None, max_length=500)
|
||||
|
||||
|
||||
@router.get("/global")
|
||||
@@ -139,9 +143,10 @@ async def create_global_source(
|
||||
)
|
||||
|
||||
cursor = await db.execute(
|
||||
"""INSERT INTO sources (name, url, domain, source_type, category, status, notes, added_by, tenant_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, 'system', NULL)""",
|
||||
(data.name, data.url, data.domain, data.source_type, data.category, data.status, data.notes),
|
||||
"""INSERT INTO sources (name, url, domain, source_type, category, status, notes, language, bias, added_by, tenant_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'system', NULL)""",
|
||||
(data.name, data.url, data.domain, data.source_type, data.category, data.status, data.notes,
|
||||
data.language, data.bias),
|
||||
)
|
||||
src_id = cursor.lastrowid
|
||||
await db.commit()
|
||||
@@ -223,6 +228,24 @@ async def delete_global_source(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@router.get("/global/languages")
|
||||
async def get_global_languages(
|
||||
admin: dict = Depends(get_current_admin),
|
||||
db: aiosqlite.Connection = Depends(db_dependency),
|
||||
):
|
||||
"""Distinct language-Werte aus Grundquellen - für Frontend-Filter-Dropdown."""
|
||||
cur = await db.execute("""
|
||||
SELECT DISTINCT language
|
||||
FROM sources
|
||||
WHERE tenant_id IS NULL AND language IS NOT NULL AND language != ''
|
||||
ORDER BY language
|
||||
""")
|
||||
return [r["language"] for r in await cur.fetchall()]
|
||||
|
||||
|
||||
@router.get("/global/stats")
|
||||
async def get_global_stats(
|
||||
admin: dict = Depends(get_current_admin),
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren