'use client' import { useState, useRef, useEffect, useCallback } from 'react' import { useRouter } from 'next/navigation' import { toast } from 'sonner' import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { Button } from '@/components/ui/button' import { cn } from '@/lib/utils' import { Plus, Loader2, Sparkles, X, Text, ChevronDown } from 'lucide-react' import { inferNoteType, formatContentForType } from '@/lib/type-inference' import { NoteType } from '@/types/note' interface TypeSuggestion { type: NoteType confidence: 'high' | 'medium' | 'low' reason: string formattedContent: string } const TYPE_LABELS: Record = { command: 'Comando', snippet: 'Snippet', procedure: 'Procedimiento', recipe: 'Receta', decision: 'Decisión', inventory: 'Inventario', note: 'Nota', } export function QuickAdd() { const [value, setValue] = useState('') const [isLoading, setIsLoading] = useState(false) const [isOpen, setIsOpen] = useState(false) const [isMultiline, setIsMultiline] = useState(false) const [typeSuggestion, setTypeSuggestion] = useState(null) const [dismissedSuggestion, setDismissedSuggestion] = useState(false) const inputRef = useRef(null) const textareaRef = useRef(null) const popupRef = useRef(null) const router = useRouter() const detectContentType = useCallback((text: string) => { if (!text.trim() || text.length < 10) { setTypeSuggestion(null) return } if (dismissedSuggestion) return const suggestion = inferNoteType(text) if (suggestion && suggestion.confidence === 'high') { const formatted = formatContentForType(text, suggestion.type) setTypeSuggestion({ ...suggestion, formattedContent: formatted, }) } else { setTypeSuggestion(null) } }, [dismissedSuggestion]) const handlePaste = (e: React.ClipboardEvent) => { setDismissedSuggestion(false) setTimeout(() => { detectContentType(value) }, 0) } const acceptSuggestion = () => { if (typeSuggestion) { setValue(typeSuggestion.formattedContent) setTypeSuggestion(null) setIsMultiline(true) setIsOpen(true) } } const dismissSuggestion = () => { setTypeSuggestion(null) setDismissedSuggestion(true) } const handleSubmit = async (e?: React.FormEvent) => { e?.preventDefault() if (!value.trim() || isLoading) return setIsLoading(true) try { const response = await fetch('/api/notes/quick', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: value }), }) if (!response.ok) { const error = await response.json() throw new Error(error.error || 'Error creating note') } const note = await response.json() toast.success('Nota creada', { description: note.title, }) setValue('') setIsOpen(false) setIsMultiline(false) router.refresh() } catch (error) { toast.error('Error', { description: error instanceof Error ? error.message : 'No se pudo crear la nota', }) } finally { setIsLoading(false) } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey && !isMultiline) { e.preventDefault() handleSubmit() } if (e.key === 'Escape') { setValue('') setIsOpen(false) setIsMultiline(false) inputRef.current?.blur() textareaRef.current?.blur() } } const toggleMultiline = () => { setIsMultiline(!isMultiline) if (!isMultiline) { setTimeout(() => textareaRef.current?.focus(), 0) } } const handleInputFocus = () => { setIsOpen(true) } // Close popup when clicking outside useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (popupRef.current && !popupRef.current.contains(e.target as Node)) { setIsOpen(false) } } if (isOpen) { document.addEventListener('mousedown', handleClickOutside) } return () => document.removeEventListener('mousedown', handleClickOutside) }, [isOpen]) // Focus on keyboard shortcut useEffect(() => { const handleGlobalKeyDown = (e: KeyboardEvent) => { if ((e.key === 'n' && (e.metaKey || e.ctrlKey)) || (e.key === 'n' && e.altKey)) { e.preventDefault() inputRef.current?.focus() inputRef.current?.select() setIsOpen(true) } if (e.key === 'Escape' && document.activeElement === inputRef.current) { inputRef.current?.blur() setIsOpen(false) } } window.addEventListener('keydown', handleGlobalKeyDown) return () => window.removeEventListener('keydown', handleGlobalKeyDown) }, []) return (
{/* Compact input row */}
{ setValue(e.target.value) detectContentType(e.target.value) if (e.target.value) setIsOpen(true) }} onKeyDown={handleKeyDown} onFocus={handleInputFocus} onPaste={handlePaste} className="w-full sm:w-80 h-9 pr-16" disabled={isLoading} /> {isLoading && ( )} {/* Action buttons inside input */}
{/* Expanded popup */} {isOpen && (
{/* Multiline textarea (shown when multiline mode) */} {isMultiline && (