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 (
<>
Loading profile...
{profile.title}
{profile.description}
{/* Contact Information - Prominent Display */} {profile.contact && ({area.description}
No profile data available