Phase 1 — Python (klausur-service): 5 monoliths → 36 files - dsfa_corpus_ingestion.py (1,828 LOC → 5 files) - cv_ocr_engines.py (2,102 LOC → 7 files) - cv_layout.py (3,653 LOC → 10 files) - vocab_worksheet_api.py (2,783 LOC → 8 files) - grid_build_core.py (1,958 LOC → 6 files) Phase 2 — Go (edu-search-service, school-service): 8 monoliths → 19 files - staff_crawler.go (1,402 → 4), policy/store.go (1,168 → 3) - policy_handlers.go (700 → 2), repository.go (684 → 2) - search.go (592 → 2), ai_extraction_handlers.go (554 → 2) - seed_data.go (591 → 2), grade_service.go (646 → 2) Phase 3 — TypeScript (admin-lehrer): 45 monoliths → 220+ files - sdk/types.ts (2,108 → 16 domain files) - ai/rag/page.tsx (2,686 → 14 files) - 22 page.tsx files split into _components/ + _hooks/ - 11 component files split into sub-components - 10 SDK data catalogs added to loc-exceptions - Deleted dead backup index_original.ts (4,899 LOC) All original public APIs preserved via re-export facades. Zero new errors: Python imports verified, Go builds clean, TypeScript tsc --noEmit shows only pre-existing errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
114 lines
4.2 KiB
TypeScript
114 lines
4.2 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Alerts Monitoring Admin Page (migrated from website/admin/alerts)
|
|
*
|
|
* Google Alerts & Feed-Ueberwachung Dashboard
|
|
* Provides inbox management, topic configuration, rule builder, and relevance profiles
|
|
*/
|
|
|
|
import { useState } from 'react'
|
|
import { PagePurpose } from '@/components/common/PagePurpose'
|
|
import type { TabId } from './types'
|
|
import { useAlertsData } from './useAlertsData'
|
|
import { StatsOverview } from './_components/StatsOverview'
|
|
import { DashboardTab } from './_components/DashboardTab'
|
|
import { InboxTab } from './_components/InboxTab'
|
|
import { TopicsTab } from './_components/TopicsTab'
|
|
import { RulesTab } from './_components/RulesTab'
|
|
import { ProfileTab } from './_components/ProfileTab'
|
|
import { AuditTab } from './_components/AuditTab'
|
|
import { DocumentationTab } from './_components/DocumentationTab'
|
|
|
|
const TABS: { id: TabId; label: string; hasBadge?: boolean }[] = [
|
|
{ id: 'dashboard', label: 'Dashboard' },
|
|
{ id: 'inbox', label: 'Inbox', hasBadge: true },
|
|
{ id: 'topics', label: 'Topics' },
|
|
{ id: 'rules', label: 'Regeln' },
|
|
{ id: 'profile', label: 'Profil' },
|
|
{ id: 'audit', label: 'Audit' },
|
|
{ id: 'documentation', label: 'Dokumentation' },
|
|
]
|
|
|
|
export default function AlertsPage() {
|
|
const [activeTab, setActiveTab] = useState<TabId>('dashboard')
|
|
const data = useAlertsData()
|
|
|
|
if (data.loading) {
|
|
return (
|
|
<div className="flex items-center justify-center h-64">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-green-600" />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<PagePurpose
|
|
title="Alerts Monitoring"
|
|
purpose="Google Alerts & Feed-Ueberwachung mit KI-gestuetzter Relevanzpruefung. Verwalten Sie Topics, konfigurieren Sie Filterregeln und nutzen Sie LLM-basiertes Scoring fuer automatische Kategorisierung."
|
|
audience={['Marketing', 'Admins', 'DSB']}
|
|
architecture={{
|
|
services: ['backend (FastAPI)', 'APScheduler', 'LLM Gateway'],
|
|
databases: ['PostgreSQL', 'Valkey Cache'],
|
|
}}
|
|
relatedPages={[
|
|
{ name: 'Unified Inbox', href: '/communication/mail', description: 'E-Mail-Konten verwalten' },
|
|
{ name: 'Voice Service', href: '/communication/matrix', description: 'Voice-First Interface' },
|
|
]}
|
|
collapsible={true}
|
|
defaultCollapsed={false}
|
|
/>
|
|
|
|
<StatsOverview stats={data.stats} />
|
|
|
|
{/* Tab Navigation */}
|
|
<div className="bg-white rounded-lg shadow mb-6">
|
|
<div className="border-b border-slate-200 px-4">
|
|
<nav className="flex gap-4 overflow-x-auto">
|
|
{TABS.map((tab) => {
|
|
const badge = tab.hasBadge ? (data.stats?.new_alerts || 0) : 0
|
|
return (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => setActiveTab(tab.id)}
|
|
className={`pb-3 pt-4 px-1 text-sm font-medium border-b-2 transition-colors flex items-center gap-2 whitespace-nowrap ${
|
|
activeTab === tab.id
|
|
? 'border-green-600 text-green-600'
|
|
: 'border-transparent text-slate-500 hover:text-slate-700'
|
|
}`}
|
|
>
|
|
{tab.label}
|
|
{badge > 0 && (
|
|
<span className="px-2 py-0.5 rounded-full text-xs font-semibold bg-red-500 text-white">
|
|
{badge}
|
|
</span>
|
|
)}
|
|
</button>
|
|
)
|
|
})}
|
|
</nav>
|
|
</div>
|
|
|
|
<div className="p-6">
|
|
{activeTab === 'dashboard' && (
|
|
<DashboardTab topics={data.topics} alerts={data.alerts} error={data.error} />
|
|
)}
|
|
{activeTab === 'inbox' && (
|
|
<InboxTab
|
|
filteredAlerts={data.filteredAlerts}
|
|
inboxFilter={data.inboxFilter}
|
|
setInboxFilter={data.setInboxFilter}
|
|
/>
|
|
)}
|
|
{activeTab === 'topics' && <TopicsTab topics={data.topics} />}
|
|
{activeTab === 'rules' && <RulesTab rules={data.rules} />}
|
|
{activeTab === 'profile' && <ProfileTab profile={data.profile} />}
|
|
{activeTab === 'audit' && <AuditTab />}
|
|
{activeTab === 'documentation' && <DocumentationTab />}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|