Files
breakpilot-lehrer/admin-lehrer/app/(admin)/communication/alerts/page.tsx
Benjamin Admin b681ddb131 [split-required] Split 58 monoliths across Python, Go, TypeScript (Phases 1-3)
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>
2026-04-24 17:28:57 +02:00

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>
)
}