|
|
document.addEventListener("DOMContentLoaded", () => { |
|
|
|
|
|
const chatForm = document.getElementById("chat-form"); |
|
|
const messageInput = document.getElementById("message-input"); |
|
|
const chatMessages = document.getElementById("chat-messages"); |
|
|
const themeToggle = document.getElementById("theme-toggle"); |
|
|
const sendButton = chatForm.querySelector("button[type='submit']"); |
|
|
const leftColumn = document.getElementById("left-column"); |
|
|
const uploadResumeButton = document.getElementById("upload-resume-button"); |
|
|
const resumeFileInput = document.getElementById("resume-file-input"); |
|
|
const refreshChatButton = document.getElementById("refresh-chat-button"); |
|
|
const menuToggle = document.getElementById("menu-toggle"); |
|
|
|
|
|
|
|
|
const closeMenuButton = document.getElementById("close-menu-button"); |
|
|
const pageOverlay = document.getElementById("page-overlay"); |
|
|
|
|
|
let conversationState = {}; |
|
|
|
|
|
|
|
|
const openMenu = () => { |
|
|
leftColumn.classList.add('active'); |
|
|
pageOverlay.classList.add('active'); |
|
|
document.body.classList.add('menu-open'); |
|
|
}; |
|
|
|
|
|
const closeMenu = () => { |
|
|
leftColumn.classList.remove('active'); |
|
|
pageOverlay.classList.remove('active'); |
|
|
document.body.classList.remove('menu-open'); |
|
|
}; |
|
|
|
|
|
|
|
|
menuToggle.addEventListener('click', openMenu); |
|
|
closeMenuButton.addEventListener('click', closeMenu); |
|
|
pageOverlay.addEventListener('click', closeMenu); |
|
|
|
|
|
const sendMessage = (messageText) => { |
|
|
messageInput.value = messageText; |
|
|
const submitEvent = new Event('submit', { bubbles: true, cancelable: true }); |
|
|
chatForm.dispatchEvent(submitEvent); |
|
|
}; |
|
|
|
|
|
const uploadResumeFile = async (file) => { |
|
|
if (!file) return; |
|
|
if (file.size > 5 * 1024 * 1024) { |
|
|
addMessage("bot", "The selected file is too large. Please upload a file smaller than 5MB."); |
|
|
return; |
|
|
} |
|
|
|
|
|
addMessage("user", `Uploading Resume: <i>${file.name}</i>`); |
|
|
showTypingIndicator(); |
|
|
|
|
|
const formData = new FormData(); |
|
|
formData.append("resume_file", file); |
|
|
|
|
|
try { |
|
|
const response = await fetch("/upload_resume", { |
|
|
method: "POST", |
|
|
body: formData, |
|
|
}); |
|
|
|
|
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); |
|
|
const data = await response.json(); |
|
|
|
|
|
removeTypingIndicator(); |
|
|
|
|
|
if (data.response) { |
|
|
let finalResponse = data.response; |
|
|
finalResponse += "<div class='quick-reply-container'><div class='quick-reply-button clickable-card' data-action='quick_reply' data-value='Resume Analyser'>📄 Analyze Another Resume</div><div class='quick-reply-button clickable-card' data-action='quick_reply' data-value='End Chat'>🚪 End Chat</div></div>"; |
|
|
addMessage("bot", finalResponse); |
|
|
|
|
|
conversationState.state = 'awaiting_initial_action'; |
|
|
|
|
|
} else if (data.error) { |
|
|
addMessage("bot", `Error: ${data.error}`); |
|
|
conversationState.state = 'awaiting_initial_action'; |
|
|
} |
|
|
updatePlaceholder(); |
|
|
|
|
|
} catch (error) { |
|
|
removeTypingIndicator(); |
|
|
console.error("Upload error:", error); |
|
|
addMessage("bot", "Sorry, I encountered an error while uploading your resume."); |
|
|
conversationState.state = 'awaiting_initial_action'; |
|
|
updatePlaceholder(); |
|
|
} |
|
|
resumeFileInput.value = ''; |
|
|
}; |
|
|
|
|
|
chatMessages.addEventListener('click', (event) => { |
|
|
const card = event.target.closest('.clickable-card'); |
|
|
if (!card) return; |
|
|
const action = card.dataset.action; |
|
|
const value = card.dataset.value; |
|
|
if (action === 'details' || action === 'compare' || action === 'quick_reply') { |
|
|
sendMessage(value); |
|
|
const buttonContainer = card.closest('.quick-reply-container'); |
|
|
if (buttonContainer) buttonContainer.remove(); |
|
|
} else if (action === 'restart') { |
|
|
location.reload(); |
|
|
} |
|
|
}); |
|
|
|
|
|
leftColumn.addEventListener('click', (event) => { |
|
|
const featureCard = event.target.closest('[data-action="feature_select"]'); |
|
|
if (!featureCard || conversationState.state === 'asking_questions') { |
|
|
if (conversationState.state === 'asking_questions') { |
|
|
const card = document.querySelector('.card'); |
|
|
card.style.transition = 'outline 0.1s ease-out'; |
|
|
card.style.outline = '2px solid #DB2777'; |
|
|
setTimeout(() => { card.style.outline = 'none'; }, 500); |
|
|
} |
|
|
return; |
|
|
} |
|
|
const featureName = featureCard.dataset.value; |
|
|
sendMessage(featureName); |
|
|
closeMenu(); |
|
|
}); |
|
|
|
|
|
const updatePlaceholder = () => { |
|
|
const currentState = conversationState.state; |
|
|
let shouldBeEnabled = true; |
|
|
let placeholderText = "Send a message..."; |
|
|
|
|
|
if (currentState === 'session_ended') { |
|
|
placeholderText = "Click 'Start Over' to begin a new session."; |
|
|
shouldBeEnabled = false; |
|
|
} else if (currentState === 'awaiting_initial_action') { |
|
|
placeholderText = "Type 'start' or select a feature..."; |
|
|
} else if (currentState === 'awaiting_resume_upload') { |
|
|
placeholderText = "Upload your resume using the upload button..."; |
|
|
shouldBeEnabled = false; |
|
|
} else { |
|
|
placeholderText = "Send a message..."; |
|
|
} |
|
|
|
|
|
messageInput.placeholder = placeholderText; |
|
|
messageInput.disabled = !shouldBeEnabled; |
|
|
sendButton.disabled = !shouldBeEnabled; |
|
|
uploadResumeButton.style.display = (currentState === 'awaiting_resume_upload') ? 'block' : 'none'; |
|
|
|
|
|
if (shouldBeEnabled) { |
|
|
messageInput.focus(); |
|
|
} |
|
|
}; |
|
|
|
|
|
const addMessage = (sender, message) => { |
|
|
const messageElement = document.createElement("div"); |
|
|
messageElement.classList.add("message", `${sender}-message`); |
|
|
if (sender === "bot") { |
|
|
const avatarImg = document.createElement("img"); |
|
|
avatarImg.src = "/static/bot_avatar.png"; |
|
|
avatarImg.alt = "Bot Avatar"; |
|
|
avatarImg.className = "bot-avatar"; |
|
|
messageElement.appendChild(avatarImg); |
|
|
} |
|
|
const paragraph = document.createElement("p"); |
|
|
paragraph.innerHTML = message; |
|
|
messageElement.appendChild(paragraph); |
|
|
chatMessages.appendChild(messageElement); |
|
|
|
|
|
}; |
|
|
|
|
|
const showTypingIndicator = () => { |
|
|
messageInput.disabled = true; |
|
|
sendButton.disabled = true; |
|
|
if (document.getElementById("typing-indicator")) return; |
|
|
const indicatorElement = document.createElement("div"); |
|
|
indicatorElement.id = "typing-indicator"; |
|
|
indicatorElement.classList.add("message", "bot-message"); |
|
|
indicatorElement.innerHTML = `<img src="/static/bot_avatar.png" alt="Bot Avatar" class="bot-avatar"><div class="typing-indicator"><span></span><span></span><span></span></div>`; |
|
|
chatMessages.appendChild(indicatorElement); |
|
|
chatMessages.scrollTop = chatMessages.scrollHeight; |
|
|
}; |
|
|
|
|
|
const removeTypingIndicator = () => { |
|
|
const indicator = document.getElementById("typing-indicator"); |
|
|
if (indicator) indicator.remove(); |
|
|
}; |
|
|
|
|
|
const handleFormSubmit = async (event) => { |
|
|
event.preventDefault(); |
|
|
const message = messageInput.value.trim(); |
|
|
if (!message) return; |
|
|
|
|
|
addMessage("user", message); |
|
|
chatMessages.scrollTop = chatMessages.scrollHeight; |
|
|
|
|
|
messageInput.value = ""; |
|
|
showTypingIndicator(); |
|
|
|
|
|
try { |
|
|
const response = await fetch("/chat", { |
|
|
method: "POST", |
|
|
headers: { "Content-Type": "application/json" }, |
|
|
body: JSON.stringify({ message: message, conversation: conversationState }), |
|
|
}); |
|
|
|
|
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); |
|
|
const data = await response.json(); |
|
|
|
|
|
setTimeout(() => { |
|
|
removeTypingIndicator(); |
|
|
conversationState = data.conversation; |
|
|
if (data.response) { |
|
|
addMessage("bot", data.response); |
|
|
} |
|
|
updatePlaceholder(); |
|
|
}, 500); |
|
|
|
|
|
} catch (error) { |
|
|
removeTypingIndicator(); |
|
|
console.error("Fetch error:", error); |
|
|
addMessage("bot", "Sorry, I'm having trouble connecting."); |
|
|
updatePlaceholder(); |
|
|
} |
|
|
}; |
|
|
|
|
|
const initializeChat = async () => { |
|
|
try { |
|
|
const response = await fetch("/chat", { |
|
|
method: "POST", |
|
|
headers: { "Content-Type": "application/json" }, |
|
|
body: JSON.stringify({ message: "", conversation: {} }), |
|
|
}); |
|
|
const data = await response.json(); |
|
|
conversationState = data.conversation; |
|
|
addMessage("bot", data.response); |
|
|
updatePlaceholder(); |
|
|
} catch (error) { |
|
|
console.error("Initialization error:", error); |
|
|
addMessage("bot", "Sorry, I'm having trouble connecting."); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleThemeChange = () => { |
|
|
document.body.className = themeToggle.checked ? 'dark' : 'light'; |
|
|
}; |
|
|
|
|
|
refreshChatButton.addEventListener('click', (event) => { |
|
|
event.preventDefault(); |
|
|
location.reload(); |
|
|
}); |
|
|
|
|
|
chatForm.addEventListener("submit", handleFormSubmit); |
|
|
themeToggle.addEventListener("change", handleThemeChange); |
|
|
uploadResumeButton.addEventListener('click', () => resumeFileInput.click()); |
|
|
resumeFileInput.addEventListener('change', (event) => uploadResumeFile(event.target.files[0])); |
|
|
|
|
|
document.body.className = 'light'; |
|
|
initializeChat(); |
|
|
}); |