import React, { useState, useEffect } from 'react' import { Eye, EyeOff, ArrowLeft } from 'lucide-react' import ProfileLogo from '../Logos/ProfileLogo' import { useTheme } from '../../contexts/ThemeContext'; import { useIsSettingsModalOpen, useSetIsSettingsModalOpen } from '../../contexts/UIContext'; import { useAuth } from '../../contexts/AuthContext'; import { IS_WORDPRESS } from '../../constants'; import useRenderTracker from '../../hooks/useRenderTracker'; const spinnerProps = { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [ , ] }; export default function SettingsModal() { const isSettingsModalOpen = useIsSettingsModalOpen(); const setIsSettingsModalOpen = useSetIsSettingsModalOpen(); const { theme, setTheme } = useTheme(); const { userInfo, logout, updateEmail, updatePassword, requestVerification, verifyCode } = useAuth(); const [activeTab, setActiveTab] = useState<'profile' | 'preferences'>('profile') const [isEditingEmail, setIsEditingEmail] = useState(false) const [isEditingPassword, setIsEditingPassword] = useState(false) const [newEmail, setNewEmail] = useState('') const [currentPassword, setCurrentPassword] = useState('') const [newPassword, setNewPassword] = useState('') const [confirmPassword, setConfirmPassword] = useState('') const [selectedTheme, setSelectedTheme] = useState(theme) const [showCurrentPassword, setShowCurrentPassword] = useState(false) const [showNewPassword, setShowNewPassword] = useState(false) const [showConfirmPassword, setShowConfirmPassword] = useState(false) const [emailCurrentPassword, setEmailCurrentPassword] = useState('') const [showEmailCurrentPassword, setShowEmailCurrentPassword] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false); const [successMessage, setSuccessMessage] = useState(null); const [verificationCode, setVerificationCode] = useState(''); const [emailVerificationStep, setEmailVerificationStep] = useState<'initial' | 'verifying' | 'complete'>('initial'); const [emailChangeData, setEmailChangeData] = useState<{ newEmail: string; currentPassword: string; } | null>(null); const [emailError, setEmailError] = useState(null); const [verificationError, setVerificationError] = useState(null); const [infoMessage, setInfoMessage] = useState(null); const [passwordError, setPasswordError] = useState(null); useRenderTracker('SettingsModal', { isSettingsModalOpen, activeTab, isEditingEmail, isEditingPassword, selectedTheme, emailCurrentPassword, newEmail, currentPassword, newPassword, confirmPassword, showCurrentPassword, showNewPassword, showConfirmPassword, userInfo, useIsSettingsModalOpen, useSetIsSettingsModalOpen, theme, setTheme, logout, updateEmail, updatePassword, requestVerification, verifyCode, }); // Update selectedTheme when userInfo.theme changes useEffect(() => { setSelectedTheme(theme); }, [theme]); if (!isSettingsModalOpen) return null const resetState = () => { setActiveTab('profile'); resetEmailChangeState(); setIsEditingEmail(false); setIsEditingPassword(false); setEmailCurrentPassword(''); setNewEmail(''); setCurrentPassword(''); setNewPassword(''); setConfirmPassword(''); setShowCurrentPassword(false); setShowNewPassword(false); setShowConfirmPassword(false); setShowEmailCurrentPassword(false); setVerificationCode(''); setEmailVerificationStep('initial'); setEmailChangeData(null); setEmailError(null); setVerificationError(null); setInfoMessage(null); setSuccessMessage(null); }; const handleClose = () => { resetState(); setIsSettingsModalOpen(false); }; const handleEmailUpdate = async (e: React.FormEvent) => { e.preventDefault(); setIsSubmitting(true); setEmailError(null); setSuccessMessage(null); try { const result = await updateEmail(newEmail, emailCurrentPassword); if (result.requiresVerification) { await requestVerification(newEmail, 'email_change'); setEmailChangeData({ newEmail, currentPassword: emailCurrentPassword }); setEmailVerificationStep('verifying'); } } catch (error) { setEmailError(error instanceof Error ? error.message : 'Failed to update email'); } finally { setIsSubmitting(false); } }; // Add validation functions const validatePassword = (password: string): string | null => { if (password.length < 8) { return 'Password must be at least 8 characters long'; } return null; }; const handlePasswordUpdate = async (e: React.FormEvent) => { e.preventDefault(); setPasswordError(null); setSuccessMessage(null); // Client-side validation const passwordValidationError = validatePassword(newPassword); if (passwordValidationError) { setPasswordError(passwordValidationError); return; } if (newPassword !== confirmPassword) { setPasswordError('New passwords do not match'); return; } setIsSubmitting(true); try { await updatePassword(currentPassword, newPassword); resetState(); setSuccessMessage('Password updated successfully!'); } catch (error) { // Improved error handling if (error instanceof Error) { setPasswordError(error.message); } else if (typeof error === 'object' && error !== null) { // Handle validation errors from the server const errorObj = error as { detail?: string }; setPasswordError(errorObj.detail || 'Failed to update password'); } else { setPasswordError('An unexpected error occurred'); } } finally { setIsSubmitting(false); } }; const handleThemeUpdate = async (newTheme: 'system' | 'light' | 'dark') => { setSelectedTheme(newTheme) setTheme(newTheme) } const handleVerificationSubmit = async (e: React.FormEvent) => { e.preventDefault(); setIsSubmitting(true); setVerificationError(null); try { if (!emailChangeData) throw new Error('Missing email change data'); const isValid = await verifyCode(emailChangeData.newEmail, verificationCode); if (isValid) { resetState(); setSuccessMessage('Email updated successfully!'); } } catch (error) { setVerificationError(error instanceof Error ? error.message : 'Verification failed'); } finally { setIsSubmitting(false); } }; const resetEmailChangeState = () => { setIsEditingEmail(false); setEmailVerificationStep('initial'); setEmailChangeData(null); setSuccessMessage(null); setEmailError(null); setVerificationError(null); setInfoMessage(null); setNewEmail(''); setEmailCurrentPassword(''); setVerificationCode(''); }; return (

