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>
215 lines
14 KiB
TypeScript
215 lines
14 KiB
TypeScript
'use client'
|
|
|
|
const ARCHITECTURE_DIAGRAM = `
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
│ BreakPilot Alerts Frontend │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐│
|
|
│ │ Dashboard │ │ Inbox │ │ Topics │ │ Profile ││
|
|
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────┘│
|
|
└───────────────────────────────┬─────────────────────────────────────┘
|
|
│
|
|
v
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
│ Ingestion Layer │
|
|
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
|
|
│ │ RSS Fetcher │ │ Email Parser │ │ APScheduler │ │
|
|
│ └───────┬────────┘ └───────┬────────┘ └───────┬────────┘ │
|
|
│ └───────────────────┼───────────────────┘ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ Deduplication (URL-Hash + SimHash) │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
v
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
│ Processing Layer │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ Rule Engine │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ LLM Relevance Scorer │ │
|
|
│ │ Output: { score, decision: KEEP/DROP/REVIEW } │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
v
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
│ Action Layer │
|
|
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
|
|
│ │ Email Action │ │ Webhook Action │ │ Slack Action │ │
|
|
│ └────────────────┘ └────────────────┘ └────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
v
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
│ Storage Layer │
|
|
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
|
|
│ │ PostgreSQL │ │ Valkey │ │ LLM Gateway │ │
|
|
│ └────────────────┘ └────────────────┘ └────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────┘`
|
|
|
|
const API_ENDPOINTS = [
|
|
{ endpoint: '/api/alerts/inbox', method: 'GET', desc: 'Inbox Items abrufen' },
|
|
{ endpoint: '/api/alerts/ingest', method: 'POST', desc: 'Manuell Alert importieren' },
|
|
{ endpoint: '/api/alerts/topics', method: 'GET/POST', desc: 'Topics verwalten' },
|
|
{ endpoint: '/api/alerts/rules', method: 'GET/POST', desc: 'Regeln verwalten' },
|
|
{ endpoint: '/api/alerts/profile', method: 'GET/PUT', desc: 'Profil abrufen/aktualisieren' },
|
|
{ endpoint: '/api/alerts/stats', method: 'GET', desc: 'Statistiken abrufen' },
|
|
]
|
|
|
|
const RULE_OPERATORS = [
|
|
{ op: 'contains', desc: 'Text enthaelt', example: 'title contains "Inklusion"' },
|
|
{ op: 'not_contains', desc: 'Text enthaelt nicht', example: 'title not_contains "Werbung"' },
|
|
{ op: 'equals', desc: 'Exakte Uebereinstimmung', example: 'status equals "new"' },
|
|
{ op: 'regex', desc: 'Regulaerer Ausdruck', example: 'title regex "\\d{4}"' },
|
|
{ op: 'gt / lt', desc: 'Groesser/Kleiner', example: 'relevance_score gt 0.8' },
|
|
]
|
|
|
|
const SCORING_ROWS = [
|
|
{ decision: 'KEEP', range: '0.7 - 1.0', meaning: 'Klar relevant, in Inbox anzeigen', bgCls: 'bg-green-50', textCls: 'text-green-800' },
|
|
{ decision: 'REVIEW', range: '0.4 - 0.7', meaning: 'Unsicher, Nutzer entscheidet', bgCls: 'bg-amber-50', textCls: 'text-amber-800' },
|
|
{ decision: 'DROP', range: '0.0 - 0.4', meaning: 'Irrelevant, automatisch archivieren', bgCls: 'bg-red-50', textCls: 'text-red-800' },
|
|
]
|
|
|
|
const CONTACTS = [
|
|
{ role: 'Technischer Support', addr: 'support@breakpilot.de' },
|
|
{ role: 'Datenschutzbeauftragter', addr: 'dsb@breakpilot.de' },
|
|
{ role: 'Dokumentation', addr: 'docs.breakpilot.de' },
|
|
]
|
|
|
|
export function DocumentationTab() {
|
|
return (
|
|
<div className="bg-white rounded-xl border border-slate-200 p-6 overflow-auto max-h-[calc(100vh-350px)]">
|
|
<div className="prose prose-slate max-w-none prose-headings:font-semibold prose-h1:text-2xl prose-h2:text-xl prose-h3:text-lg">
|
|
{/* Header */}
|
|
<div className="not-prose mb-8 pb-6 border-b border-slate-200">
|
|
<h1 className="text-2xl font-bold text-slate-900">BreakPilot Alerts Agent</h1>
|
|
<p className="text-sm text-slate-500 mt-1">Version: 1.0.0 | Stand: Januar 2026 | Autor: BreakPilot Development Team</p>
|
|
</div>
|
|
|
|
{/* Audit Box */}
|
|
<div className="not-prose bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
|
|
<h3 className="font-semibold text-blue-900 mb-2">Audit-Relevante Informationen</h3>
|
|
<p className="text-sm text-blue-800">
|
|
Dieses Dokument dient als technische Dokumentation fuer das Alert-Monitoring-System der BreakPilot Plattform.
|
|
Es ist fuer Audits durch Bildungstraeger und Datenschutzbeauftragte konzipiert.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Ziel des Systems */}
|
|
<h2>Ziel des Alert-Systems</h2>
|
|
<p>Das System ermoeglicht automatisierte Ueberwachung von Bildungsthemen mit:</p>
|
|
<ul>
|
|
<li><strong>Google Alerts Integration</strong>: RSS-Feeds von Google Alerts automatisch abrufen</li>
|
|
<li><strong>RSS/Atom Feeds</strong>: Beliebige Nachrichtenquellen einbinden</li>
|
|
<li><strong>KI-Relevanzpruefung</strong>: Automatische Bewertung der Relevanz durch LLM</li>
|
|
<li><strong>Regelbasierte Filterung</strong>: Flexible Regeln fuer automatische Sortierung</li>
|
|
<li><strong>Multi-Channel Actions</strong>: E-Mail, Webhook, Slack Benachrichtigungen</li>
|
|
<li><strong>Few-Shot Learning</strong>: Profil verbessert sich durch Nutzerfeedback</li>
|
|
</ul>
|
|
|
|
{/* Architecture Diagram */}
|
|
<h2>Systemarchitektur</h2>
|
|
<div className="not-prose bg-slate-900 rounded-lg p-4 overflow-x-auto">
|
|
<pre className="text-green-400 text-xs">{ARCHITECTURE_DIAGRAM}</pre>
|
|
</div>
|
|
|
|
{/* API Endpoints */}
|
|
<h2>API Endpoints</h2>
|
|
<div className="not-prose overflow-x-auto">
|
|
<table className="min-w-full text-sm border border-slate-200 rounded-lg">
|
|
<thead className="bg-slate-50">
|
|
<tr>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Endpoint</th>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Methode</th>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Beschreibung</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-slate-200">
|
|
{API_ENDPOINTS.map((row) => (
|
|
<tr key={row.endpoint}>
|
|
<td className="px-4 py-2 font-mono text-xs">{row.endpoint}</td>
|
|
<td className="px-4 py-2">{row.method}</td>
|
|
<td className="px-4 py-2 text-slate-600">{row.desc}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{/* Rule Engine */}
|
|
<h2>Rule Engine - Operatoren</h2>
|
|
<div className="not-prose overflow-x-auto">
|
|
<table className="min-w-full text-sm border border-slate-200 rounded-lg">
|
|
<thead className="bg-slate-50">
|
|
<tr>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Operator</th>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Beschreibung</th>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Beispiel</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-slate-200">
|
|
{RULE_OPERATORS.map((row) => (
|
|
<tr key={row.op}>
|
|
<td className="px-4 py-2 font-mono text-xs">{row.op}</td>
|
|
<td className="px-4 py-2">{row.desc}</td>
|
|
<td className="px-4 py-2 text-slate-600">{row.example}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{/* Scoring */}
|
|
<h2>LLM Relevanz-Scoring</h2>
|
|
<div className="not-prose overflow-x-auto">
|
|
<table className="min-w-full text-sm border border-slate-200 rounded-lg">
|
|
<thead className="bg-slate-50">
|
|
<tr>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Entscheidung</th>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Score-Bereich</th>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Bedeutung</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-slate-200">
|
|
{SCORING_ROWS.map((row) => (
|
|
<tr key={row.decision} className={row.bgCls}>
|
|
<td className={`px-4 py-2 font-semibold ${row.textCls}`}>{row.decision}</td>
|
|
<td className="px-4 py-2">{row.range}</td>
|
|
<td className="px-4 py-2">{row.meaning}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{/* Contact */}
|
|
<h2>Kontakt & Support</h2>
|
|
<div className="not-prose overflow-x-auto">
|
|
<table className="min-w-full text-sm border border-slate-200 rounded-lg">
|
|
<thead className="bg-slate-50">
|
|
<tr>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Kontakt</th>
|
|
<th className="px-4 py-2 text-left font-semibold text-slate-700 border-b">Adresse</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-slate-200">
|
|
{CONTACTS.map((row) => (
|
|
<tr key={row.role}>
|
|
<td className="px-4 py-2">{row.role}</td>
|
|
<td className="px-4 py-2">{row.addr}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<div className="not-prose mt-8 pt-6 border-t border-slate-200 text-sm text-slate-500">
|
|
<p>Dokumentation erstellt: Januar 2026 | Version: 1.0.0</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|