const state = { mode: "idle", step: null, booking: {}, cancellationId: "", feedbackRating: null, }; const doctors = { Cardiology: ["Dr. Asha Patel", "Dr. Michael Reed"], Orthopedics: ["Dr. Daniel Brooks", "Dr. Priya Nair"], Dermatology: ["Dr. Sara Kim", "Dr. Omar Khan"], Pediatrics: ["Dr. Neha Shah", "Dr. Luis Moreno"], "Internal Medicine": ["Dr. Rachel Adams", "Dr. Vikram Rao"], }; const symptomDepartments = [ { terms: ["chest", "heart", "palpitation", "blood pressure"], department: "Cardiology" }, { terms: ["knee", "bone", "joint", "back", "fracture", "shoulder"], department: "Orthopedics" }, { terms: ["rash", "skin", "acne", "itch"], department: "Dermatology" }, { terms: ["child", "baby", "pediatric", "fever"], department: "Pediatrics" }, ]; const faqs = [ { terms: ["visiting", "hours"], answer: "General visiting hours are 9:00 AM to 7:00 PM. Intensive care and specialty units may use separate schedules.", }, { terms: ["document", "bring", "id", "insurance"], answer: "Please bring a photo ID, insurance card if applicable, referral letter if required, medication list, and previous reports.", }, { terms: ["emergency", "er", "urgent"], answer: "The emergency department is open 24/7. If this is a medical emergency, call emergency services or go to the nearest emergency department.", }, { terms: ["parking"], answer: "Patient parking is available near the main entrance. Accessible parking is located beside the reception entrance.", }, { terms: ["telehealth", "online", "video"], answer: "Telehealth appointments are available for selected departments. The care team confirms eligibility during booking.", }, ]; const chatLog = document.querySelector("[data-chat-log]"); const form = document.querySelector("[data-chat-form]"); const input = document.querySelector("[data-chat-input]"); const quickActions = document.querySelector("[data-quick-actions]"); const memoryPanel = document.querySelector("[data-memory]"); const appointmentPanel = document.querySelector("[data-appointments]"); const resetButton = document.querySelector("[data-reset]"); function getMemory() { return JSON.parse(localStorage.getItem("mediflow-memory") || "{}"); } function setMemory(nextMemory) { localStorage.setItem("mediflow-memory", JSON.stringify(nextMemory)); renderPanels(); } function getAppointments() { return JSON.parse(localStorage.getItem("mediflow-appointments") || "[]"); } function setAppointments(appointments) { localStorage.setItem("mediflow-appointments", JSON.stringify(appointments)); renderPanels(); } function addMessage(sender, text, options = {}) { const message = document.createElement("div"); message.className = `message ${sender}`; if (options.card) { message.classList.add("card-message"); } message.innerHTML = text; chatLog.appendChild(message); chatLog.scrollTop = chatLog.scrollHeight; } function addBot(text, options) { addMessage("bot", text, options); } function addUser(text) { addMessage("user", escapeHtml(text)); } function escapeHtml(value) { return value.replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'", }[char])); } function renderQuickActions(actions = []) { quickActions.innerHTML = ""; actions.forEach((action) => { const button = document.createElement("button"); button.type = "button"; button.className = "choice"; button.textContent = action.label; button.addEventListener("click", () => handleInput(action.value || action.label, true)); quickActions.appendChild(button); }); } function renderPanels() { const memory = getMemory(); const appointments = getAppointments(); memoryPanel.innerHTML = `
Name
${memory.fullName || "Not saved"}
Department
${memory.preferredDepartment || "Not saved"}
Doctor
${memory.preferredDoctor || "Not saved"}
`; if (!appointments.length) { appointmentPanel.innerHTML = "

No active prototype appointments.

"; return; } appointmentPanel.innerHTML = appointments.map((appointment) => `
${appointment.id} ${appointment.doctor} ${appointment.department} · ${appointment.date} · ${appointment.time}
`).join(""); } function start() { const memory = getMemory(); const name = memory.fullName ? ` Welcome back, ${memory.fullName}.` : ""; addBot(`Hello, I am MediFlow Assistant.${name} I can help you book a doctor appointment, cancel an appointment, answer hospital questions, or collect feedback.`); renderQuickActions([ { label: "Book appointment" }, { label: "Cancel appointment" }, { label: "Ask hospital question" }, { label: "Give feedback" }, ]); renderPanels(); } function routeIntent(text) { const lower = text.toLowerCase(); if (lower.includes("cancel")) return beginCancellation(); if (lower.includes("feedback") || lower.includes("rate")) return beginFeedback(); if (lower.includes("hour") || lower.includes("insurance") || lower.includes("parking") || lower.includes("document") || lower.includes("emergency") || lower.includes("telehealth")) return answerFaq(text); return beginBooking(text); } function beginBooking(initialText = "") { const memory = getMemory(); state.mode = "booking"; state.step = memory.fullName ? "reuseName" : "name"; state.booking = {}; if (initialText) { const department = detectDepartment(initialText); if (department) { state.booking.department = department; state.booking.symptoms = initialText; } } if (memory.fullName) { addBot(`Should I book this appointment for ${memory.fullName}?`); renderQuickActions([{ label: "Yes, use saved name" }, { label: "No, enter another name" }]); return; } addBot("Please enter the patient's full name."); renderQuickActions([]); } function continueBooking(text) { const memory = getMemory(); if (state.step === "reuseName") { if (text.toLowerCase().startsWith("yes")) { state.booking.patientName = memory.fullName; state.step = "contact"; addBot("Please enter a phone number or email for appointment updates."); renderQuickActions([]); return; } state.step = "name"; addBot("Please enter the patient's full name."); renderQuickActions([]); return; } if (state.step === "name") { state.booking.patientName = text; setMemory({ ...memory, fullName: text }); state.step = "contact"; addBot("Please enter a phone number or email for appointment updates."); return; } if (state.step === "contact") { state.booking.contact = text; if (state.booking.department) { state.step = "doctor"; askDoctor(); return; } state.step = "department"; addBot("Which department, doctor, or symptom should I use for this appointment?"); renderQuickActions([ { label: "Cardiology" }, { label: "Orthopedics" }, { label: "Dermatology" }, { label: "Pediatrics" }, ]); return; } if (state.step === "department") { const department = detectDepartment(text); state.booking.symptoms = text; state.booking.department = department || "Internal Medicine"; setMemory({ ...getMemory(), preferredDepartment: state.booking.department }); state.step = "doctor"; askDoctor(); return; } if (state.step === "doctor") { state.booking.doctor = text === "No preference" ? doctors[state.booking.department][0] : text; setMemory({ ...getMemory(), preferredDoctor: state.booking.doctor }); state.step = "date"; addBot("What date would you prefer? Example: May 20, 2026."); renderQuickActions([]); return; } if (state.step === "date") { state.booking.date = text; state.step = "time"; addBot("What time would you prefer?"); renderQuickActions([ { label: "9:30 AM" }, { label: "11:00 AM" }, { label: "2:30 PM" }, { label: "4:00 PM" }, ]); return; } if (state.step === "time") { state.booking.time = text; state.step = "confirm"; addConfirmationPreview(); renderQuickActions([{ label: "Confirm" }, { label: "Change details" }, { label: "Cancel booking" }]); return; } if (state.step === "confirm") { if (text === "Confirm") return confirmBooking(); if (text === "Change details") return beginBooking(); state.mode = "idle"; state.step = null; addBot("No problem. I cancelled this draft booking."); renderQuickActions([{ label: "Book appointment" }, { label: "Ask hospital question" }]); } } function detectDepartment(text) { const lower = text.toLowerCase(); const exact = Object.keys(doctors).find((department) => lower.includes(department.toLowerCase())); if (exact) return exact; const mapped = symptomDepartments.find((item) => item.terms.some((term) => lower.includes(term))); return mapped?.department || null; } function askDoctor() { const department = state.booking.department; const options = [...doctors[department], "No preference"]; addBot(`I mapped that request to ${department}. Please choose a doctor.`); addBot(`
${department.slice(0, 1)}
${doctors[department][0]} ${department} · Available Mon/Wed/Fri Caption: Doctor profile card showing specialty and available appointment days.
`, { card: true }); renderQuickActions(options.map((label) => ({ label }))); } function addConfirmationPreview() { const booking = state.booking; addBot(`
Appointment draft ${booking.patientName} ${booking.department} with ${booking.doctor} ${booking.date} at ${booking.time} Caption: Appointment confirmation card showing doctor, department, date, time, and patient name.
`, { card: true }); } function confirmBooking() { const id = `MF-${Date.now().toString().slice(-6)}`; const appointment = { ...state.booking, id }; setAppointments([...getAppointments(), appointment]); state.mode = "idle"; state.step = null; addBot(`Your appointment is confirmed. Appointment ID: ${id}.`); addBot("Would you like to leave feedback or ask another hospital question?"); renderQuickActions([{ label: "Give feedback" }, { label: "Ask hospital question" }, { label: "Book another appointment" }]); } function beginCancellation() { state.mode = "cancellation"; state.step = "identify"; addBot("Please enter your appointment ID."); renderQuickActions(getAppointments().map((appointment) => ({ label: appointment.id }))); } function continueCancellation(text) { const appointment = getAppointments().find((item) => item.id.toLowerCase() === text.toLowerCase()); if (!appointment) { addBot("I could not find that prototype appointment ID. Please check the ID or start a new booking."); renderQuickActions([{ label: "Book appointment" }, { label: "Cancel appointment" }]); state.mode = "idle"; return; } if (state.step === "identify") { state.cancellationId = appointment.id; state.step = "confirmCancel"; addBot(`I found ${appointment.id}: ${appointment.department} with ${appointment.doctor} on ${appointment.date} at ${appointment.time}. Do you want to cancel it?`); renderQuickActions([{ label: "Yes, cancel" }, { label: "No, keep appointment" }]); return; } } function finishCancellation(text) { if (text === "Yes, cancel") { setAppointments(getAppointments().filter((appointment) => appointment.id !== state.cancellationId)); addBot("Your appointment has been cancelled. You can book another appointment if needed."); } else { addBot("Okay, I kept the appointment active."); } state.mode = "idle"; state.step = null; renderQuickActions([{ label: "Book appointment" }, { label: "Give feedback" }]); } function answerFaq(text) { state.mode = "idle"; state.step = null; const lower = text.toLowerCase(); const match = faqs.find((faq) => faq.terms.some((term) => lower.includes(term))); addBot(match ? match.answer : "I can help with visiting hours, required documents, emergency care, parking, and telehealth."); renderQuickActions([ { label: "Visiting hours" }, { label: "Required documents" }, { label: "Emergency department" }, { label: "Book appointment" }, ]); } function beginFeedback() { state.mode = "feedback"; state.step = "rating"; addBot("How would you rate this experience from 1 to 5?"); renderQuickActions(["1", "2", "3", "4", "5"].map((label) => ({ label }))); } function continueFeedback(text) { if (state.step === "rating") { state.feedbackRating = text; state.step = "comment"; addBot("Please share one thing we could improve."); renderQuickActions([]); return; } addBot(`Thank you. I saved your prototype feedback rating as ${state.feedbackRating}.`); state.mode = "idle"; state.step = null; renderQuickActions([{ label: "Book appointment" }, { label: "Ask hospital question" }]); } function handleInput(rawText, fromQuickAction = false) { const text = rawText.trim(); if (!text) return; addUser(text); input.value = ""; if (text === "Book appointment" || text === "Book another appointment") return beginBooking(); if (text === "Cancel appointment") return beginCancellation(); if (text === "Ask hospital question") { state.mode = "faq"; addBot("What hospital question can I help with?"); renderQuickActions([{ label: "Visiting hours" }, { label: "Required documents" }, { label: "Parking" }, { label: "Telehealth" }]); return; } if (text === "Give feedback") return beginFeedback(); if (state.mode === "booking") return continueBooking(text); if (state.mode === "cancellation" && state.step === "confirmCancel") return finishCancellation(text); if (state.mode === "cancellation") return continueCancellation(text); if (state.mode === "feedback") return continueFeedback(text); if (state.mode === "faq" || fromQuickAction) return answerFaq(text); return routeIntent(text); } form.addEventListener("submit", (event) => { event.preventDefault(); handleInput(input.value); }); resetButton.addEventListener("click", () => { localStorage.removeItem("mediflow-memory"); localStorage.removeItem("mediflow-appointments"); chatLog.innerHTML = ""; state.mode = "idle"; state.step = null; state.booking = {}; start(); }); start();