Settings

{!isEditingEmail && !isEditingPassword && (
)} {activeTab === 'profile' && (
{!isEditingEmail && !isEditingPassword && ( <>

{userInfo?.name}

{userInfo?.email}

)} {isEditingEmail && (

Change Email

{emailVerificationStep === 'verifying' && (

We've sent a verification code to {emailChangeData?.newEmail}

Please check your inbox (and spam folder) for the code.

)} {emailVerificationStep === 'initial' ? (
setEmailCurrentPassword(e.target.value)} className="w-full px-4 py-3 rounded-2xl border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-1 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-500/15 text-gray-900 dark:text-gray-100" required />
setNewEmail(e.target.value)} className="w-full px-4 py-3 rounded-2xl border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-1 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-500/15 text-gray-900 dark:text-gray-100" required />
{emailError && (
{emailError}
)}
) : emailVerificationStep === 'verifying' && (
setVerificationCode(e.target.value)} className="w-full px-4 py-3 rounded-2xl border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-1 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-500/15 text-gray-900 dark:text-gray-100" required />
{verificationError && (
{verificationError}
)}
)} {infoMessage && (

{infoMessage}

)}
)} {isEditingPassword && (

Change Password

setCurrentPassword(e.target.value)} className="w-full px-4 py-3 rounded-2xl border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-1 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-500/15 text-gray-900 dark:text-gray-100" required />
{ setNewPassword(e.target.value); }} className="w-full px-4 py-3 rounded-2xl border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-1 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-500/15 text-gray-900 dark:text-gray-100" required />
setConfirmPassword(e.target.value)} className="w-full px-4 py-3 rounded-2xl border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-1 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-500/15 text-gray-900 dark:text-gray-100" required />
{passwordError && (
{passwordError}
)}
)}
)} {activeTab === 'preferences' && (
{['system', 'light', 'dark'].map((theme) => ( ))}
{!IS_WORDPRESS && (

Allow anonymized conversations to be published as WordPress articles

)}
)}
{successMessage && (
{successMessage}
)}
) } const SeoConsentToggle = () => { const { userInfo, updateSeoConsent } = useAuth(); const [isEnabled, setIsEnabled] = useState(() => userInfo?.seo_consent === true); const [isSubmitting, setIsSubmitting] = useState(false); useEffect(() => { if (userInfo !== undefined) { setIsEnabled(userInfo?.seo_consent === true); } }, [userInfo]); if (IS_WORDPRESS) { return null; } const toggleConsent = async () => { if (isSubmitting) return; setIsSubmitting(true); const currentValue = isEnabled === true; const newValue = !currentValue; try { await updateSeoConsent(newValue); setIsEnabled(newValue); } catch (error) { console.error("Failed to update consent:", error); } finally { setIsSubmitting(false); } }; if (!userInfo) return
; return ( ); };