Files
breakpilot-lehrer/studio-v2/app/_components/HeaderBar.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

151 lines
7.0 KiB
TypeScript

'use client'
import { useRouter } from 'next/navigation'
import { useLanguage } from '@/lib/LanguageContext'
import { useTheme } from '@/lib/ThemeContext'
import { useAlerts, getImportanceColor, getRelativeTime } from '@/lib/AlertsContext'
import { LanguageDropdown } from '@/components/LanguageDropdown'
import { ThemeToggle } from '@/components/ThemeToggle'
interface HeaderBarProps {
showAlertsDropdown: boolean
setShowAlertsDropdown: (show: boolean) => void
}
export function HeaderBar({ showAlertsDropdown, setShowAlertsDropdown }: HeaderBarProps) {
const router = useRouter()
const { t } = useLanguage()
const { isDark } = useTheme()
const { alerts, unreadCount, markAsRead } = useAlerts()
return (
<div className="flex items-center justify-between mb-8">
<div>
<h1 className={`text-4xl font-bold mb-2 ${isDark ? 'text-white' : 'text-slate-900'}`}>{t('dashboard')}</h1>
<p className={isDark ? 'text-white/60' : 'text-slate-600'}>{t('dashboard_subtitle')}</p>
</div>
{/* Search, Language & Actions */}
<div className="flex items-center gap-4">
<div className="relative">
<input
type="text"
placeholder={t('search_placeholder')}
className={`backdrop-blur-xl border rounded-2xl px-5 py-3 pl-12 focus:outline-none focus:ring-2 w-64 transition-all ${
isDark
? 'bg-white/10 border-white/20 text-white placeholder-white/40 focus:ring-white/30'
: 'bg-white/70 border-black/10 text-slate-900 placeholder-slate-400 focus:ring-indigo-300'
}`}
/>
<svg className={`absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 ${isDark ? 'text-white/40' : 'text-slate-400'}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<LanguageDropdown />
<ThemeToggle />
{/* Notifications Bell with Glow Effect */}
<div className="relative">
<button
onClick={() => setShowAlertsDropdown(!showAlertsDropdown)}
className={`relative p-3 backdrop-blur-xl border rounded-2xl transition-all ${
unreadCount > 0
? 'animate-pulse bg-gradient-to-r from-amber-500/20 to-orange-500/20 border-amber-500/30 shadow-lg shadow-amber-500/30'
: isDark
? 'bg-white/10 border-white/20 hover:bg-white/20'
: 'bg-black/5 border-black/10 hover:bg-black/10'
} ${isDark ? 'text-white' : 'text-slate-700'}`}
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
{unreadCount > 0 && (
<span className="absolute -top-1 -right-1 w-5 h-5 bg-red-500 rounded-full text-xs text-white flex items-center justify-center font-medium">
{unreadCount > 9 ? '9+' : unreadCount}
</span>
)}
</button>
{/* Alerts Dropdown */}
{showAlertsDropdown && (
<>
<div className="fixed inset-0 z-40" onClick={() => setShowAlertsDropdown(false)} />
<div className={`absolute right-0 mt-2 w-80 rounded-2xl border shadow-xl z-50 ${
isDark
? 'bg-slate-900 border-white/20'
: 'bg-white border-slate-200'
}`}>
<div className={`p-4 border-b ${isDark ? 'border-white/10' : 'border-slate-100'}`}>
<div className="flex items-center justify-between">
<h3 className={`font-semibold ${isDark ? 'text-white' : 'text-slate-900'}`}>
Aktuelle Alerts
</h3>
{unreadCount > 0 && (
<span className="px-2 py-0.5 text-xs font-medium rounded-full bg-amber-500/20 text-amber-500">
{unreadCount} neu
</span>
)}
</div>
</div>
<div className="max-h-80 overflow-y-auto">
{alerts.slice(0, 5).map(alert => (
<button
key={alert.id}
onClick={() => {
markAsRead(alert.id)
setShowAlertsDropdown(false)
router.push('/alerts')
}}
className={`w-full text-left p-4 transition-all ${
isDark
? `hover:bg-white/5 ${!alert.isRead ? 'bg-amber-500/5 border-l-2 border-amber-500' : ''}`
: `hover:bg-slate-50 ${!alert.isRead ? 'bg-amber-50 border-l-2 border-amber-500' : ''}`
}`}
>
<div className="flex items-start gap-3">
<span className={`px-1.5 py-0.5 rounded text-xs font-medium ${getImportanceColor(alert.importance, isDark)}`}>
{alert.importance.slice(0, 4)}
</span>
<div className="flex-1 min-w-0">
<p className={`text-sm font-medium truncate ${isDark ? 'text-white' : 'text-slate-900'}`}>
{alert.title}
</p>
<p className={`text-xs ${isDark ? 'text-white/40' : 'text-slate-400'}`}>
{getRelativeTime(alert.timestamp)}
</p>
</div>
</div>
</button>
))}
{alerts.length === 0 && (
<div className={`p-8 text-center ${isDark ? 'text-white/40' : 'text-slate-400'}`}>
<span className="text-2xl block mb-2">📭</span>
<p className="text-sm">Keine Alerts</p>
</div>
)}
</div>
<div className={`p-3 border-t ${isDark ? 'border-white/10' : 'border-slate-100'}`}>
<button
onClick={() => {
setShowAlertsDropdown(false)
router.push('/alerts')
}}
className={`w-full py-2 text-sm font-medium rounded-lg transition-all ${
isDark
? 'text-amber-400 hover:bg-amber-500/10'
: 'text-amber-600 hover:bg-amber-50'
}`}
>
Alle Alerts anzeigen
</button>
</div>
</div>
</>
)}
</div>
</div>
</div>
)
}