Files
breakpilot-lehrer/website/app/admin/mail/page.tsx
Benjamin Admin b6983ab1dc [split-required] Split 500-1000 LOC files across all services
backend-lehrer (5 files):
- alerts_agent/db/repository.py (992 → 5), abitur_docs_api.py (956 → 3)
- teacher_dashboard_api.py (951 → 3), services/pdf_service.py (916 → 3)
- mail/mail_db.py (987 → 6)

klausur-service (5 files):
- legal_templates_ingestion.py (942 → 3), ocr_pipeline_postprocess.py (929 → 4)
- ocr_pipeline_words.py (876 → 3), ocr_pipeline_ocr_merge.py (616 → 2)
- KorrekturPage.tsx (956 → 6)

website (5 pages):
- mail (985 → 9), edu-search (958 → 8), mac-mini (950 → 7)
- ocr-labeling (946 → 7), audit-workspace (871 → 4)

studio-v2 (5 files + 1 deleted):
- page.tsx (946 → 5), MessagesContext.tsx (925 → 4)
- korrektur (914 → 6), worksheet-cleanup (899 → 6)
- useVocabWorksheet.ts (888 → 3)
- Deleted dead page-original.tsx (934 LOC)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 23:35:37 +02:00

161 lines
5.1 KiB
TypeScript

'use client'
/**
* Unified Inbox Mail Admin Page
*
* Admin interface for managing email accounts, viewing system status,
* and configuring AI analysis settings.
*
* See: docs/klausur-modul/UNIFIED-INBOX-SPECIFICATION.md
*/
import { useState, useEffect, useCallback } from 'react'
import AdminLayout from '@/components/admin/AdminLayout'
import type { MailStats, SyncStatus, EmailAccount, TabId } from './types'
import { API_BASE, tabs } from './constants'
import OverviewTab from './_components/OverviewTab'
import AccountsTab from './_components/AccountsTab'
import AISettingsTab from './_components/AISettingsTab'
import TemplatesTab from './_components/TemplatesTab'
import AuditLogTab from './_components/AuditLogTab'
export default function MailAdminPage() {
const [activeTab, setActiveTab] = useState<TabId>('overview')
const [stats, setStats] = useState<MailStats | null>(null)
const [accounts, setAccounts] = useState<EmailAccount[]>([])
const [syncStatus, setSyncStatus] = useState<SyncStatus | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const fetchData = useCallback(async () => {
try {
setLoading(true)
// Fetch stats and accounts in parallel
const [statsRes, accountsRes, statusRes] = await Promise.all([
fetch(`${API_BASE}/api/v1/mail/stats`),
fetch(`${API_BASE}/api/v1/mail/accounts`),
fetch(`${API_BASE}/api/v1/mail/sync/status`),
])
if (statsRes.ok) {
const statsData = await statsRes.json()
setStats({
totalAccounts: statsData.total_accounts || 0,
activeAccounts: statsData.active_accounts || 0,
totalEmails: statsData.total_emails || 0,
unreadEmails: statsData.unread_emails || 0,
totalTasks: statsData.total_tasks || 0,
pendingTasks: statsData.pending_tasks || 0,
overdueTasks: statsData.overdue_tasks || 0,
aiAnalyzedCount: statsData.ai_analyzed_count || 0,
lastSyncTime: statsData.last_sync_time,
})
}
if (accountsRes.ok) {
const accountsData = await accountsRes.json()
setAccounts(accountsData.accounts || [])
}
if (statusRes.ok) {
const statusData = await statusRes.json()
setSyncStatus({
running: statusData.running || false,
accountsInProgress: statusData.accounts_in_progress || [],
lastCompleted: statusData.last_completed,
errors: statusData.errors || [],
})
}
setError(null)
} catch (err) {
console.error('Failed to fetch mail data:', err)
setError('Verbindung zum Mail-Service fehlgeschlagen')
} finally {
setLoading(false)
}
}, [])
useEffect(() => {
fetchData()
// Refresh every 10 seconds if syncing
const interval = setInterval(() => {
if (syncStatus?.running) {
fetchData()
}
}, 10000)
return () => clearInterval(interval)
}, [fetchData, syncStatus?.running])
return (
<AdminLayout
title="Unified Inbox"
description="E-Mail-Konten & KI-gestützte Analyse verwalten"
>
{/* Error Banner */}
{error && (
<div className="mb-6 bg-red-50 border border-red-200 rounded-lg p-4 flex items-center gap-3">
<svg className="w-5 h-5 text-red-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="text-red-700">{error}</span>
<button onClick={fetchData} className="ml-auto text-red-600 hover:text-red-800 text-sm font-medium">
Erneut versuchen
</button>
</div>
)}
{/* Tab Navigation */}
<div className="border-b border-slate-200 mb-6">
<nav className="-mb-px flex space-x-8">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`
flex items-center gap-2 py-4 px-1 border-b-2 font-medium text-sm transition-colors
${activeTab === tab.id
? 'border-primary-500 text-primary-600'
: 'border-transparent text-slate-500 hover:text-slate-700 hover:border-slate-300'
}
`}
>
{tab.icon}
{tab.name}
</button>
))}
</nav>
</div>
{/* Tab Content */}
{activeTab === 'overview' && (
<OverviewTab
stats={stats}
syncStatus={syncStatus}
loading={loading}
onRefresh={fetchData}
/>
)}
{activeTab === 'accounts' && (
<AccountsTab
accounts={accounts}
loading={loading}
onRefresh={fetchData}
/>
)}
{activeTab === 'ai-settings' && (
<AISettingsTab />
)}
{activeTab === 'templates' && (
<TemplatesTab />
)}
{activeTab === 'logs' && (
<AuditLogTab />
)}
</AdminLayout>
)
}