'use client' import { useState, useEffect, useRef } from 'react' import { useLanguage } from '@/lib/i18n/LanguageProvider' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Lightbulb, ThumbsUp, ThumbsDown, X, Sparkles, ArrowRight } from 'lucide-react' import { toast } from 'sonner' interface MemoryEchoInsight { id: string note1Id: string note2Id: string note1: { id: string title: string | null content: string } note2: { id: string title: string | null content: string } similarityScore: number insight: string insightDate: Date viewed: boolean feedback: string | null } interface MemoryEchoNotificationProps { onOpenNote?: (noteId: string) => void } export function MemoryEchoNotification({ onOpenNote }: MemoryEchoNotificationProps) { const { t } = useLanguage() const [insight, setInsight] = useState(null) const [isLoading, setIsLoading] = useState(false) const [isDismissed, setIsDismissed] = useState(false) const [showModal, setShowModal] = useState(false) const [demoMode, setDemoMode] = useState(false) const pollingRef = useRef | null>(null) // Fetch insight on mount useEffect(() => { fetchInsight() return () => { if (pollingRef.current) clearInterval(pollingRef.current) } }, []) const fetchInsight = async () => { setIsLoading(true) try { const res = await fetch('/api/ai/echo') const data = await res.json() if (data.insight) { setInsight(data.insight) // Check if user is in demo mode by looking at frequency settings setDemoMode(true) // If we got an insight after dismiss, assume demo mode } } catch (error) { console.error('[MemoryEcho] Failed to fetch insight:', error) } finally { setIsLoading(false) } } // Start polling in demo mode after first dismiss useEffect(() => { if (isDismissed && !pollingRef.current) { pollingRef.current = setInterval(async () => { try { const res = await fetch('/api/ai/echo') const data = await res.json() if (data.insight) { setInsight(data.insight) setIsDismissed(false) } } catch { // silent } }, 15000) // Poll every 15s } return () => { if (pollingRef.current) { clearInterval(pollingRef.current) pollingRef.current = null } } }, [isDismissed]) const handleView = async () => { if (!insight) return try { // Mark as viewed await fetch('/api/ai/echo', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'view', insightId: insight.id }) }) // Show success message and open modal toast.success(t('toast.openingConnection')) setShowModal(true) } catch (error) { console.error('[MemoryEcho] Failed to view connection:', error) toast.error(t('toast.openConnectionFailed')) } } const handleFeedback = async (feedback: 'thumbs_up' | 'thumbs_down') => { if (!insight) return try { await fetch('/api/ai/echo', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'feedback', insightId: insight.id, feedback }) }) // Show feedback toast if (feedback === 'thumbs_up') { toast.success(t('toast.thanksFeedback')) } else { toast.success(t('toast.thanksFeedbackImproving')) } // Dismiss notification setIsDismissed(true) // Stop polling after explicit feedback if (pollingRef.current) { clearInterval(pollingRef.current) pollingRef.current = null } } catch (error) { console.error('[MemoryEcho] Failed to submit feedback:', error) toast.error(t('toast.feedbackFailed')) } } const handleDismiss = () => { setIsDismissed(true) } // Don't render notification if dismissed, loading, or no insight if (isDismissed || isLoading || !insight) { return null } // Calculate values for both notification and modal const note1Title = insight.note1.title || t('notification.untitled') const note2Title = insight.note2.title || t('notification.untitled') const similarityPercentage = Math.round(insight.similarityScore * 100) // Render modal if requested if (showModal && insight) { return (
{/* Header */}

{t('memoryEcho.title')}

{t('connection.similarityInfo', { similarity: similarityPercentage })}

{/* AI-generated insight */}

{insight.insight}

{/* Notes Grid */}
{/* Note 1 */}
{ if (onOpenNote) { onOpenNote(insight.note1.id) setShowModal(false) } }} className="cursor-pointer border dark:border-zinc-700 rounded-lg p-4 hover:border-amber-300 dark:hover:border-amber-700 transition-colors" >

{note1Title}

{insight.note1.content}

{t('memoryEcho.clickToView')}

{/* Note 2 */}
{ if (onOpenNote) { onOpenNote(insight.note2.id) setShowModal(false) } }} className="cursor-pointer border dark:border-zinc-700 rounded-lg p-4 hover:border-purple-300 dark:hover:border-purple-700 transition-colors" >

{note2Title}

{insight.note2.content}

{t('memoryEcho.clickToView')}

{/* Feedback Section */}

{t('connection.isHelpful')}

) } return (
{t('memoryEcho.title')} {t('memoryEcho.description')}
{/* AI-generated insight */}

{insight.insight}

{/* Connected notes */}
{note1Title} {note2Title} {t('memoryEcho.match', { percentage: similarityPercentage })}
{/* Action buttons */}
{/* Dismiss link */}
) }