import { useState, useEffect } from 'react' import { Save, RefreshCw, CheckCircle, AlertCircle, Sparkles, Shield, RotateCcw, Coins, Globe, ShieldCheck, } from 'lucide-react' import { Header } from '../components/layout/Header' import { Card, CardContent, Button, HelpTooltip } from '../components/common' import { endpoints, getErrorMessage } from '../api/client' import { t, setLocale, getCurrentLocale } from '../lib/i18n' import { useOnboarding } from '../hooks/useOnboarding' import { PingDot } from '../components/onboarding/PingDot' interface SettingsData { sample_count: number title_cache_limit: number language_detection: string default_language: string default_posts_count: number auto_publish: boolean debug_mode: boolean admin_language: string data_language: string enable_smart_linking: boolean low_credits_threshold: number copyscape_enabled: boolean copyscape_threshold: number copyscape_auto_rewrite: boolean } const defaultSettings: SettingsData = { sample_count: 10, title_cache_limit: 200, language_detection: 'auto', default_language: 'he', default_posts_count: 10, auto_publish: false, debug_mode: false, admin_language: getCurrentLocale(), data_language: window.bmaiSettings?.dataLanguage || 'he', enable_smart_linking: true, low_credits_threshold: 100, copyscape_enabled: true, copyscape_threshold: 5, copyscape_auto_rewrite: true, } export default function SettingsPage() { const [settings, setSettings] = useState(defaultSettings) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [resetting, setResetting] = useState(false) const [loadSuccess, setLoadSuccess] = useState(false) const [loadError, setLoadError] = useState(null) const [saveMessage, setSaveMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null) const [hasChanges, setHasChanges] = useState(false) const [savedSettings, setSavedSettings] = useState(defaultSettings) const { state: onboardingState, markSeen } = useOnboarding() useEffect(() => { if (!onboardingState.settingsSeen) { void markSeen('settingsSeen') } }, []) // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { const fetchSettings = async () => { try { const response = await endpoints.getSettings() if (response.data) { const merged = { ...defaultSettings, ...(response.data as Partial) } setSettings(merged) setSavedSettings(merged) } setLoadSuccess(true) setLoadError(null) } catch (err) { setLoadSuccess(false) setLoadError(getErrorMessage(err, t('Failed to load settings. Save is disabled to prevent data loss.'))) } setLoading(false) } fetchSettings() }, []) const update = (key: keyof SettingsData, value: unknown) => { setSettings(prev => { const next = { ...prev, [key]: value } setHasChanges(JSON.stringify(next) !== JSON.stringify(savedSettings)) return next }) } const handleSave = async () => { if (saving || !loadSuccess) return setSaving(true) setSaveMessage(null) const languageChanged = settings.admin_language !== getCurrentLocale() const dataLanguageChanged = settings.data_language !== (window.bmaiSettings?.dataLanguage || 'he') try { await endpoints.updateSettings(settings) setSavedSettings(settings) setHasChanges(false) if (window.bmaiSettings) { window.bmaiSettings.dataLanguage = settings.data_language } if (languageChanged || dataLanguageChanged) { setLocale(settings.admin_language) window.location.reload() return } setSaveMessage({ type: 'success', text: t('Settings saved successfully') }) setTimeout(() => setSaveMessage(null), 4000) } catch { setSaveMessage({ type: 'error', text: t('Error saving settings') }) } finally { setSaving(false) } } const handleReset = async () => { if (resetting || !loadSuccess) return if (!confirm(t('Reset all settings to defaults?'))) return setResetting(true) try { await endpoints.resetSettings() setSettings(defaultSettings) setSavedSettings(defaultSettings) setHasChanges(false) setSaveMessage({ type: 'success', text: t('Settings reset successfully') }) setTimeout(() => setSaveMessage(null), 4000) } catch { setSaveMessage({ type: 'error', text: t('Error resetting settings') }) } finally { setResetting(false) } } if (loading) { return (
{t('Loading settings...')}
) } return (
{loadError && (

{t('Failed to load settings. Save is disabled to prevent data loss.')}

)} {/* Content Generation */}

{t('Content Creation')}

{t('Default settings for AI content creation')}

update('sample_count', parseInt(e.target.value) || 5)} className="w-full px-3 py-2 border border-bc-gray-300 rounded-bc text-sm focus:border-bc-primary focus:ring-1 focus:ring-bc-primary outline-none transition-colors" />

{t('How many existing posts will be analyzed before creation')}

update('title_cache_limit', parseInt(e.target.value) || 200)} className="w-full px-3 py-2 border border-bc-gray-300 rounded-bc text-sm focus:border-bc-primary focus:ring-1 focus:ring-bc-primary outline-none transition-colors" />

{t('Higher value prevents duplicates better but uses more BoostCoins')}

{/* Note: 'Auto publish' global setting removed in 2.1.15. Publishing behavior is controlled per-plan via the publishing_schedule field (immediate / draft / drip-daily / drip-weekly) set when creating/editing a content plan. */}
update('low_credits_threshold', parseInt(e.target.value) || 0)} className="w-full max-w-xs px-3 py-2 border border-bc-gray-300 rounded-bc text-sm focus:border-bc-primary focus:ring-1 focus:ring-bc-primary outline-none transition-colors" />

{t('Show a warning banner when BoostCoins balance drops below this number')}

{/* Rich HTML templates toggle — disabled until backend template parsing is complete */}
{/* Admin UI Language */}

{t('Admin UI Language')}

{t('Changes the admin interface language (does not affect generated content)')}

{settings.admin_language !== getCurrentLocale() ? t('Save settings and reload the page to apply the language change.') : '' }

{t('Default working language')}

{t('Sets the default language for new reporters and content creation. Can be overridden per reporter.')}

{/* Copyscape — Plagiarism Check */}

{t('Copyscape — Plagiarism Check')}

{t('Check generated content for plagiarism before publishing')}

{settings.copyscape_enabled && ( <>
update('copyscape_threshold', parseInt(e.target.value) || 5)} className="w-24 px-3 py-2 border border-bc-gray-300 rounded-bc text-sm focus:border-bc-primary focus:ring-1 focus:ring-bc-primary outline-none transition-colors" /> %

{t('Content below this threshold is considered original')}

📊 {t('Estimated cost')}:{' '} {t('Estimated Copyscape cost: ~2 BoostCoins per article')}
)}
{/* Advanced */}

{t('Advanced')}

{t('Debug and reset settings')}

{/* Save bar */}
{hasChanges && ( {t('Unsaved changes')} )} {saveMessage && ( {saveMessage.type === 'success' ? : } {saveMessage.text} )}
) }