AI Prativa – Smart Farming Assistant – Integrating Front End With Back End


Smart Farming Assistant – Integrating Front End With Back End

'use client';

import { useState, useRef, useEffect } from 'react';
import { Send, Mic, MicOff, Volume2, Loader2, Sprout, Sun, Cloud, Droplets, Wind, Zap, Leaf, MessageCircle, X, Sparkles } from 'lucide-react';

interface Message {
  id: string;
  text: string;
  sender: 'user' | 'ai';
  audioUrl?: string;
  timestamp: Date;
}

export default function SmartFarmingChat() {
  const [messages, setMessages] = useState<Message[]>([
    {
      id: '1',
      text: 'Namaste! ๐Ÿ™ I\'m your Smart Farming Assistant. I can help you with:\n\n๐ŸŒพ Crop recommendations & planting schedules\n๐ŸŒง๏ธ Seasonal farming advice (Kharif, Rabi, Zaid)\n๐ŸŒฑ Soil management & fertilizers\n๐Ÿ› Pest control & disease prevention\n๐Ÿ’ง Irrigation techniques\n\nHow can I assist you today?',
      sender: 'ai',
      timestamp: new Date(),
    }
  ]);
  const [input, setInput] = useState('');
  const [loading, setLoading] = useState(false);
  const [recording, setRecording] = useState(false);
  const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);
  const audioRef = useRef<HTMLAudioElement>(null);
  const messagesEndRef = useRef<HTMLDivElement>(null);
  const audioChunksRef = useRef<Blob[]>([]);

  // Backend URL from environment variable
  const BACKEND_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';

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

  // Send text message
  const sendMessage = async () => {
    if (!input.trim() || loading) return;

    const userMessage: Message = {
      id: Date.now().toString(),
      text: input,
      sender: 'user',
      timestamp: new Date(),
    };

    setMessages(prev => [...prev, userMessage]);
    const currentInput = input;
    setInput('');
    setLoading(true);

    try {
      // โœ… FIX 1: Use FormData instead of JSON
      const formData = new FormData();
      formData.append('text', currentInput);

      // โœ… FIX 2: Correct endpoint - /api/chat
      const response = await fetch(`${BACKEND_URL}/api/chat`, {
        method: 'POST',
        body: formData, // Don't set Content-Type header - browser handles it
      });

      if (!response.ok) {
        throw new Error(`API Error: ${response.status}`);
      }

      // โœ… FIX 3: Backend returns { text, voice } not { success, text, audio_url }
      const data = await response.json();

      const aiMessage: Message = {
        id: (Date.now() + 1).toString(),
        text: data.text,
        sender: 'ai',
        audioUrl: data.voice ? `${BACKEND_URL}${data.voice}` : undefined, // โœ… FIX 4: Prepend backend URL
        timestamp: new Date(),
      };
      setMessages(prev => [...prev, aiMessage]);

    } catch (error) {
      console.error('Error:', error);
      const errorMessage: Message = {
        id: (Date.now() + 1).toString(),
        text: 'โš ๏ธ Sorry, I encountered an error. Please make sure the backend API is running at ' + BACKEND_URL,
        sender: 'ai',
        timestamp: new Date(),
      };
      setMessages(prev => [...prev, errorMessage]);
    } finally {
      setLoading(false);
    }
  };

  // Start voice recording
  const startRecording = async () => {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const recorder = new MediaRecorder(stream);
      audioChunksRef.current = [];

      recorder.ondataavailable = (event) => {
        audioChunksRef.current.push(event.data);
      };

      recorder.onstop = async () => {
        const audioBlob = new Blob(audioChunksRef.current, { type: 'audio/webm' });
        await sendVoiceMessage(audioBlob);
        stream.getTracks().forEach(track => track.stop());
      };

      recorder.start();
      setMediaRecorder(recorder);
      setRecording(true);
    } catch (error) {
      console.error('Error accessing microphone:', error);
      alert('Could not access microphone. Please check permissions.');
    }
  };

  // Stop recording
  const stopRecording = () => {
    if (mediaRecorder && recording) {
      mediaRecorder.stop();
      setRecording(false);
      setMediaRecorder(null);
    }
  };

  // Send voice message
  const sendVoiceMessage = async (audioBlob: Blob) => {
    const userMessage: Message = {
      id: Date.now().toString(),
      text: '๐ŸŽค Processing voice...',
      sender: 'user',
      timestamp: new Date(),
    };
    setMessages(prev => [...prev, userMessage]);
    setLoading(true);

    try {
      // โœ… Send audio to get transcription
      const formData = new FormData();
      formData.append('audio', audioBlob, 'recording.webm');

      const response = await fetch(`${BACKEND_URL}/api/chat`, {
        method: 'POST',
        body: formData,
      });

      if (!response.ok) {
        throw new Error('Failed to process audio');
      }

      const data = await response.json();

      // โœ… Backend returns { text } for audio (transcription only)
      const transcription = data.text;

      // Update user message with transcription
      setMessages(prev => prev.map(msg => 
        msg.id === userMessage.id 
          ? { ...msg, text: `๐ŸŽค "${transcription}"` }
          : msg
      ));

      // Now send transcription as text to get AI response
      const textFormData = new FormData();
      textFormData.append('text', transcription);

      const aiResponse = await fetch(`${BACKEND_URL}/api/chat`, {
        method: 'POST',
        body: textFormData,
      });

      if (aiResponse.ok) {
        const aiData = await aiResponse.json();
        
        const aiMessage: Message = {
          id: (Date.now() + 1).toString(),
          text: aiData.text,
          sender: 'ai',
          audioUrl: aiData.voice ? `${BACKEND_URL}${aiData.voice}` : undefined,
          timestamp: new Date(),
        };
        setMessages(prev => [...prev, aiMessage]);
      }

    } catch (error) {
      console.error('Error:', error);
      const errorMessage: Message = {
        id: (Date.now() + 1).toString(),
        text: 'โš ๏ธ Failed to process voice message.',
        sender: 'ai',
        timestamp: new Date(),
      };
      setMessages(prev => [...prev, errorMessage]);
    } finally {
      setLoading(false);
    }
  };

  // Play audio response
  const playAudio = (url: string) => {
    if (audioRef.current) {
      audioRef.current.src = url;
      audioRef.current.play().catch(err => console.error('Audio play error:', err));
    }
  };

  // Handle Enter key
  const handleKeyPress = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      sendMessage();
    }
  };

  // Suggested questions
  const suggestedQuestions = [
    "๐ŸŒพ What crops for monsoon season?",
    "๐ŸŒฑ Best time to plant wheat?",
    "๐Ÿ’ง How to save water in farming?",
    "๐Ÿ› Natural pest control methods?",
  ];

  return (
    <div className="relative">
      {/* Agricultural Background Pattern */}
      <div className="absolute inset-0 opacity-5 pointer-events-none">
        <div className="absolute top-10 left-10 text-6xl">๐ŸŒพ</div>
        <div className="absolute top-32 right-20 text-5xl">๐ŸŒฑ</div>
        <div className="absolute bottom-20 left-1/4 text-6xl">๐Ÿšœ</div>
        <div className="absolute top-1/3 right-10 text-5xl">๐ŸŒป</div>
        <div className="absolute bottom-10 right-1/3 text-6xl">๐ŸŒฝ</div>
      </div>

      <div className="relative flex flex-col h-[calc(100vh-3rem)] glass rounded-3xl border-2 border-green-500/20 overflow-hidden shadow-2xl">
        {/* Header - Agricultural Theme */}
        <div className="relative p-6 bg-gradient-to-r from-green-600/20 via-emerald-600/20 to-green-600/20 border-b-2 border-green-500/30">
          {/* Animated background elements */}
          <div className="absolute inset-0 overflow-hidden opacity-10">
            <div className="absolute -top-10 -left-10 w-40 h-40 bg-green-400 rounded-full blur-3xl animate-pulse" />
            <div className="absolute -bottom-10 -right-10 w-40 h-40 bg-emerald-400 rounded-full blur-3xl animate-pulse" style={{ animationDelay: '1s' }} />
          </div>

          <div className="relative z-10 flex items-center justify-between">
            <div className="flex items-center gap-4">
              {/* Animated Logo */}
              <div className="relative">
                <div className="absolute inset-0 bg-gradient-to-br from-green-400 to-emerald-600 rounded-2xl blur-xl opacity-50 animate-pulse" />
                <div className="relative w-16 h-16 bg-gradient-to-br from-green-500 to-emerald-600 rounded-2xl flex items-center justify-center shadow-lg">
                  <Sprout className="w-8 h-8 text-white animate-bounce" style={{ animationDuration: '3s' }} />
                </div>
              </div>
              
              {/* Title */}
              <div>
                <h3 className="text-2xl font-black text-white flex items-center gap-2" style={{ fontFamily: 'Outfit' }}>
                  <span className="bg-gradient-to-r from-green-200 to-emerald-200 bg-clip-text text-transparent">
                    Smart Farming Assistant
                  </span>
                  <span className="text-2xl">๐ŸŒพ</span>
                </h3>
                <div className="flex items-center gap-2 mt-1">
                  <div className="w-2 h-2 bg-green-400 rounded-full animate-ping" />
                  <div className="w-2 h-2 bg-green-400 rounded-full absolute" />
                  <p className="text-sm text-green-300 font-medium ml-2">Online • AI-Powered</p>
                </div>
              </div>
            </div>
            
            {/* Weather Icons (Decorative) */}
            <div className="hidden md:flex items-center gap-3 text-green-300">
              <Sun className="w-6 h-6 animate-pulse" style={{ animationDuration: '3s' }} />
              <Cloud className="w-6 h-6 opacity-70" />
              <Droplets className="w-6 h-6 animate-bounce" style={{ animationDuration: '2s' }} />
              <Wind className="w-5 h-5 opacity-70" />
            </div>
          </div>
        </div>

        {/* Stats Bar */}
        <div className="px-6 py-3 bg-gradient-to-r from-green-900/30 to-emerald-900/30 border-b border-green-500/20">
          <div className="flex items-center justify-between text-sm">
            <div className="flex items-center gap-6">
              <div className="flex items-center gap-2">
                <MessageCircle className="w-4 h-4 text-green-400" />
                <span className="text-gray-300">Messages: <span className="font-bold text-white">{messages.length}</span></span>
              </div>
              <div className="flex items-center gap-2">
                <Zap className="w-4 h-4 text-yellow-400" />
                <span className="text-gray-300">AI Model: <span className="font-bold text-white">GPT-5-Nano</span></span>
              </div>
            </div>
            <div className="flex items-center gap-2">
              <Leaf className="w-4 h-4 text-green-400 animate-pulse" />
              <span className="text-green-300 font-medium">Helping Indian Farmers</span>
            </div>
          </div>
        </div>

        {/* Messages Area */}
        <div className="flex-1 overflow-y-auto p-6 space-y-4 bg-gradient-to-b from-transparent to-green-900/5">
          {/* Welcome Suggestions */}
          {messages.length === 1 && (
            <div className="mb-6 space-y-4">
              <div className="text-center">
                <div className="inline-flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-green-500/20 to-emerald-500/20 rounded-full border border-green-400/30 mb-4">
                  <Sparkles className="w-4 h-4 text-green-400" />
                  <span className="text-sm font-semibold text-green-300">Quick Start Questions</span>
                </div>
              </div>
              
              <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
                {suggestedQuestions.map((question, idx) => (
                  <button
                    key={idx}
                    onClick={() => setInput(question)}
                    className="group p-4 text-left glass rounded-2xl border-2 border-green-500/20 hover:border-green-400/50 transition-all text-sm text-gray-300 hover:text-white hover:scale-105 transform duration-300"
                  >
                    <div className="flex items-center gap-2">
                      <div className="w-8 h-8 bg-gradient-to-br from-green-500/30 to-emerald-500/30 rounded-lg flex items-center justify-center group-hover:scale-110 transition-transform">
                        <MessageCircle className="w-4 h-4 text-green-400" />
                      </div>
                      <span className="font-medium">{question}</span>
                    </div>
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* Messages */}
          {messages.map((message, index) => (
            <div
              key={message.id}
              className={`flex ${message.sender === 'user' ? 'justify-end' : 'justify-start'} animate-fadeIn`}
              style={{ animationDelay: `${index * 50}ms` }}
            >
              <div
                className={`
                  max-w-[85%] md:max-w-[75%] rounded-3xl p-5 shadow-lg
                  ${message.sender === 'user'
                    ? 'bg-gradient-to-br from-green-600 to-emerald-600 text-white ml-auto'
                    : 'glass border-2 border-green-500/30 text-white'
                  }
                `}
              >
                {/* AI Header */}
                {message.sender === 'ai' && (
                  <div className="flex items-center gap-2 mb-3 pb-2 border-b border-green-400/30">
                    <div className="w-8 h-8 bg-gradient-to-br from-green-500 to-emerald-600 rounded-full flex items-center justify-center">
                      <Sprout className="w-4 h-4 text-white" />
                    </div>
                    <span className="text-xs font-bold text-green-400 uppercase tracking-wide">AI Farm Expert</span>
                  </div>
                )}
                
                {/* Message Text */}
                <p className="whitespace-pre-wrap leading-relaxed text-[15px]">{message.text}</p>
                
                {/* Audio Player */}
                {message.audioUrl && (
                  <button
                    onClick={() => playAudio(message.audioUrl!)}
                    className="mt-4 flex items-center gap-3 px-4 py-2.5 bg-green-500/20 hover:bg-green-500/30 rounded-xl transition-all group border border-green-400/30"
                  >
                    <Volume2 className="w-5 h-5 text-green-400 group-hover:scale-125 transition-transform" />
                    <span className="text-sm font-semibold text-green-300">Play Audio Response</span>
                    <div className="flex gap-1 ml-auto">
                      <div className="w-1 h-3 bg-green-400 rounded-full animate-pulse" />
                      <div className="w-1 h-4 bg-green-400 rounded-full animate-pulse" style={{ animationDelay: '0.1s' }} />
                      <div className="w-1 h-5 bg-green-400 rounded-full animate-pulse" style={{ animationDelay: '0.2s' }} />
                      <div className="w-1 h-4 bg-green-400 rounded-full animate-pulse" style={{ animationDelay: '0.3s' }} />
                    </div>
                  </button>
                )}
                
                {/* Timestamp */}
                <p className={`text-xs mt-3 ${message.sender === 'user' ? 'text-green-100' : 'text-gray-400'}`}>
                  {message.timestamp.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })}
                </p>
              </div>
            </div>
          ))}

          {/* Loading Animation */}
          {loading && (
            <div className="flex justify-start animate-fadeIn">
              <div className="glass border-2 border-green-500/30 rounded-3xl p-5 flex items-center gap-3">
                <Loader2 className="w-6 h-6 text-green-400 animate-spin" />
                <div className="space-y-2">
                  <div className="flex gap-1">
                    <div className="w-2 h-2 bg-green-400 rounded-full animate-bounce" />
                    <div className="w-2 h-2 bg-green-400 rounded-full animate-bounce" style={{ animationDelay: '0.1s' }} />
                    <div className="w-2 h-2 bg-green-400 rounded-full animate-bounce" style={{ animationDelay: '0.2s' }} />
                  </div>
                  <span className="text-gray-300 text-sm font-medium">AI is thinking...</span>
                </div>
              </div>
            </div>
          )}
          
          <div ref={messagesEndRef} />
        </div>

        {/* Input Area - Agricultural Theme */}
        <div className="p-4 bg-gradient-to-r from-green-900/40 via-emerald-900/40 to-green-900/40 border-t-2 border-green-500/30">
          {/* Recording Indicator */}
          {recording && (
            <div className="mb-3 px-4 py-2 bg-red-500/20 border border-red-400/50 rounded-xl flex items-center justify-between animate-pulse">
              <div className="flex items-center gap-2">
                <div className="w-3 h-3 bg-red-500 rounded-full animate-ping" />
                <div className="w-3 h-3 bg-red-500 rounded-full absolute" />
                <span className="text-red-300 font-semibold text-sm ml-3">Recording voice message...</span>
              </div>
              <button
                onClick={stopRecording}
                className="px-3 py-1 bg-red-500 hover:bg-red-600 rounded-lg text-white text-sm font-bold transition-colors"
              >
                Stop
              </button>
            </div>
          )}

          <div className="flex gap-3">
            {/* Voice Button */}
            <button
              onClick={recording ? stopRecording : startRecording}
              disabled={loading}
              className={`p-4 rounded-2xl transition-all duration-300 ${
                recording 
                  ? 'bg-red-500 hover:bg-red-600 animate-pulse' 
                  : 'glass border-2 border-green-500/30 hover:border-green-400/50 hover:scale-110'
              } disabled:opacity-50 disabled:cursor-not-allowed`}
              title={recording ? 'Stop Recording' : 'Voice Message'}
            >
              {recording ? (
                <MicOff className="w-6 h-6 text-white" />
              ) : (
                <Mic className="w-6 h-6 text-green-400" />
              )}
            </button>

            {/* Text Input */}
            <div className="flex-1 relative">
              <input
                type="text"
                value={input}
                onChange={(e) => setInput(e.target.value)}
                onKeyPress={handleKeyPress}
                placeholder="Ask about crops, seasons, pests, irrigation..."
                className="w-full px-6 py-4 glass rounded-2xl border-2 border-green-500/30 text-white placeholder-gray-400 outline-none focus:border-green-400/60 transition-all text-[15px] pr-24"
                disabled={loading || recording}
              />
              {/* Character count */}
              <div className="absolute right-4 top-1/2 -translate-y-1/2 text-xs text-gray-500">
                {input.length}/500
              </div>
            </div>
            
            {/* Send Button */}
            <button
              onClick={sendMessage}
              disabled={loading || !input.trim() || recording}
              className="px-8 py-4 bg-gradient-to-br from-green-500 to-emerald-600 hover:from-green-600 hover:to-emerald-700 rounded-2xl text-white font-bold hover:scale-110 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 flex items-center gap-2 shadow-lg"
            >
              <Send className="w-6 h-6" />
              <span className="hidden md:inline">Send</span>
            </button>
          </div>
          
          {/* Footer Info */}
          <div className="mt-3 flex items-center justify-between text-xs text-gray-500">
            <div className="flex items-center gap-4">
              <span>๐ŸŒพ Press Enter to send</span>
              <span>•</span>
              <span>๐ŸŽค Click mic for voice</span>
            </div>
            <span className="text-green-400 font-medium">Powered by OpenAI</span>
          </div>
        </div>
      </div>

      {/* Hidden Audio Element */}
      <audio ref={audioRef} className="hidden" />
    </div>
  );
}

Leave a Reply

Your email address will not be published. Required fields are marked *