import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useCurrentInstance } from '../../../hooks/useCurrentInstance'; import * as teamsbotApi from '../../../api/teamsbotApi'; import type { TeamsbotConfig, ConfigUpdateRequest, VoiceOption } from '../../../api/teamsbotApi'; import { FaPlay, FaSpinner } from 'react-icons/fa'; import styles from './Teamsbot.module.css'; /** Format voice name for display: "de-DE-Wavenet-A" -> "Wavenet A" + gender */ function _formatVoiceName(voice: VoiceOption): string { const parts = voice.name.split('-'); const type = parts[2] || ''; // Wavenet, Neural2, Standard, etc. const variant = parts[3] || ''; const gender = voice.ssmlGender === 'FEMALE' ? 'Frau' : voice.ssmlGender === 'MALE' ? 'Mann' : ''; return `${gender} ${variant} (${type})`.trim(); } /** * TeamsbotSettingsView - Bot configuration for a feature instance. */ export const TeamsbotSettingsView: React.FC = () => { const { instance } = useCurrentInstance(); const instanceId = instance?.id || ''; const [_config, setConfig] = useState(null); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [testingVoice, setTestingVoice] = useState(false); const [error, setError] = useState(null); const [successMsg, setSuccessMsg] = useState(null); const audioRef = useRef(null); // Form state const [formData, setFormData] = useState({}); // Dynamic voice data from Google TTS API const [languages, setLanguages] = useState([]); const [voices, setVoices] = useState([]); const [loadingVoices, setLoadingVoices] = useState(false); const _loadConfig = useCallback(async () => { if (!instanceId) return; try { setLoading(true); // Load per-user settings (merged with instance defaults) const [settingsResult, languagesResult] = await Promise.all([ teamsbotApi.getUserSettings(instanceId), teamsbotApi.fetchLanguages(), ]); const effectiveConfig = settingsResult.effectiveConfig; setConfig(effectiveConfig); setFormData(effectiveConfig); setLanguages(languagesResult); // Load voices for the current language const lang = effectiveConfig.language || 'de-DE'; const voicesResult = await teamsbotApi.fetchVoices(lang); setVoices(voicesResult); setError(null); } catch (err: any) { setError(err.message || 'Fehler beim Laden der Konfiguration'); } finally { setLoading(false); } }, [instanceId]); useEffect(() => { _loadConfig(); }, [_loadConfig]); const _handleSave = async () => { if (!instanceId) return; setSaving(true); setError(null); setSuccessMsg(null); try { // Save as per-user settings (not instance config) const result = await teamsbotApi.updateUserSettings(instanceId, formData); setConfig(result.effectiveConfig); setFormData(result.effectiveConfig); setSuccessMsg('Einstellungen gespeichert'); setTimeout(() => setSuccessMsg(null), 3000); } catch (err: any) { setError(err.message || 'Fehler beim Speichern'); } finally { setSaving(false); } }; const _updateField = (field: keyof ConfigUpdateRequest, value: any) => { setFormData(prev => ({ ...prev, [field]: value })); }; const _handleLanguageChange = async (language: string) => { _updateField('language', language); // Load voices for the new language dynamically setLoadingVoices(true); try { const voicesResult = await teamsbotApi.fetchVoices(language); setVoices(voicesResult); // Auto-select first voice if current voice doesn't match if (voicesResult.length > 0 && !voicesResult.find(v => v.name === formData.voiceId)) { _updateField('voiceId', voicesResult[0].name); } } catch { setVoices([]); } finally { setLoadingVoices(false); } }; const _handleTestVoice = async () => { if (!instanceId) return; setTestingVoice(true); try { const language = formData.language || 'de-DE'; const botName = formData.botName || 'AI Assistant'; const result = await teamsbotApi.testVoice(instanceId, botName, language, formData.voiceId); if (result.success && result.audio) { // Play audio from base64 const audioBlob = new Blob( [Uint8Array.from(atob(result.audio), c => c.charCodeAt(0))], { type: 'audio/mp3' } ); const audioUrl = URL.createObjectURL(audioBlob); if (audioRef.current) { audioRef.current.pause(); } const audio = new Audio(audioUrl); audioRef.current = audio; audio.play(); audio.onended = () => URL.revokeObjectURL(audioUrl); } else { setError(result.error || 'Stimmtest fehlgeschlagen'); setTimeout(() => setError(null), 3000); } } catch (err: any) { setError(err.message || 'Stimmtest fehlgeschlagen'); setTimeout(() => setError(null), 3000); } finally { setTestingVoice(false); } }; if (loading) return
Lade Konfiguration...
; return (

Bot-Einstellungen

{error &&
{error}
} {successMsg &&
{successMsg}
} {/* Bot Identity */}

Bot-Identitaet

_updateField('botName', e.target.value)} placeholder="AI Assistant" /> Default-Name fuer den Bot im Meeting. Falls keiner angegeben, wird der Name des System-Bots verwendet (z.B. "Nyla Larsson").
{/* AI Behavior */}

AI-Verhalten