'use client' import { useState, useEffect } from 'react' import { type TabType, type MeetingStats, type Meeting, type Recording, type BreakoutRoom, getBackendUrl, getJitsiUrl, } from './types' export function useMeetPage() { // Tab state const [activeTab, setActiveTab] = useState('dashboard') const [stats, setStats] = useState({ active: 0, scheduled: 0, recordings: 0, participants: 0 }) const [scheduledMeetings, setScheduledMeetings] = useState([]) const [activeMeetings, setActiveMeetings] = useState([]) const [recordings, setRecordings] = useState([]) const [recordingsFilter, setRecordingsFilter] = useState('all') const [loading, setLoading] = useState(true) const [showNewMeetingModal, setShowNewMeetingModal] = useState(false) const [showJoinModal, setShowJoinModal] = useState(false) const [showTranscriptModal, setShowTranscriptModal] = useState(false) const [currentMeetingUrl, setCurrentMeetingUrl] = useState('') const [currentMeetingTitle, setCurrentMeetingTitle] = useState('') const [currentRecording, setCurrentRecording] = useState(null) const [transcriptText, setTranscriptText] = useState('') const [transcriptLoading, setTranscriptLoading] = useState(false) // Breakout rooms state const [breakoutRooms, setBreakoutRooms] = useState([ { id: '1', name: 'Raum 1', participants: [] }, { id: '2', name: 'Raum 2', participants: [] }, { id: '3', name: 'Raum 3', participants: [] }, ]) const [breakoutAssignment, setBreakoutAssignment] = useState('equal') const [breakoutTimer, setBreakoutTimer] = useState(15) const [hasActiveMeeting, setHasActiveMeeting] = useState(false) // Form state const [meetingType, setMeetingType] = useState('quick') const [meetingTitle, setMeetingTitle] = useState('') const [meetingDuration, setMeetingDuration] = useState(60) const [meetingDateTime, setMeetingDateTime] = useState('') const [enableLobby, setEnableLobby] = useState(true) const [enableRecording, setEnableRecording] = useState(false) const [muteOnStart, setMuteOnStart] = useState(true) const [creating, setCreating] = useState(false) const [errorMessage, setErrorMessage] = useState(null) // ============================================ // DATA FETCHING // ============================================ useEffect(() => { fetchData() }, []) const fetchData = async () => { setLoading(true) try { const [statsRes, scheduledRes, activeRes, recordingsRes] = await Promise.all([ fetch(`${getBackendUrl()}/api/meetings/stats`).catch(() => null), fetch(`${getBackendUrl()}/api/meetings/scheduled`).catch(() => null), fetch(`${getBackendUrl()}/api/meetings/active`).catch(() => null), fetch(`${getBackendUrl()}/api/recordings`).catch(() => null), ]) if (statsRes?.ok) { const statsData = await statsRes.json() setStats(statsData) } if (scheduledRes?.ok) { const scheduledData = await scheduledRes.json() setScheduledMeetings(scheduledData) } if (activeRes?.ok) { const activeData = await activeRes.json() setActiveMeetings(activeData) setHasActiveMeeting(activeData.length > 0) } if (recordingsRes?.ok) { const recordingsData = await recordingsRes.json() setRecordings(recordingsData.recordings || recordingsData || []) } } catch (error) { console.error('Failed to fetch meeting data:', error) } finally { setLoading(false) } } // ============================================ // ACTIONS // ============================================ const joinMeeting = (roomName: string, title: string) => { const url = `${getJitsiUrl()}/${roomName}#config.prejoinPageEnabled=false&config.defaultLanguage=de&interfaceConfig.SHOW_JITSI_WATERMARK=false` setCurrentMeetingUrl(url) setCurrentMeetingTitle(title) setShowJoinModal(true) } const createMeeting = async () => { setCreating(true) setErrorMessage(null) try { const response = await fetch(`${getBackendUrl()}/api/meetings/create`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: meetingType, title: meetingTitle || 'Neues Meeting', duration: meetingDuration, scheduled_at: meetingType !== 'quick' ? meetingDateTime : null, config: { enable_lobby: enableLobby, enable_recording: enableRecording, start_with_audio_muted: muteOnStart, start_with_video_muted: false, }, }), }) if (response.ok) { const meeting = await response.json() setShowNewMeetingModal(false) if (meetingType === 'quick') { joinMeeting(meeting.room_name, meetingTitle || 'Neues Meeting') } else { fetchData() } setMeetingTitle('') setMeetingType('quick') } else { const errorData = await response.json().catch(() => ({})) const errorMsg = errorData.detail || `Server-Fehler: ${response.status}` setErrorMessage(errorMsg) console.error('Failed to create meeting:', response.status, errorData) } } catch (error) { const errorMsg = error instanceof Error ? error.message : 'Netzwerkfehler - Backend nicht erreichbar' setErrorMessage(errorMsg) console.error('Failed to create meeting:', error) } finally { setCreating(false) } } const startQuickMeeting = async () => { setCreating(true) setErrorMessage(null) try { const response = await fetch(`${getBackendUrl()}/api/meetings/create`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'quick', title: 'Sofort-Meeting', duration: 60, config: { enable_lobby: false, enable_recording: false, start_with_audio_muted: true, start_with_video_muted: false, }, }), }) if (response.ok) { const meeting = await response.json() joinMeeting(meeting.room_name, 'Sofort-Meeting') } else { const errorData = await response.json().catch(() => ({})) const errorMsg = errorData.detail || `Server-Fehler: ${response.status}` setErrorMessage(errorMsg) console.error('Failed to create meeting:', response.status, errorData) } } catch (error) { const errorMsg = error instanceof Error ? error.message : 'Netzwerkfehler - Backend nicht erreichbar' setErrorMessage(errorMsg) console.error('Failed to start quick meeting:', error) } finally { setCreating(false) } } const openInNewTab = () => { window.open(currentMeetingUrl, '_blank') setShowJoinModal(false) } const copyMeetingLink = async (roomName: string) => { const url = `${getJitsiUrl()}/${roomName}` await navigator.clipboard.writeText(url) } // Recording actions const playRecording = (recordingId: string) => { window.open(`${getBackendUrl()}/meetings/recordings/${recordingId}/play`, '_blank') } const viewTranscript = async (recording: Recording) => { setCurrentRecording(recording) setShowTranscriptModal(true) setTranscriptLoading(true) setTranscriptText('') try { const response = await fetch(`${getBackendUrl()}/api/recordings/${recording.id}/transcription/text`) if (response.ok) { const data = await response.json() setTranscriptText(data.text || 'Kein Transkript verfuegbar') } else if (response.status === 404) { setTranscriptText('PENDING') } else { setTranscriptText('Fehler beim Laden des Transkripts') } } catch { setTranscriptText('Fehler beim Laden des Transkripts') } finally { setTranscriptLoading(false) } } const startTranscription = async (recordingId: string) => { try { const response = await fetch(`${getBackendUrl()}/api/recordings/${recordingId}/transcribe`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ language: 'de', model: 'large-v3' }), }) if (response.ok) { alert('Transkription gestartet! Dies kann einige Minuten dauern.') setShowTranscriptModal(false) fetchData() } } catch { alert('Fehler beim Starten der Transkription') } } const downloadRecording = (recordingId: string) => { window.location.href = `${getBackendUrl()}/api/recordings/${recordingId}/download` } const deleteRecording = async (recordingId: string) => { const reason = prompt('Grund fuer die Loeschung (DSGVO-Dokumentation):') if (!reason) return try { const response = await fetch(`${getBackendUrl()}/api/recordings/${recordingId}?reason=${encodeURIComponent(reason)}`, { method: 'DELETE', }) if (response.ok) { fetchData() } } catch { alert('Fehler beim Loeschen') } } const filteredRecordings = recordings.filter((r) => { if (recordingsFilter === 'all') return true return r.status === recordingsFilter }) const totalStorageBytes = recordings.reduce((sum, r) => sum + (r.file_size_bytes || 0), 0) const maxStorageGB = 10 const storagePercent = ((totalStorageBytes / (maxStorageGB * 1024 * 1024 * 1024)) * 100).toFixed(1) // Breakout room actions const addBreakoutRoom = () => { const newId = String(breakoutRooms.length + 1) setBreakoutRooms([...breakoutRooms, { id: newId, name: `Raum ${newId}`, participants: [] }]) } const removeBreakoutRoom = (id: string) => { setBreakoutRooms(breakoutRooms.filter((r) => r.id !== id)) } return { // Tab activeTab, setActiveTab, // Data stats, scheduledMeetings, activeMeetings, recordings, loading, // Modals showNewMeetingModal, setShowNewMeetingModal, showJoinModal, setShowJoinModal, showTranscriptModal, setShowTranscriptModal, currentMeetingUrl, currentMeetingTitle, currentRecording, transcriptText, transcriptLoading, // Recordings recordingsFilter, setRecordingsFilter, filteredRecordings, totalStorageBytes, maxStorageGB, storagePercent, // Breakout breakoutRooms, breakoutAssignment, setBreakoutAssignment, breakoutTimer, setBreakoutTimer, hasActiveMeeting, addBreakoutRoom, removeBreakoutRoom, // Form meetingType, setMeetingType, meetingTitle, setMeetingTitle, meetingDuration, setMeetingDuration, meetingDateTime, setMeetingDateTime, enableLobby, setEnableLobby, enableRecording, setEnableRecording, muteOnStart, setMuteOnStart, creating, errorMessage, setErrorMessage, // Actions fetchData, createMeeting, startQuickMeeting, joinMeeting, openInNewTab, copyMeetingLink, playRecording, viewTranscript, startTranscription, downloadRecording, deleteRecording, } }