frontend_nyla/src/pages/views/commcoach/CommcoachSettingsView.tsx
2026-03-02 00:51:25 +01:00

243 lines
8.4 KiB
TypeScript

/**
* CommCoach Settings View
*
* User profile settings: voice preferences, reminders, email notifications.
*/
import React, { useState, useEffect, useCallback } from 'react';
import { useApiRequest } from '../../../hooks/useApi';
import { useInstanceId } from '../../../hooks/useCurrentInstance';
import {
getProfileApi, updateProfileApi,
getVoiceLanguagesApi, getVoiceVoicesApi, testVoiceApi,
type CoachingUserProfile,
} from '../../../api/commcoachApi';
import styles from './CommcoachSettingsView.module.css';
export const CommcoachSettingsView: React.FC = () => {
const { request } = useApiRequest();
const instanceId = useInstanceId();
const [profile, setProfile] = useState<CoachingUserProfile | null>(null);
const [languages, setLanguages] = useState<any[]>([]);
const [voices, setVoices] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [testing, setTesting] = useState(false);
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState<string | null>(null);
const [language, setLanguage] = useState('de-DE');
const [voiceId, setVoiceId] = useState('');
const [reminderEnabled, setReminderEnabled] = useState(false);
const [reminderTime, setReminderTime] = useState('09:00');
const [emailEnabled, setEmailEnabled] = useState(true);
useEffect(() => {
if (!instanceId) return;
const loadData = async () => {
setLoading(true);
try {
const [profileData, languagesData] = await Promise.all([
getProfileApi(request, instanceId),
getVoiceLanguagesApi(request, instanceId),
]);
setProfile(profileData);
setLanguages(languagesData || []);
if (profileData) {
setLanguage(profileData.preferredLanguage || 'de-DE');
setVoiceId(profileData.preferredVoice || '');
setReminderEnabled(profileData.dailyReminderEnabled || false);
setReminderTime(profileData.dailyReminderTime || '09:00');
setEmailEnabled(profileData.emailSummaryEnabled !== false);
}
const voicesData = await getVoiceVoicesApi(request, instanceId, profileData?.preferredLanguage || 'de-DE');
setVoices(voicesData || []);
} catch (err: any) {
setError(err.message || 'Fehler beim Laden');
} finally {
setLoading(false);
}
};
loadData();
}, [request, instanceId]);
const handleLanguageChange = useCallback(async (newLang: string) => {
setLanguage(newLang);
if (!instanceId) return;
try {
const voicesData = await getVoiceVoicesApi(request, instanceId, newLang);
setVoices(voicesData || []);
setVoiceId('');
} catch { /* ignore */ }
}, [request, instanceId]);
const handleSave = useCallback(async () => {
if (!instanceId) return;
setSaving(true);
setError(null);
setSuccess(null);
try {
const updated = await updateProfileApi(request, instanceId, {
preferredLanguage: language,
preferredVoice: voiceId || null,
dailyReminderEnabled: reminderEnabled,
dailyReminderTime: reminderTime,
emailSummaryEnabled: emailEnabled,
});
setProfile(updated);
setSuccess('Einstellungen gespeichert');
setTimeout(() => setSuccess(null), 3000);
} catch (err: any) {
setError(err.message || 'Fehler beim Speichern');
} finally {
setSaving(false);
}
}, [request, instanceId, language, voiceId, reminderEnabled, reminderTime, emailEnabled]);
const handleTestVoice = useCallback(async () => {
if (!instanceId) return;
setTesting(true);
try {
const result = await testVoiceApi(request, instanceId, {
language,
voiceId: voiceId || undefined,
});
if (result.success && result.audio) {
const audioData = `data:audio/mp3;base64,${result.audio}`;
const audio = new Audio(audioData);
audio.play();
}
} catch (err: any) {
setError('Sprachtest fehlgeschlagen');
} finally {
setTesting(false);
}
}, [request, instanceId, language, voiceId]);
if (loading) {
return <div className={styles.loading}>Einstellungen werden geladen...</div>;
}
return (
<div className={styles.settings}>
<h2 className={styles.heading}>Coaching-Einstellungen</h2>
{error && <div className={styles.error}>{error}</div>}
{success && <div className={styles.success}>{success}</div>}
{/* Voice Settings */}
<div className={styles.section}>
<h3 className={styles.sectionTitle}>Sprache und Stimme</h3>
<div className={styles.field}>
<label className={styles.label}>Sprache</label>
<select className={styles.select} value={language} onChange={e => handleLanguageChange(e.target.value)}>
{languages.length > 0 ? (
languages.map((lang: any) => (
<option key={lang.code || lang} value={lang.code || lang}>
{lang.name || lang.code || lang}
</option>
))
) : (
<>
<option value="de-DE">Deutsch</option>
<option value="en-US">English (US)</option>
<option value="fr-FR">Francais</option>
</>
)}
</select>
</div>
<div className={styles.field}>
<label className={styles.label}>Stimme</label>
<div className={styles.voiceRow}>
<select className={styles.select} value={voiceId} onChange={e => setVoiceId(e.target.value)}>
<option value="">Standard</option>
{voices.map((v: any) => (
<option key={v.name || v} value={v.name || v}>
{v.displayName || v.name || v}
</option>
))}
</select>
<button className={styles.testBtn} onClick={handleTestVoice} disabled={testing}>
{testing ? 'Teste...' : 'Testen'}
</button>
</div>
</div>
</div>
{/* Reminder Settings */}
<div className={styles.section}>
<h3 className={styles.sectionTitle}>Erinnerungen</h3>
<div className={styles.field}>
<label className={styles.checkboxLabel}>
<input
type="checkbox"
checked={reminderEnabled}
onChange={e => setReminderEnabled(e.target.checked)}
/>
Taegliche Coaching-Erinnerung per E-Mail
</label>
</div>
{reminderEnabled && (
<div className={styles.field}>
<label className={styles.label}>Uhrzeit</label>
<input
type="time"
className={styles.input}
value={reminderTime}
onChange={e => setReminderTime(e.target.value)}
/>
</div>
)}
<div className={styles.field}>
<label className={styles.checkboxLabel}>
<input
type="checkbox"
checked={emailEnabled}
onChange={e => setEmailEnabled(e.target.checked)}
/>
Session-Zusammenfassung per E-Mail senden
</label>
</div>
</div>
{/* Stats */}
{profile && (
<div className={styles.section}>
<h3 className={styles.sectionTitle}>Statistik</h3>
<div className={styles.statsGrid}>
<div className={styles.statItem}>
<span className={styles.statValue}>{profile.totalSessions}</span>
<span className={styles.statLabel}>Sessions gesamt</span>
</div>
<div className={styles.statItem}>
<span className={styles.statValue}>{profile.totalMinutes}</span>
<span className={styles.statLabel}>Minuten gesamt</span>
</div>
<div className={styles.statItem}>
<span className={styles.statValue}>{profile.streakDays}</span>
<span className={styles.statLabel}>Aktueller Streak</span>
</div>
<div className={styles.statItem}>
<span className={styles.statValue}>{profile.longestStreak}</span>
<span className={styles.statLabel}>Laengster Streak</span>
</div>
</div>
</div>
)}
<button className={styles.saveBtn} onClick={handleSave} disabled={saving}>
{saving ? 'Speichern...' : 'Einstellungen speichern'}
</button>
</div>
);
};
export default CommcoachSettingsView;