← back to Dear Bubbe Nextjs

app/messages/page.tsx

240 lines

'use client'

import { useState, useEffect } from 'react'
import { useSession } from 'next-auth/react'
import { useRouter } from 'next/navigation'
import Link from 'next/link'

interface ConversationMessage {
  timestamp: string
  message: string
  response: string
  context?: string
}

interface UserMemory {
  userId: string
  lastVisit: string
  totalMessages: number
  topics: string[]
  conversations: ConversationMessage[]
}

export default function MessagesPage() {
  const { data: session, status } = useSession()
  const router = useRouter()
  const [memory, setMemory] = useState<UserMemory | null>(null)
  const [isLoading, setIsLoading] = useState(true)
  const [selectedTopic, setSelectedTopic] = useState<string>('all')
  const [searchQuery, setSearchQuery] = useState('')

  // Redirect if not logged in
  useEffect(() => {
    if (status === 'unauthenticated') {
      router.push('/')
    }
  }, [status, router])

  // Load conversation history
  useEffect(() => {
    const loadMessages = async () => {
      if (!session?.user?.email) return
      
      try {
        const response = await fetch(`/api/messages?userId=${encodeURIComponent(session.user.email)}`)
        if (response.ok) {
          const data = await response.json()
          setMemory(data)
        }
      } catch (error) {
        console.error('Failed to load messages:', error)
      } finally {
        setIsLoading(false)
      }
    }
    
    if (session) {
      loadMessages()
    }
  }, [session])

  // Filter conversations
  const filteredConversations = memory?.conversations.filter(conv => {
    const matchesSearch = searchQuery === '' || 
      conv.message.toLowerCase().includes(searchQuery.toLowerCase()) ||
      conv.response.toLowerCase().includes(searchQuery.toLowerCase())
    
    const matchesTopic = selectedTopic === 'all' || 
      (conv.context && conv.context.includes(selectedTopic))
    
    return matchesSearch && matchesTopic
  }) || []

  if (status === 'loading' || isLoading) {
    return (
      <div className="min-h-screen bg-gradient-to-br from-amber-50 to-orange-100 flex items-center justify-center">
        <div className="text-2xl text-amber-600">Loading your conversations...</div>
      </div>
    )
  }

  return (
    <div className="min-h-screen bg-gradient-to-br from-amber-50 to-orange-100 py-8">
      <div className="max-w-5xl mx-auto px-4">
        {/* Header */}
        <div className="mb-8">
          <Link href="/" className="text-amber-600 hover:text-amber-700 mb-4 inline-block">
            ← Back to Bubbe
          </Link>
          <h1 className="text-3xl font-bold text-gray-800">My Conversations with Bubbe</h1>
          <div className="mt-2 text-gray-600">
            {memory && (
              <>
                <p>Total messages: {memory.totalMessages}</p>
                <p>Last visit: {new Date(memory.lastVisit).toLocaleDateString()}</p>
              </>
            )}
          </div>
        </div>

        {/* Filters */}
        <div className="bg-white rounded-xl shadow-lg p-6 mb-6">
          <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
            {/* Search */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">
                Search Conversations
              </label>
              <input
                type="text"
                value={searchQuery}
                onChange={(e) => setSearchQuery(e.target.value)}
                placeholder="Search your messages..."
                className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
              />
            </div>

            {/* Topic Filter */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">
                Filter by Topic
              </label>
              <select
                value={selectedTopic}
                onChange={(e) => setSelectedTopic(e.target.value)}
                className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500"
              >
                <option value="all">All Topics</option>
                {memory?.topics.map(topic => (
                  <option key={topic} value={topic}>
                    {topic.charAt(0).toUpperCase() + topic.slice(1).replace('_', ' ')}
                  </option>
                ))}
              </select>
            </div>
          </div>
        </div>

        {/* Conversations List */}
        <div className="space-y-4">
          {filteredConversations.length === 0 ? (
            <div className="bg-white rounded-xl shadow-lg p-8 text-center">
              <p className="text-gray-600">
                {searchQuery || selectedTopic !== 'all' 
                  ? 'No conversations match your filters.'
                  : 'No conversations yet. Go talk to Bubbe!'}
              </p>
            </div>
          ) : (
            filteredConversations.map((conv, index) => (
              <div key={index} className="bg-white rounded-xl shadow-lg p-6">
                <div className="text-xs text-gray-500 mb-2">
                  {new Date(conv.timestamp).toLocaleString()}
                </div>
                
                {/* User Message */}
                <div className="mb-4">
                  <div className="text-sm font-semibold text-gray-700 mb-1">You:</div>
                  <div className="bg-blue-50 rounded-lg p-3 text-gray-800">
                    {conv.message}
                  </div>
                </div>
                
                {/* Bubbe's Response */}
                <div>
                  <div className="text-sm font-semibold text-amber-700 mb-1">Bubbe:</div>
                  <div className="bg-amber-50 rounded-lg p-3 text-gray-800">
                    {conv.response}
                  </div>
                </div>
                
                {/* Context/Topics */}
                {conv.context && (
                  <div className="mt-3 text-xs text-gray-500">
                    Topics: {conv.context}
                  </div>
                )}
              </div>
            ))
          )}
        </div>

        {/* Load More / Pagination could go here */}
        {filteredConversations.length > 20 && (
          <div className="mt-6 text-center">
            <button className="px-6 py-2 bg-amber-500 text-white rounded-lg hover:bg-amber-600 transition-colors">
              Load More Conversations
            </button>
          </div>
        )}

        {/* Export Options */}
        <div className="mt-8 bg-white rounded-xl shadow-lg p-6">
          <h2 className="text-xl font-semibold text-gray-800 mb-4">Export Your Data</h2>
          <div className="flex gap-4">
            <button 
              onClick={() => {
                // Export as JSON
                const dataStr = JSON.stringify(memory, null, 2)
                const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr)
                const exportFileDefaultName = `bubbe_conversations_${Date.now()}.json`
                
                const linkElement = document.createElement('a')
                linkElement.setAttribute('href', dataUri)
                linkElement.setAttribute('download', exportFileDefaultName)
                linkElement.click()
              }}
              className="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors"
            >
              Export as JSON
            </button>
            
            <button 
              onClick={() => {
                // Export as text
                let textContent = 'My Conversations with Bubbe\n\n'
                filteredConversations.forEach(conv => {
                  textContent += `Date: ${new Date(conv.timestamp).toLocaleString()}\n`
                  textContent += `You: ${conv.message}\n`
                  textContent += `Bubbe: ${conv.response}\n\n`
                  textContent += '---\n\n'
                })
                
                const dataUri = 'data:text/plain;charset=utf-8,'+ encodeURIComponent(textContent)
                const exportFileDefaultName = `bubbe_conversations_${Date.now()}.txt`
                
                const linkElement = document.createElement('a')
                linkElement.setAttribute('href', dataUri)
                linkElement.setAttribute('download', exportFileDefaultName)
                linkElement.click()
              }}
              className="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors"
            >
              Export as Text
            </button>
          </div>
        </div>
      </div>
    </div>
  )
}