import { useState, useEffect, useRef, useCallback } from 'react'; import axios from '../services/api'; const LANGUAGES = [ { value: 'text', label: 'Text' }, { value: 'python', label: 'Python' }, { value: 'javascript', label: 'JavaScript' }, { value: 'sql', label: 'SQL' }, { value: 'bash', label: 'Bash' }, { value: 'json', label: 'JSON' }, ]; function NotesTool() { const [notes, setNotes] = useState([]); const [selected, setSelected] = useState(null); const [title, setTitle] = useState('Neue Notiz'); const [content, setContent] = useState(''); const [language, setLanguage] = useState('text'); const [saved, setSaved] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const [error, setError] = useState(''); const debounceRef = useRef(null); // Tracks whether the user has actually edited the current note. // Prevents auto-save from firing just because a note was selected. const isDirtyRef = useRef(false); const isNew = selected === null; const loadNotes = useCallback(async () => { try { const res = await axios.get('/api/notes'); setNotes(res.data); } catch { setError('Fehler beim Laden der Notizen'); } }, []); useEffect(() => { loadNotes(); }, [loadNotes]); const selectNote = (note) => { isDirtyRef.current = false; setSelected(note.id); setTitle(note.title); setContent(note.content || ''); setLanguage(note.language || 'text'); setSaved(false); setConfirmDelete(false); setError(''); }; const newNote = () => { isDirtyRef.current = false; setSelected(null); setTitle('Neue Notiz'); setContent(''); setLanguage('text'); setSaved(false); setConfirmDelete(false); setError(''); }; const save = async () => { setError(''); try { if (isNew) { const res = await axios.post('/api/notes', { title, content, language }); isDirtyRef.current = false; setSelected(res.data.id); await loadNotes(); } else { await axios.put(`/api/notes/${selected}`, { title, content, language }); isDirtyRef.current = false; await loadNotes(); } setSaved(true); setTimeout(() => setSaved(false), 2000); } catch (err) { setError(err.response?.data?.message || 'Fehler beim Speichern'); } }; const deleteNote = async () => { if (!confirmDelete) { setConfirmDelete(true); return; } try { await axios.delete(`/api/notes/${selected}`); setNotes(prev => prev.filter(n => n.id !== selected)); newNote(); } catch (err) { setError(err.response?.data?.message || 'Fehler beim Löschen'); } }; // Auto-save for existing notes — only fires when the user actually edited content. // selected is in deps so the closure is never stale when switching between notes. useEffect(() => { if (isNew || !isDirtyRef.current) return; clearTimeout(debounceRef.current); debounceRef.current = setTimeout(async () => { if (!isDirtyRef.current) return; try { await axios.put(`/api/notes/${selected}`, { title, content, language }); isDirtyRef.current = false; await loadNotes(); setSaved(true); setTimeout(() => setSaved(false), 1500); } catch { /* silent — manual save still available */ } }, 1000); return () => clearTimeout(debounceRef.current); }, [title, content, language, selected, isNew, loadNotes]); const formatDate = (iso) => { if (!iso) return ''; return new Date(iso).toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: '2-digit', hour: '2-digit', minute: '2-digit', }); }; return (

Notizen & Snippets

{/* Sidebar */}
{notes.length === 0 ? (

Keine Notizen vorhanden

) : notes.map((note) => (
selectNote(note)} style={{ padding: '10px 14px', cursor: 'pointer', borderBottom: '1px solid var(--border)', background: selected === note.id ? 'rgba(34,211,238,0.08)' : 'transparent', borderLeft: selected === note.id ? '3px solid var(--accent)' : '3px solid transparent', }} >
{note.title}
{formatDate(note.updated_at)}
))}
{/* Editor */}
{ isDirtyRef.current = true; setTitle(e.target.value); setSaved(false); }} placeholder="Titel" style={{ flex: 1, margin: 0, minWidth: '150px' }} />