perplexity-hackathon-LawMitra / perplexity_hackathon / legal-chat-pwa / src / components / Chat.tsx
Chat.tsx
Raw
'use client';

import React, { useState, useEffect, useRef } from 'react';
import { Message, Language, SUPPORTED_LANGUAGES } from '@/types';
import { ChatMessage } from './ChatMessage';
import { ChatInput } from './ChatInput';
import { LanguageSelector } from './LanguageSelector';
import { FaMicrophone, FaMicrophoneSlash, FaGlobe, FaCog } from 'react-icons/fa';
import { Card } from './shared/Card';

export function Chat() {
  const [messages, setMessages] = useState<Message[]>([]);
  const [language, setLanguage] = useState<Language>(SUPPORTED_LANGUAGES[0]);
  const [voiceEnabled, setVoiceEnabled] = useState(true);
  const [showSettings, setShowSettings] = useState(false);
  const messagesEndRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    // Load messages from localStorage
    const savedMessages = localStorage.getItem('chatMessages');
    if (savedMessages) {
      setMessages(JSON.parse(savedMessages));
    }

    // Load settings from localStorage
    const savedLanguage = localStorage.getItem('chatLanguage');
    if (savedLanguage) {
      const lang = SUPPORTED_LANGUAGES.find(
        (l) => l.code === JSON.parse(savedLanguage).code
      );
      if (lang) setLanguage(lang);
    }

    const savedVoiceEnabled = localStorage.getItem('voiceEnabled');
    if (savedVoiceEnabled !== null) {
      setVoiceEnabled(JSON.parse(savedVoiceEnabled));
    }
  }, []);

  useEffect(() => {
    // Save messages to localStorage
    localStorage.setItem('chatMessages', JSON.stringify(messages));
  }, [messages]);

  useEffect(() => {
    // Save settings to localStorage
    localStorage.setItem('chatLanguage', JSON.stringify(language));
    localStorage.setItem('voiceEnabled', JSON.stringify(voiceEnabled));
  }, [language, voiceEnabled]);

  useEffect(() => {
    // Scroll to bottom when messages change
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages]);

  const handleSendMessage = async (text: string, audioBlob?: Blob) => {
    // Create user message
    const userMessage: Message = {
      id: Date.now().toString(),
      text: text || 'Voice message',
      type: 'user',
      timestamp: Date.now(),
      language: language.code,
    };

    if (audioBlob) {
      // Convert blob to base64 for storage
      const reader = new FileReader();
      reader.onloadend = () => {
        userMessage.audioUrl = reader.result as string;
        setMessages((prev) => [...prev, userMessage]);
      };
      reader.readAsDataURL(audioBlob);
    } else {
      setMessages((prev) => [...prev, userMessage]);
    }

    try {
      // Send to backend API
      const response = await fetch('/api/legal/ask-legal-question', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          question: text,
          audioBlob: audioBlob ? await audioBlob.arrayBuffer() : undefined,
          language: language.code,
        }),
      });

      const data = await response.json();

      // Create bot message
      const botMessage: Message = {
        id: Date.now().toString(),
        text: data.text,
        type: 'bot',
        timestamp: Date.now(),
        language: language.code,
      };

      if (data.audioUrl) {
        botMessage.audioUrl = data.audioUrl;
      }

      setMessages((prev) => [...prev, botMessage]);
    } catch (error) {
      console.error('Error sending message:', error);
      // Add error message
      setMessages((prev) => [
        ...prev,
        {
          id: Date.now().toString(),
          text: 'Sorry, there was an error processing your request.',
          type: 'bot',
          timestamp: Date.now(),
          language: language.code,
        },
      ]);
    }
  };

  return (
    <div className="flex h-screen flex-col bg-gradient-to-br from-blue-50 to-indigo-50">
      <div className="border-b bg-white px-6 py-4 shadow-sm">
        <div className="mx-auto flex max-w-7xl items-center justify-between">
          <h1 className="text-2xl font-bold text-gray-900">Legal Chat Assistant</h1>
          <div className="flex items-center gap-4">
            <button
              onClick={() => setShowSettings(!showSettings)}
              className="rounded-full p-2 text-gray-500 hover:bg-gray-100 hover:text-gray-700"
              title="Settings"
            >
              <FaCog />
            </button>
            <button
              onClick={() => setVoiceEnabled(!voiceEnabled)}
              className={`rounded-full p-2 transition-colors ${
                voiceEnabled
                  ? 'bg-blue-100 text-blue-500 hover:bg-blue-200'
                  : 'bg-gray-100 text-gray-500 hover:bg-gray-200'
              }`}
              title={voiceEnabled ? 'Disable voice' : 'Enable voice'}
            >
              {voiceEnabled ? <FaMicrophone /> : <FaMicrophoneSlash />}
            </button>
            <div className="relative">
              <LanguageSelector
                currentLanguage={language}
                onLanguageChange={setLanguage}
              />
            </div>
          </div>
        </div>
      </div>

      {showSettings && (
        <Card className="absolute right-6 top-20 z-10 w-80">
          <h3 className="mb-4 text-lg font-semibold">Settings</h3>
          <div className="space-y-4">
            <div className="flex items-center justify-between">
              <span className="text-gray-700">Voice Input/Output</span>
              <button
                onClick={() => setVoiceEnabled(!voiceEnabled)}
                className={`relative h-6 w-11 rounded-full transition-colors ${
                  voiceEnabled ? 'bg-blue-500' : 'bg-gray-300'
                }`}
              >
                <span
                  className={`absolute left-1 top-1 h-4 w-4 rounded-full bg-white transition-transform ${
                    voiceEnabled ? 'translate-x-5' : ''
                  }`}
                />
              </button>
            </div>
            <div>
              <label className="mb-2 block text-sm text-gray-700">Language</label>
              <LanguageSelector
                currentLanguage={language}
                onLanguageChange={setLanguage}
              />
            </div>
          </div>
        </Card>
      )}

      <div className="flex-1 overflow-y-auto px-6 py-4">
        <div className="mx-auto max-w-3xl space-y-6">
          {messages.length === 0 ? (
            <div className="flex flex-col items-center justify-center space-y-4 py-12 text-center text-gray-500">
              <FaGlobe className="h-12 w-12" />
              <div>
                <p className="text-lg font-medium">Welcome to Legal Chat Assistant</p>
                <p className="mt-1">Ask any legal question in your preferred language</p>
              </div>
            </div>
          ) : (
            messages.map((message) => (
              <ChatMessage key={message.id} message={message} />
            ))
          )}
          <div ref={messagesEndRef} />
        </div>
      </div>

      <div className="border-t bg-white px-6 py-4">
        <div className="mx-auto max-w-3xl">
          <ChatInput
            onSendMessage={handleSendMessage}
            voiceEnabled={voiceEnabled}
            language={language.name}
          />
        </div>
      </div>
    </div>
  );
}