|  | 
|  | 1 | +import React, { useState, useEffect, useRef } from "react"; | 
|  | 2 | + | 
|  | 3 | +export default function Chatbot() { | 
|  | 4 | +  const [isOpen, setIsOpen] = useState(false); | 
|  | 5 | +  const [messages, setMessages] = useState([]); | 
|  | 6 | +  const [inputValue, setInputValue] = useState(""); | 
|  | 7 | +  const [isLoading, setIsLoading] = useState(false); | 
|  | 8 | +  const [botStatus, setBotStatus] = useState(""); | 
|  | 9 | +  const [showGreetingMessage, setShowGreetingMessage] = useState(true); | 
|  | 10 | +  const messagesEndRef = useRef(null); | 
|  | 11 | + | 
|  | 12 | +  const CHAT_STORAGE_KEY = "chat_history"; | 
|  | 13 | + | 
|  | 14 | +  const toggleChat = () => setIsOpen(!isOpen); | 
|  | 15 | + | 
|  | 16 | +  // Load saved messages | 
|  | 17 | +  useEffect(() => { | 
|  | 18 | +    const saved = localStorage.getItem(CHAT_STORAGE_KEY); | 
|  | 19 | +    if (saved) setMessages(JSON.parse(saved)); | 
|  | 20 | +  }, []); | 
|  | 21 | + | 
|  | 22 | +  // Save chat history | 
|  | 23 | +  useEffect(() => { | 
|  | 24 | +    if (messages.length > 0) { | 
|  | 25 | +      localStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(messages)); | 
|  | 26 | +    } | 
|  | 27 | +  }, [messages]); | 
|  | 28 | + | 
|  | 29 | +  // Auto-scroll | 
|  | 30 | +  useEffect(() => { | 
|  | 31 | +    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); | 
|  | 32 | +  }, [messages]); | 
|  | 33 | + | 
|  | 34 | +  // Hide greeting after 7s | 
|  | 35 | +  useEffect(() => { | 
|  | 36 | +    if (!isOpen && showGreetingMessage) { | 
|  | 37 | +      const timer = setTimeout(() => setShowGreetingMessage(false), 7000); | 
|  | 38 | +      return () => clearTimeout(timer); | 
|  | 39 | +    } | 
|  | 40 | +  }, [isOpen, showGreetingMessage]); | 
|  | 41 | + | 
|  | 42 | +  const sendMessage = async () => { | 
|  | 43 | +    if (!inputValue.trim()) return; | 
|  | 44 | + | 
|  | 45 | +    const userMsg = { | 
|  | 46 | +      id: Date.now(), | 
|  | 47 | +      text: inputValue, | 
|  | 48 | +      sender: "user", | 
|  | 49 | +      timestamp: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }), | 
|  | 50 | +    }; | 
|  | 51 | + | 
|  | 52 | +    setMessages((prev) => [...prev, userMsg]); | 
|  | 53 | +    setInputValue(""); | 
|  | 54 | +    setIsLoading(true); | 
|  | 55 | + | 
|  | 56 | +    const statuses = ["Reviewing your query...", "Searching knowledge base...", "Formulating response..."]; | 
|  | 57 | +    let idx = 0; | 
|  | 58 | +    const interval = setInterval(() => { | 
|  | 59 | +      setBotStatus(statuses[idx]); | 
|  | 60 | +      idx = (idx + 1) % statuses.length; | 
|  | 61 | +    }, 2000); | 
|  | 62 | + | 
|  | 63 | +    try { | 
|  | 64 | +      const res = await fetch("https://docbot.keploy.io/chat", { | 
|  | 65 | +        method: "POST", | 
|  | 66 | +        headers: { "Content-Type": "application/json" }, | 
|  | 67 | +        body: JSON.stringify({ question: userMsg.text }), | 
|  | 68 | +      }); | 
|  | 69 | +      const data = await res.json(); | 
|  | 70 | + | 
|  | 71 | +      const botMsg = { | 
|  | 72 | +        id: Date.now() + 1, | 
|  | 73 | +        text: data.answer || "I couldn't find an answer. Try rephrasing.", | 
|  | 74 | +        sender: "bot", | 
|  | 75 | +        timestamp: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }), | 
|  | 76 | +      }; | 
|  | 77 | + | 
|  | 78 | +      setMessages((prev) => [...prev, botMsg]); | 
|  | 79 | +    } catch (err) { | 
|  | 80 | +      setMessages((prev) => [ | 
|  | 81 | +        ...prev, | 
|  | 82 | +        { | 
|  | 83 | +          id: Date.now() + 2, | 
|  | 84 | +          text: "⚠️ Error: please try again later.", | 
|  | 85 | +          sender: "bot", | 
|  | 86 | +          timestamp: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }), | 
|  | 87 | +        }, | 
|  | 88 | +      ]); | 
|  | 89 | +    } finally { | 
|  | 90 | +      clearInterval(interval); | 
|  | 91 | +      setBotStatus(""); | 
|  | 92 | +      setIsLoading(false); | 
|  | 93 | +    } | 
|  | 94 | +  }; | 
|  | 95 | + | 
|  | 96 | +  return ( | 
|  | 97 | +    <div style={{ position: "fixed", bottom: "20px", right: "20px", zIndex: 1000, fontFamily: "sans-serif" }}> | 
|  | 98 | +      {/* Floating Button */} | 
|  | 99 | +      {!isOpen && ( | 
|  | 100 | +        <div style={{ position: "relative" }}> | 
|  | 101 | +          {showGreetingMessage && ( | 
|  | 102 | +            <div | 
|  | 103 | +              style={{ | 
|  | 104 | +                position: "absolute", | 
|  | 105 | +                bottom: "75px", | 
|  | 106 | +                right: "0", | 
|  | 107 | +                background: "#fff", | 
|  | 108 | +                border: "1px solid #e5e7eb", | 
|  | 109 | +                borderRadius: "8px", | 
|  | 110 | +                padding: "10px 12px", | 
|  | 111 | +                width: "220px", | 
|  | 112 | +                fontSize: "14px", | 
|  | 113 | +                color: "#374151", | 
|  | 114 | +                boxShadow: "0 4px 10px rgba(0,0,0,0.15)", | 
|  | 115 | +              }} | 
|  | 116 | +            > | 
|  | 117 | +              <p style={{ fontWeight: "600", marginBottom: "4px" }}>Hey, I'm Keploy AI Assistant!</p> | 
|  | 118 | +              <p>May I help you?</p> | 
|  | 119 | +            </div> | 
|  | 120 | +          )} | 
|  | 121 | + | 
|  | 122 | +          <button | 
|  | 123 | +            onClick={toggleChat} | 
|  | 124 | +            style={{ | 
|  | 125 | +              width: "60px", | 
|  | 126 | +              height: "60px", | 
|  | 127 | +              borderRadius: "50%", | 
|  | 128 | +              background: "#FF6B35", | 
|  | 129 | +              border: "none", | 
|  | 130 | +              cursor: "pointer", | 
|  | 131 | +              boxShadow: "0 6px 12px rgba(0,0,0,0.25)", | 
|  | 132 | +              display: "flex", | 
|  | 133 | +              alignItems: "center", | 
|  | 134 | +              justifyContent: "center", | 
|  | 135 | +              transition: "transform 0.2s ease", | 
|  | 136 | +              color: "#fff", | 
|  | 137 | +              fontSize: "24px", | 
|  | 138 | +            }} | 
|  | 139 | +            onMouseOver={(e) => (e.currentTarget.style.transform = "scale(1.05)")} | 
|  | 140 | +            onMouseOut={(e) => (e.currentTarget.style.transform = "scale(1)")} | 
|  | 141 | +          > | 
|  | 142 | +            💬 | 
|  | 143 | +          </button> | 
|  | 144 | +        </div> | 
|  | 145 | +      )} | 
|  | 146 | + | 
|  | 147 | +      {/* Chat Window */} | 
|  | 148 | +      {isOpen && ( | 
|  | 149 | +        <div | 
|  | 150 | +          style={{ | 
|  | 151 | +            width: "360px", | 
|  | 152 | +            height: "520px", | 
|  | 153 | +            background: "#fff", | 
|  | 154 | +            border: "1px solid #e5e7eb", | 
|  | 155 | +            borderRadius: "12px", | 
|  | 156 | +            boxShadow: "0 8px 20px rgba(0,0,0,0.25)", | 
|  | 157 | +            display: "flex", | 
|  | 158 | +            flexDirection: "column", | 
|  | 159 | +            overflow: "hidden", | 
|  | 160 | +          }} | 
|  | 161 | +        > | 
|  | 162 | +          {/* Header */} | 
|  | 163 | +          <div | 
|  | 164 | +            style={{ | 
|  | 165 | +              background: "#FF6B35", | 
|  | 166 | +              color: "#fff", | 
|  | 167 | +              padding: "12px", | 
|  | 168 | +              fontWeight: "600", | 
|  | 169 | +              display: "flex", | 
|  | 170 | +              justifyContent: "space-between", | 
|  | 171 | +              alignItems: "center", | 
|  | 172 | +              fontSize: "15px", | 
|  | 173 | +            }} | 
|  | 174 | +          > | 
|  | 175 | +            Keploy AI Assistant | 
|  | 176 | +            <button | 
|  | 177 | +              onClick={toggleChat} | 
|  | 178 | +              style={{ | 
|  | 179 | +                background: "transparent", | 
|  | 180 | +                border: "none", | 
|  | 181 | +                color: "#fff", | 
|  | 182 | +                fontSize: "18px", | 
|  | 183 | +                cursor: "pointer", | 
|  | 184 | +              }} | 
|  | 185 | +            > | 
|  | 186 | +              ✖ | 
|  | 187 | +            </button> | 
|  | 188 | +          </div> | 
|  | 189 | + | 
|  | 190 | +          {/* Messages */} | 
|  | 191 | +          <div | 
|  | 192 | +            style={{ | 
|  | 193 | +              flex: 1, | 
|  | 194 | +              overflowY: "auto", | 
|  | 195 | +              padding: "12px", | 
|  | 196 | +              background: "#f9fafb", | 
|  | 197 | +              fontSize: "14px", | 
|  | 198 | +            }} | 
|  | 199 | +          > | 
|  | 200 | +            {messages.map((m) => ( | 
|  | 201 | +              <div | 
|  | 202 | +                key={m.id} | 
|  | 203 | +                style={{ | 
|  | 204 | +                  margin: "8px 0", | 
|  | 205 | +                  display: "flex", | 
|  | 206 | +                  justifyContent: m.sender === "user" ? "flex-end" : "flex-start", | 
|  | 207 | +                }} | 
|  | 208 | +              > | 
|  | 209 | +                <div | 
|  | 210 | +                  style={{ | 
|  | 211 | +                    padding: "8px 12px", | 
|  | 212 | +                    borderRadius: "12px", | 
|  | 213 | +                    background: m.sender === "user" ? "#ffedd5" : "#e5e7eb", | 
|  | 214 | +                    color: "#1f2937", | 
|  | 215 | +                    maxWidth: "75%", | 
|  | 216 | +                    wordWrap: "break-word", | 
|  | 217 | +                  }} | 
|  | 218 | +                > | 
|  | 219 | +                  {m.text} | 
|  | 220 | +                  <div style={{ fontSize: "11px", color: "#6b7280", marginTop: "4px", textAlign: "right" }}> | 
|  | 221 | +                    {m.timestamp} | 
|  | 222 | +                  </div> | 
|  | 223 | +                </div> | 
|  | 224 | +              </div> | 
|  | 225 | +            ))} | 
|  | 226 | + | 
|  | 227 | +            {isLoading && ( | 
|  | 228 | +              <div style={{ fontSize: "13px", color: "#6b7280", marginTop: "6px" }}>{botStatus}...</div> | 
|  | 229 | +            )} | 
|  | 230 | +            <div ref={messagesEndRef} /> | 
|  | 231 | +          </div> | 
|  | 232 | + | 
|  | 233 | +          {/* Input */} | 
|  | 234 | +          <div style={{ borderTop: "1px solid #e5e7eb", padding: "8px", display: "flex" }}> | 
|  | 235 | +            <input | 
|  | 236 | +              type="text" | 
|  | 237 | +              value={inputValue} | 
|  | 238 | +              onChange={(e) => setInputValue(e.target.value)} | 
|  | 239 | +              onKeyDown={(e) => e.key === "Enter" && sendMessage()} | 
|  | 240 | +              placeholder="Type your message..." | 
|  | 241 | +              style={{ | 
|  | 242 | +                flex: 1, | 
|  | 243 | +                border: "1px solid #d1d5db", | 
|  | 244 | +                borderRadius: "8px", | 
|  | 245 | +                padding: "8px 10px", | 
|  | 246 | +                fontSize: "14px", | 
|  | 247 | +                outline: "none", | 
|  | 248 | +              }} | 
|  | 249 | +            /> | 
|  | 250 | +            <button | 
|  | 251 | +              onClick={sendMessage} | 
|  | 252 | +              style={{ | 
|  | 253 | +                marginLeft: "8px", | 
|  | 254 | +                background: "#FF6B35", | 
|  | 255 | +                border: "none", | 
|  | 256 | +                color: "#fff", | 
|  | 257 | +                padding: "8px 14px", | 
|  | 258 | +                borderRadius: "8px", | 
|  | 259 | +                cursor: "pointer", | 
|  | 260 | +                fontWeight: "500", | 
|  | 261 | +              }} | 
|  | 262 | +            > | 
|  | 263 | +              Send | 
|  | 264 | +            </button> | 
|  | 265 | +          </div> | 
|  | 266 | +        </div> | 
|  | 267 | +      )} | 
|  | 268 | +    </div> | 
|  | 269 | +  ); | 
|  | 270 | +} | 
0 commit comments