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>
67 lines
2.0 KiB
TypeScript
67 lines
2.0 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect, useCallback } from 'react'
|
|
import { CommunicationStats, ActiveMeeting, RecentRoom } from './types'
|
|
|
|
const API_BASE = '/api/admin/communication'
|
|
|
|
export function useCommunicationStats() {
|
|
const [stats, setStats] = useState<CommunicationStats | null>(null)
|
|
const [activeMeetings, setActiveMeetings] = useState<ActiveMeeting[]>([])
|
|
const [recentRooms, setRecentRooms] = useState<RecentRoom[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
const fetchStats = useCallback(async () => {
|
|
try {
|
|
const response = await fetch(`${API_BASE}/stats`)
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}`)
|
|
}
|
|
const data = await response.json()
|
|
setStats(data)
|
|
setActiveMeetings(data.active_meetings || [])
|
|
setRecentRooms(data.recent_rooms || [])
|
|
setError(null)
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Verbindungsfehler')
|
|
// Set mock data for display purposes when API unavailable
|
|
setStats({
|
|
matrix: {
|
|
total_users: 0,
|
|
active_users: 0,
|
|
total_rooms: 0,
|
|
active_rooms: 0,
|
|
messages_today: 0,
|
|
messages_this_week: 0,
|
|
status: 'offline'
|
|
},
|
|
jitsi: {
|
|
active_meetings: 0,
|
|
total_participants: 0,
|
|
meetings_today: 0,
|
|
average_duration_minutes: 0,
|
|
peak_concurrent_users: 0,
|
|
total_minutes_today: 0,
|
|
status: 'offline'
|
|
},
|
|
last_updated: new Date().toISOString()
|
|
})
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
fetchStats()
|
|
}, [fetchStats])
|
|
|
|
// Auto-refresh every 15 seconds
|
|
useEffect(() => {
|
|
const interval = setInterval(fetchStats, 15000)
|
|
return () => clearInterval(interval)
|
|
}, [fetchStats])
|
|
|
|
return { stats, activeMeetings, recentRooms, loading, error, fetchStats }
|
|
}
|