import React, { useContext, useRef, useState, useEffect } from 'react'; import { SettingsIcon, AlertTriangleIcon, Trash2Icon, KeyIcon } from '../components/icons'; import { AuthContext } from '../context/AuthContext'; import { useLocalStorage } from '../hooks/useLocalStorage'; import type { BonsaiTree, UserTool } from '../types'; import { reinitializeAI } from '../services/geminiService'; const SettingsView: React.FC = () => { const { logout, updateCredentials } = useContext(AuthContext); const [, setTrees] = useLocalStorage('bonsai-diary-trees', []); const [, setToolkit] = useLocalStorage('user-toolkit', []); const importFileRef = useRef(null); const [apiKey, setApiKey] = useState(''); const [saveStatus, setSaveStatus] = useState<'idle' | 'saved'>('idle'); // State for credentials change const [currentPassword, setCurrentPassword] = useState(''); const [newLoginName, setNewLoginName] = useState(''); const [newPassword, setNewPassword] = useState(''); const [credentialStatus, setCredentialStatus] = useState<{ type: 'idle' | 'success' | 'error', message: string }>({ type: 'idle', message: '' }); const [isUpdatingCreds, setIsUpdatingCreds] = useState(false); useEffect(() => { const storedKey = window.localStorage.getItem('gemini-api-key') || ''; setApiKey(storedKey); }, []); const handleSaveKey = () => { window.localStorage.setItem('gemini-api-key', apiKey); reinitializeAI(); setSaveStatus('saved'); setTimeout(() => setSaveStatus('idle'), 2000); }; const handleClearKey = () => { if (window.confirm('Are you sure you want to clear your API key?')) { window.localStorage.removeItem('gemini-api-key'); setApiKey(''); reinitializeAI(); } }; const handleCredentialChange = (e: React.FormEvent) => { e.preventDefault(); setIsUpdatingCreds(true); const result = updateCredentials(currentPassword, newLoginName, newPassword); if (result.success) { setCredentialStatus({ type: 'success', message: "Credentials updated! You will be logged out to sign in again." }); setTimeout(() => { logout(); }, 2000); } else { setCredentialStatus({ type: 'error', message: result.message }); setTimeout(() => setCredentialStatus({ type: 'idle', message: '' }), 3000); setIsUpdatingCreds(false); } }; const handleExport = () => { const treeKey = `yuki-app-bonsai-diary-trees`; const toolKey = `yuki-app-user-toolkit`; const data = { trees: JSON.parse(window.localStorage.getItem(treeKey) || '[]'), toolkit: JSON.parse(window.localStorage.getItem(toolKey) || '[]'), }; const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `yuki-bonsai-backup-${new Date().toISOString().split('T')[0]}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; const handleImport = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { try { const text = e.target?.result; if (typeof text !== 'string') throw new Error("File is not readable"); const data = JSON.parse(text); if (Array.isArray(data.trees) && Array.isArray(data.toolkit)) { if(window.confirm("This will overwrite your current data. Are you sure you want to proceed?")) { setTrees(data.trees); setToolkit(data.toolkit); alert("Data imported successfully!"); } } else { throw new Error("Invalid file format."); } } catch (err) { alert(`Failed to import data: ${err instanceof Error ? err.message : "Unknown error"}`); } }; reader.readAsText(file); event.target.value = ''; // Reset file input }; const handleDelete = () => { if (window.confirm("DANGER: Are you absolutely sure you want to delete all your trees and toolkit data? This action cannot be undone.")) { if (window.confirm("FINAL WARNING: This is your last chance. All data will be permanently deleted. Continue?")) { setTrees([]); setToolkit([]); alert("All your data has been deleted."); logout(); } } }; return (

Settings

Manage your application data and preferences.

Change Login Credentials

Update your login name and secret password. If you haven't set custom credentials yet, the "Current Secret Password" is the one you originally received. After updating, you will be logged out.

setCurrentPassword(e.target.value)} className="mt-1 block w-full rounded-md border-0 py-2 px-3 text-stone-900 shadow-sm ring-1 ring-inset ring-stone-300 focus:ring-2 focus:ring-inset focus:ring-green-600" required />
setNewLoginName(e.target.value)} className="mt-1 block w-full rounded-md border-0 py-2 px-3 text-stone-900 shadow-sm ring-1 ring-inset ring-stone-300 focus:ring-2 focus:ring-inset focus:ring-green-600" placeholder="e.g., my-bonsai-corner" required />
setNewPassword(e.target.value)} className="mt-1 block w-full rounded-md border-0 py-2 px-3 text-stone-900 shadow-sm ring-1 ring-inset ring-stone-300 focus:ring-2 focus:ring-inset focus:ring-green-600" placeholder="Enter a strong new password" required />
{credentialStatus.type === 'success' &&

{credentialStatus.message}

} {credentialStatus.type === 'error' &&

{credentialStatus.message}

}

Gemini API Key

To use the AI features, you need a Google Gemini API key. You can generate a free key from{' '} Google AI Studio .

setApiKey(e.target.value)} className="block w-full rounded-md border-0 py-2 px-3 text-stone-900 shadow-sm ring-1 ring-inset ring-stone-300 placeholder:text-stone-400 focus:ring-2 focus:ring-inset focus:ring-green-600 sm:text-sm sm:leading-6" placeholder="Enter your Gemini API key" />

Data Management

Danger Zone

This action is irreversible. Please export your data first if you want to keep a backup.

); }; export default SettingsView;