website (17 pages + 3 components): - multiplayer/wizard, middleware/wizard+test-wizard, communication - builds/wizard, staff-search, voice, sbom/wizard - foerderantrag, mail/tasks, tools/communication, sbom - compliance/evidence, uni-crawler, brandbook (already done) - CollectionsTab, IngestionTab, RiskHeatmap backend-lehrer (5 files): - letters_api (641 → 2), certificates_api (636 → 2) - alerts_agent/db/models (636 → 3) - llm_gateway/communication_service (614 → 2) - game/database already done in prior batch klausur-service (2 files): - hybrid_vocab_extractor (664 → 2) - klausur-service/frontend: api.ts (620 → 3), EHUploadWizard (591 → 2) voice-service (3 files): - bqas/rag_judge (618 → 3), runner (529 → 2) - enhanced_task_orchestrator (519 → 2) studio-v2 (6 files): - korrektur/[klausurId] (578 → 4), fairness (569 → 2) - AlertsWizard (552 → 2), OnboardingWizard (513 → 2) - korrektur/api.ts (506 → 3), geo-lernwelt (501 → 2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
117 lines
5.8 KiB
TypeScript
117 lines
5.8 KiB
TypeScript
import { CommunicationStats } from './types'
|
|
import {
|
|
calculateEstimatedTraffic,
|
|
calculateHourlyEstimate,
|
|
calculateMonthlyEstimate,
|
|
getResourceRecommendation,
|
|
} from './helpers'
|
|
|
|
export function TrafficSection({ stats }: { stats: CommunicationStats | null }) {
|
|
return (
|
|
<div className="bg-white rounded-xl border border-slate-200 p-6 mb-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 bg-emerald-100 rounded-lg flex items-center justify-center">
|
|
<svg className="w-6 h-6 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h3 className="font-semibold text-slate-900">Traffic & Bandbreite</h3>
|
|
<p className="text-sm text-slate-500">SysEleven Ressourcenplanung</p>
|
|
</div>
|
|
</div>
|
|
<span className="px-3 py-1 rounded-full text-xs font-semibold uppercase bg-emerald-100 text-emerald-800">
|
|
Live
|
|
</span>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
|
|
<div className="bg-slate-50 rounded-lg p-4">
|
|
<div className="text-xs text-slate-500 mb-1">Eingehend (heute)</div>
|
|
<div className="text-2xl font-bold text-slate-900">
|
|
{stats?.traffic?.total?.bandwidth_in_mb?.toFixed(1) || calculateEstimatedTraffic(stats, 'in').toFixed(1)} MB
|
|
</div>
|
|
</div>
|
|
<div className="bg-slate-50 rounded-lg p-4">
|
|
<div className="text-xs text-slate-500 mb-1">Ausgehend (heute)</div>
|
|
<div className="text-2xl font-bold text-slate-900">
|
|
{stats?.traffic?.total?.bandwidth_out_mb?.toFixed(1) || calculateEstimatedTraffic(stats, 'out').toFixed(1)} MB
|
|
</div>
|
|
</div>
|
|
<div className="bg-slate-50 rounded-lg p-4">
|
|
<div className="text-xs text-slate-500 mb-1">Geschätzt/Stunde</div>
|
|
<div className="text-2xl font-bold text-blue-600">
|
|
{stats?.traffic?.jitsi?.estimated_hourly_gb?.toFixed(2) || calculateHourlyEstimate(stats).toFixed(2)} GB
|
|
</div>
|
|
</div>
|
|
<div className="bg-slate-50 rounded-lg p-4">
|
|
<div className="text-xs text-slate-500 mb-1">Geschätzt/Monat</div>
|
|
<div className="text-2xl font-bold text-emerald-600">
|
|
{stats?.traffic?.total?.estimated_monthly_gb?.toFixed(1) || calculateMonthlyEstimate(stats).toFixed(1)} GB
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{/* Matrix Traffic */}
|
|
<div className="border border-slate-200 rounded-lg p-4">
|
|
<div className="flex items-center gap-2 mb-3">
|
|
<div className="w-3 h-3 bg-purple-500 rounded-full"></div>
|
|
<span className="text-sm font-medium text-slate-700">Matrix Messaging</span>
|
|
</div>
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex justify-between">
|
|
<span className="text-slate-500">Nachrichten/Min</span>
|
|
<span className="font-medium">{stats?.traffic?.matrix?.messages_per_minute || Math.round((stats?.matrix?.messages_today || 0) / (new Date().getHours() || 1) / 60)}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-slate-500">Media Uploads heute</span>
|
|
<span className="font-medium">{stats?.traffic?.matrix?.media_uploads_today || 0}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-slate-500">Media Größe</span>
|
|
<span className="font-medium">{stats?.traffic?.matrix?.media_size_mb?.toFixed(1) || '0.0'} MB</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Jitsi Traffic */}
|
|
<div className="border border-slate-200 rounded-lg p-4">
|
|
<div className="flex items-center gap-2 mb-3">
|
|
<div className="w-3 h-3 bg-blue-500 rounded-full"></div>
|
|
<span className="text-sm font-medium text-slate-700">Jitsi Video</span>
|
|
</div>
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex justify-between">
|
|
<span className="text-slate-500">Video Streams aktiv</span>
|
|
<span className="font-medium">{stats?.traffic?.jitsi?.video_streams_active || (stats?.jitsi?.total_participants || 0)}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-slate-500">Audio Streams aktiv</span>
|
|
<span className="font-medium">{stats?.traffic?.jitsi?.audio_streams_active || (stats?.jitsi?.total_participants || 0)}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-slate-500">Bitrate geschätzt</span>
|
|
<span className="font-medium">{((stats?.jitsi?.total_participants || 0) * 1.5).toFixed(1)} Mbps</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* SysEleven Resource Recommendations */}
|
|
<div className="mt-4 p-4 bg-emerald-50 border border-emerald-200 rounded-lg">
|
|
<h4 className="text-sm font-semibold text-emerald-800 mb-2">SysEleven Empfehlung</h4>
|
|
<div className="text-sm text-emerald-700">
|
|
<p>Basierend auf aktuellem Traffic: <strong>{getResourceRecommendation(stats)}</strong></p>
|
|
<p className="mt-1 text-xs text-emerald-600">
|
|
Peak Teilnehmer: {stats?.jitsi?.peak_concurrent_users || 0} |
|
|
Ø Call-Dauer: {stats?.jitsi?.average_duration_minutes?.toFixed(0) || 0} Min. |
|
|
Calls heute: {stats?.jitsi?.meetings_today || 0}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|