'use client' import { useState, useEffect, useRef } from 'react' import { motion, AnimatePresence } from 'motion/react' import { Search, Sparkles, ArrowRight } from 'lucide-react' import { Button } from '@/components/ui/button' import { useLanguage } from '@/lib/i18n' import { semanticSearch } from '@/app/actions/semantic-search' import confetti from 'canvas-confetti' interface SearchResult { id: string; title: string | null; snippet?: string } interface Props { onDone: () => void locale: string autoSearch?: boolean } const SEARCH_PREFILL: Record = { fr: 'notes sur ma productivité', en: 'notes about productivity', fa: 'یادداشت‌های بهره‌وری', ar: 'ملاحظات حول الإنتاجية', de: 'Notizen zur Produktivität', es: 'notas sobre productividad', it: 'note sulla produttività', pt: 'notas sobre produtividade', ru: 'заметки о продуктивности', zh: '关于生产力的笔记', ja: '生産性に関するメモ', ko: '생산성에 관한 노트', nl: 'notities over productiviteit', pl: 'notatki o produktywności', hi: 'उत्पादकता पर नोट्स', } function fireConfetti() { const end = Date.now() + 800 const colors = ['#7c3aed', '#a78bfa', '#fbbf24', '#34d399'] const frame = () => { confetti({ particleCount: 3, angle: 60, spread: 55, origin: { x: 0 }, colors }) confetti({ particleCount: 3, angle: 120, spread: 55, origin: { x: 1 }, colors }) if (Date.now() < end) requestAnimationFrame(frame) } frame() } export function OnboardingStepAha({ onDone, locale, autoSearch = false }: Props) { const { t } = useLanguage() const lang = locale.split('-')[0].toLowerCase() const prefill = SEARCH_PREFILL[lang] ?? SEARCH_PREFILL.en const [query, setQuery] = useState(prefill) const [results, setResults] = useState(null) const [loading, setLoading] = useState(false) const [searched, setSearched] = useState(false) const [quotaExceeded, setQuotaExceeded] = useState(false) const autoSearched = useRef(false) async function handleSearch() { const q = query.trim() if (!q) return setLoading(true) setQuotaExceeded(false) try { const res = await semanticSearch(q, { limit: 5 }) const hits = res.results.map(r => ({ id: r.noteId, title: r.title, snippet: r.content?.slice(0, 80) })) setResults(hits) setSearched(true) if (hits.length > 0) { setTimeout(fireConfetti, 200) } } catch (err: unknown) { const msg = err instanceof Error ? err.message.toLowerCase() : '' if (msg.includes('quota') || msg.includes('limit') || msg.includes('exceeded') || msg.includes('upgrade')) { setQuotaExceeded(true) } setResults([]) setSearched(true) } finally { setLoading(false) } } useEffect(() => { if (!autoSearch || autoSearched.current) return autoSearched.current = true const timer = setTimeout(() => { void handleSearch() }, 800) return () => clearTimeout(timer) // eslint-disable-next-line react-hooks/exhaustive-deps }, [autoSearch]) return (

{t('onboarding.step_aha_title')}

{t('onboarding.step_aha_subtitle')}

setQuery(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleSearch()} className="flex-1 px-4 py-3 text-sm bg-transparent outline-none placeholder:text-muted-foreground" dir={['fa', 'ar'].includes(lang) ? 'rtl' : 'ltr'} aria-label={t('onboarding.step_aha_search_aria')} /> {searched && results !== null && ( {quotaExceeded ? (

{t('onboarding.quota_exceeded')}

) : results.length === 0 ? (

{t('onboarding.no_results')}

) : ( <> {results.slice(0, 3).map((r, i) => (

{r.title ?? 'Untitled'}

{r.snippet && (

{r.snippet}

)}
))} {t('onboarding.search_credit_used')} )}
)}
) }