/** * TeamsBot Assistant View * * Wizard: Select/create module → Meeting link → Bot selection → "Start bot" */ import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { useCurrentInstance } from '../../../hooks/useCurrentInstance'; import * as teamsbotApi from '../../../api/teamsbotApi'; import type { MeetingModule, TeamsbotJoinMode, UserAccountStatus } from '../../../api/teamsbotApi'; import { getUserDataCache } from '../../../utils/userCache'; import { useLanguage } from '../../../providers/language/LanguageContext'; import styles from './Teamsbot.module.css'; type WizardStep = 'module' | 'meeting' | 'bot' | 'confirm'; const STEPS: WizardStep[] = ['module', 'meeting', 'bot', 'confirm']; export const TeamsbotAssistantView: React.FC = () => { const { t } = useLanguage(); const { instance, mandateId } = useCurrentInstance(); const instanceId = instance?.id || ''; const navigate = useNavigate(); const cachedUser = getUserDataCache(); const isSysAdmin = cachedUser?.isSysAdmin === true; const [searchParams] = useSearchParams(); const preselectedModuleId = searchParams.get('moduleId'); const [step, setStep] = useState(preselectedModuleId ? 'meeting' : 'module'); const [modules, setModules] = useState([]); const [moduleFilter, setModuleFilter] = useState(''); const [selectedModuleId, setSelectedModuleId] = useState(preselectedModuleId); const [newModuleTitle, setNewModuleTitle] = useState(''); const [createNewModule, setCreateNewModule] = useState(false); const [meetingLink, setMeetingLink] = useState(''); const [botName, setBotName] = useState('AI Assistant'); const [joinMode, setJoinMode] = useState('anonymous'); const [sessionContext, setSessionContext] = useState(''); const [userAccount, setUserAccount] = useState(null); const [showCredentialForm, setShowCredentialForm] = useState(false); const [credEmail, setCredEmail] = useState(''); const [credPassword, setCredPassword] = useState(''); const [savingCredentials, setSavingCredentials] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const stepIdx = STEPS.indexOf(step); const _loadModules = useCallback(async () => { if (!instanceId) return; try { const result = await teamsbotApi.listModules(instanceId); setModules(result || []); } catch (err) { console.error('Failed to load modules:', err); } }, [instanceId]); useEffect(() => { _loadModules(); }, [_loadModules]); useEffect(() => { if (joinMode === 'userAccount' && instanceId) { teamsbotApi.getUserAccount(instanceId).then(setUserAccount).catch(() => setUserAccount(null)); } }, [joinMode, instanceId]); const filteredModules = useMemo(() => { const q = moduleFilter.trim().toLowerCase(); if (!q) return modules; return modules.filter(m => m.title.toLowerCase().includes(q)); }, [modules, moduleFilter]); const modulePrefillKeyRef = useRef(''); useEffect(() => { if (!selectedModuleId || createNewModule) { modulePrefillKeyRef.current = ''; return; } const mod = modules.find(m => m.id === selectedModuleId); if (!mod) return; const key = `${selectedModuleId}:${mod.defaultMeetingLink ?? ''}:${mod.defaultBotName ?? ''}`; if (modulePrefillKeyRef.current === key) return; modulePrefillKeyRef.current = key; if (mod.defaultMeetingLink) setMeetingLink(mod.defaultMeetingLink); if (mod.defaultBotName) setBotName(mod.defaultBotName); }, [selectedModuleId, createNewModule, modules]); const _handleNext = () => { const nextIdx = stepIdx + 1; if (nextIdx < STEPS.length) setStep(STEPS[nextIdx]); }; const _handleBack = () => { const prevIdx = stepIdx - 1; if (prevIdx >= 0) setStep(STEPS[prevIdx]); }; const _handleStart = async () => { if (!meetingLink.trim()) { setError(t('Meeting-Link erforderlich')); return; } if (joinMode === 'userAccount' && !userAccount?.hasSavedCredentials && !credEmail) { setShowCredentialForm(true); setError(t('Bitte Microsoft-Zugangsdaten eingeben oder speichern.')); return; } const needsSave = joinMode === 'userAccount' && !userAccount?.hasSavedCredentials && credEmail && credPassword; const needsUpdate = joinMode === 'userAccount' && showCredentialForm && credEmail && credPassword; if (needsSave || needsUpdate) { try { setSavingCredentials(true); await teamsbotApi.saveUserAccount(instanceId, credEmail, credPassword); setUserAccount({ hasSavedCredentials: true, email: credEmail }); setShowCredentialForm(false); } catch (err: any) { setError(err?.message || t('Fehler beim Speichern der Zugangsdaten')); setSavingCredentials(false); return; } finally { setSavingCredentials(false); } } setLoading(true); setError(null); try { let moduleId = selectedModuleId; if (createNewModule && newModuleTitle.trim()) { const mod = await teamsbotApi.createModule(instanceId, { title: newModuleTitle.trim() }); moduleId = mod.id; } const result = await teamsbotApi.startSession(instanceId, { meetingLink: meetingLink.trim(), botName, moduleId: moduleId || undefined, joinMode, sessionContext: sessionContext.trim() || undefined, }); navigate(`/mandates/${mandateId}/teamsbot/${instanceId}/sessions?sessionId=${result.session.id}`); } catch (err: any) { setError(err?.message || t('Fehler beim Starten')); } finally { setLoading(false); } }; return (

{t('Neues Meeting starten')}

{STEPS.map((s, i) => (
))}
{error &&
{error}
}
{step === 'module' && (

{t('Meeting-Modul wählen')}

{!createNewModule && ( <> setModuleFilter(e.target.value)} aria-label={t('Modul suchen')} /> )} {createNewModule && ( setNewModuleTitle(e.target.value)} /> )}
)} {step === 'meeting' && (

{t('Meeting-Link und Beitritt')}

setMeetingLink(e.target.value)} autoFocus /> {joinMode === 'userAccount' && (
{userAccount?.hasSavedCredentials && !showCredentialForm ? (
{t('Gespeichert:')} {userAccount.email}
) : ( <>
setCredEmail(e.target.value)} disabled={savingCredentials} />
setCredPassword(e.target.value)} disabled={savingCredentials} />
{t('Zugangsdaten werden verschlüsselt gespeichert.')} {userAccount?.hasSavedCredentials && ( )} )}
)}