import { useState, useEffect, useRef, useCallback } from 'react'; import { Send, User, Bot, Loader, RefreshCw, Github, Linkedin, Phone, Mail, AlertCircle } from 'lucide-react'; import './App.css'; import config from './config'; // Helper function to check backend connectivity const checkBackendHealth = async () => { try { const controller = new AbortController(); const timeoutId = setTimeout(() => { controller.abort(); }, 5000); // 5 seconds timeout const response = await fetch(config.apiUrl('/health'), { method: 'GET', signal: controller.signal }); clearTimeout(timeoutId); return response.ok; } catch (error) { return false; } }; // Helper function to render AI responses with basic markdown support const renderMessage = (content) => { if (typeof content !== 'string') return content; // Simple markdown-like formatting let formatted = content // Bold text .replace(/\*\*(.*?)\*\*/g, '$1') // Italic text .replace(/\*(.*?)\*/g, '$1') // Line breaks .replace(/\n/g, '
'); return ; }; function App() { const [messages, setMessages] = useState([]); const [inputMessage, setInputMessage] = useState(''); const [isLoading, setIsLoading] = useState(false); const [profile, setProfile] = useState(null); const [isConnected, setIsConnected] = useState(true); const [error, setError] = useState(null); const [isProfileLoading, setIsProfileLoading] = useState(true); const messagesEndRef = useRef(null); const scrollToBottom = useCallback(() => { requestAnimationFrame(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" }); }); }, []); // Check backend health periodically useEffect(() => { const checkHealth = async () => { const healthy = await checkBackendHealth(); setIsConnected(healthy); }; checkHealth(); const interval = setInterval(checkHealth, 30000); // Check every 30 seconds return () => clearInterval(interval); }, []); useEffect(() => { scrollToBottom(); }, [messages, isLoading, scrollToBottom]); useEffect(() => { // Fetch profile data with error handling const fetchProfile = async () => { try { setIsProfileLoading(true); const response = await fetch(config.apiUrl('/profile')); if (!response.ok) throw new Error('Failed to fetch profile'); // Check if response is actually JSON const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { throw new Error('Invalid response format'); } const data = await response.json(); setProfile(data); setError(null); setIsConnected(true); } catch (err) { console.error('Error fetching profile:', err); // Only show demo data if backend is completely unavailable // Don't hardcode - this should be temporary fallback only if (err.message.includes('Failed to fetch') || err.message.includes('network')) { setError('Backend not available - please check backend service'); setIsConnected(false); // Minimal fallback - should be replaced with real API setProfile({ name: "Resume Loading...", title: "Please wait while backend loads", description: "Backend service is starting up. Please refresh in a moment.", skills: ["Loading..."], contact: null, expertise_areas: [], achievements: [] }); } else { // Backend is responding but with errors - show the error setError(`Backend error: ${err.message}`); setIsConnected(false); setProfile(null); } } finally { setIsProfileLoading(false); } }; fetchProfile(); }, []); const sendMessage = async (messageText) => { if (!messageText.trim()) return; const userMessage = messageText.trim(); setInputMessage(''); setIsLoading(true); setError(null); // Add user message to chat const newUserMessage = { type: 'user', content: userMessage, timestamp: new Date() }; setMessages(prev => [...prev, newUserMessage]); try { // Prepare history for API const history = messages.map(msg => ({ [msg.type]: msg.content })); const response = await fetch(config.apiUrl('/chat'), { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message: userMessage, history: history }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } // Check if response is actually JSON const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { throw new Error('Invalid response format'); } const data = await response.json(); if (data.success) { const botMessage = { type: 'assistant', content: data.response, timestamp: new Date() }; setMessages(prev => [...prev, botMessage]); setIsConnected(true); } else { throw new Error(data.error || 'Failed to get response'); } } catch (error) { console.error('Error:', error); setIsConnected(false); const errorMessage = { type: 'assistant', content: `I apologize, but I'm unable to connect to the backend service right now. The backend at https://snvskiit-resume-backend.hf.space appears to be unavailable.\n\nYour message: "${userMessage}"\n\nPlease:\n1. Check if the backend service is running\n2. Verify the backend URL is correct\n3. Try again in a few moments\n\nThis frontend is designed to work with a live backend API, not as a standalone demo.`, timestamp: new Date() }; setMessages(prev => [...prev, errorMessage]); setError('Backend API not available - please check backend service'); } finally { setIsLoading(false); } }; const handleSubmit = (e) => { e.preventDefault(); sendMessage(inputMessage); }; const handleKeyDown = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); if (inputMessage.trim() && !isLoading) { sendMessage(inputMessage); } } }; const clearChat = () => { setMessages([]); }; return ( <>
{isConnected ? '🟢 Connected' : '🔴 Disconnected'}
{/* Left Panel - Profile Information */}
{error && (
{error}
)} {isProfileLoading ? (

Loading profile...

) : profile ? ( <> {profile.photo_url ? ( {profile.name} { e.target.style.display = 'none'; const nextSibling = e.target.nextSibling; if (nextSibling) { nextSibling.style.display = 'flex'; } }} /> ) : (
👨‍💻
)}
👨‍💻

{profile.name}

{profile.title}

{profile.description}

{/* Contact Information - Prominent Display */} {profile.contact && (

💼 Contact Information

{profile.contact.email && (
{profile.contact.email}
)} {profile.contact.mobile && (
{profile.contact.mobile}
)} {profile.contact.github && ( GitHub )} {profile.contact.linkedin && ( LinkedIn )}
)}

⚡ Technical Skills

{profile.skills.map((skill, index) => ( {skill} ))}

🎯 Areas of Expertise

{profile.expertise_areas.map((area, index) => (
{area.icon}

{area.area}

{area.description}

))}

🏆 Key Achievements

{profile.achievements.map((achievement, index) => (
{achievement}
))}
) : !error ? (

No profile data available

) : null}
{/* Right Panel - Chat Interface */}
{/* Messages */}
{messages.map((message, index) => (
{message.type === 'user' ? : }
{renderMessage(message.content)}
))} {isLoading && (
Satish is thinking...
)}
{/* Input */}
setInputMessage(e.target.value)} onKeyDown={handleKeyDown} placeholder="Type your message here... (Press Enter to send)" disabled={isLoading} autoComplete="off" autoCorrect="off" autoCapitalize="sentences" />
{messages.length > 0 && ( )}
); } export default App;