// Verify MC Patient Intake Widget // Production-grade, conversion-optimized form with AI assistance (function() { 'use strict'; // State management const state = { formData: {}, aiProcessing: false, sections: { personal: { fields: 5, filled: 0 }, emergency: { fields: 3, filled: 0 }, insurance: { fields: 4, filled: 0 }, medical: { fields: 4, filled: 0 }, visit: { fields: 2, filled: 0 } }, totalFields: 18, filledFields: 0 }; // Initialize document.addEventListener('DOMContentLoaded', init); function init() { loadSavedData(); attachEventListeners(); updateProgress(); initializePhoneFormatting(); } // Event Listeners function attachEventListeners() { const form = document.getElementById('intakeForm'); const inputs = form.querySelectorAll('input, select, textarea'); inputs.forEach(input => { // Real-time validation and progress input.addEventListener('blur', () => validateField(input)); input.addEventListener('input', () => { updateFieldStatus(input); debouncedProgressUpdate(); }); // AI suggestion on focus for certain fields if (input.name === 'symptoms' || input.name === 'medications') { input.addEventListener('focus', () => showAIHint(input)); } }); // Enter key in AI input document.getElementById('aiInput').addEventListener('keypress', (e) => { if (e.key === 'Enter') processAIInput(); }); // Form submission prevention form.addEventListener('submit', (e) => { e.preventDefault(); submitForm(); }); } // Phone number formatting function initializePhoneFormatting() { const phoneInputs = document.querySelectorAll('input[type="tel"]'); phoneInputs.forEach(input => { input.addEventListener('input', (e) => { let value = e.target.value.replace(/\D/g, ''); if (value.length >= 6) { value = `(${value.slice(0, 3)}) ${value.slice(3, 6)}-${value.slice(6, 10)}`; } else if (value.length >= 3) { value = `(${value.slice(0, 3)}) ${value.slice(3)}`; } e.target.value = value; }); }); } // Field validation function validateField(input) { const isRequired = input.hasAttribute('required'); const isEmpty = !input.value.trim(); if (isRequired && isEmpty) { input.classList.add('error'); showFieldError(input, 'This field is required'); } else if (input.type === 'email' && input.value && !isValidEmail(input.value)) { input.classList.add('error'); showFieldError(input, 'Please enter a valid email'); } else { input.classList.remove('error'); clearFieldError(input); } } function isValidEmail(email) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); } function showFieldError(input, message) { clearFieldError(input); const error = document.createElement('p'); error.className = 'field-error text-verify-error text-xs mt-1.5 flex items-center gap-1'; error.innerHTML = `${message}`; input.parentNode.appendChild(error); lucide.createIcons(); } function clearFieldError(input) { const existing = input.parentNode.querySelector('.field-error'); if (existing) existing.remove(); } // Progress tracking function updateFieldStatus(input) { const section = input.dataset.section; if (!section) return; const isFilled = input.type === 'checkbox' ? input.checked : input.value.trim().length > 0; state.formData[input.name] = isFilled ? input.value : null; // Auto-save to localStorage debouncedSave(); } let progressDebounceTimer; function debouncedProgressUpdate() { clearTimeout(progressDebounceTimer); progressDebounceTimer = setTimeout(updateProgress, 100); } function updateProgress() { const sections = ['personal', 'emergency', 'insurance', 'medical', 'visit']; let totalFilled = 0; let totalCount = 0; sections.forEach(sectionKey => { const sectionEl = document.querySelector(`[data-section="${sectionKey}"]`)?.closest('section'); if (!sectionEl) return; const fields = sectionEl.querySelectorAll('input, select, textarea'); let filled = 0; let count = 0; fields.forEach(field => { // Skip checkboxes for simple counting, focus on text inputs if (field.type === 'checkbox') return; count++; totalCount++; const isFilled = field.value.trim().length > 0; if (isFilled) { filled++; totalFilled++; } }); // Update section progress indicator const progressEl = document.getElementById(`${sectionKey}Progress`); if (progressEl) { progressEl.textContent = `${filled}/${count}`; if (filled === count) { progressEl.className = 'text-verify-teal text-xs ml-auto font-medium'; } else { progressEl.className = 'text-verify-muted text-xs ml-auto'; } } }); // Update main progress bars const percentage = Math.round((totalFilled / totalCount) * 100) || 0; document.getElementById('progressBar').style.width = `${percentage}%`; document.getElementById('bottomProgress').style.width = `${percentage}%`; document.getElementById('completionText').textContent = `${percentage}% complete`; // Visual feedback on completion milestones if (percentage === 100 && !state.completedShown) { state.completedShown = true; showToast('All fields completed! Ready to submit.'); } } // AI Processing window.processAIInput = async function() { const input = document.getElementById('aiInput'); const btn = document.getElementById('aiTryBtn'); const suggestions = document.getElementById('aiSuggestions'); const text = input.value.trim(); if (!text) return; // Show processing state state.aiProcessing = true; btn.disabled = true; btn.innerHTML = `Processing...`; lucide.createIcons(); // Simulate AI processing (in production, this would call an API) await new Promise(r => setTimeout(r, 1500)); // Parse and extract information const extracted = extractInfoFromText(text); // Display suggestions displayAISuggestions(extracted, suggestions); // Reset button btn.disabled = false; btn.innerHTML = ``; lucide.createIcons(); state.aiProcessing = false; }; function extractInfoFromText(text) { const info = {}; const lower = text.toLowerCase(); // Name extraction const nameMatch = text.match(/(?:name is|i am|my name is)\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)/i) || text.match(/^([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)/); if (nameMatch) info.fullName = nameMatch[1]; // Date extraction (various formats) const dateMatch = text.match(/(?:born|birth|dob|birthday)[\s,]*([A-Za-z]+\s+\d{1,2}[,.]?\s*\d{4}|\d{1,2}[\/\-.]\d{1,2}[\/\-.]\d{2,4})/i); if (dateMatch) { info.dateOfBirth = parseDate(dateMatch[1]); } // Phone extraction const phoneMatch = text.match(/(\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4})/); if (phoneMatch) info.phone = phoneMatch[1]; // Email extraction const emailMatch = text.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/); if (emailMatch) info.email = emailMatch[1]; // Allergies if (lower.includes('allergic to') || lower.includes('allergy')) { const allergyMatch = text.match(/allergic to\s+([^,.]+)/i); if (allergyMatch) info.allergies = allergyMatch[1]; } // Medications if (lower.includes('take') || lower.includes('medication')) { const medMatch = text.match(/(?:take|taking|on)\s+([^,.]+(?:mg|daily|twice|once)[^,.]*)/i); if (medMatch) info.medications = medMatch[1]; } // Insurance const insuranceKeywords = { 'aetna': 'aetna', 'anthem': 'anthem', 'blue cross': 'bcbs', 'bluecross': 'bcbs', 'cigna': 'cigna', 'kaiser': 'kaiser', 'medicare': 'medicare', 'medicaid': 'medicaid', 'united': 'united' }; for (const [keyword, value] of Object.entries(insuranceKeywords)) { if (lower.includes(keyword)) { info.insuranceProvider = value; break; } } // Conditions const conditions = []; if (lower.includes('diabetes')) conditions.push('diabetes'); if (lower.includes('hypertension') || lower.includes('high blood pressure')) conditions.push('hypertension'); if (lower.includes('asthma')) conditions.push('asthma'); if (lower.includes('heart')) conditions.push('heart-disease'); if (conditions.length) info.conditions = conditions; return info; } function parseDate(dateStr) { // Try to parse various date formats and return YYYY-MM-DD const tryParse = new Date(dateStr); if (!isNaN(tryParse)) { return tryParse.toISOString().split('T')[0]; } return null; } function displayAISuggestions(extracted, container) { container.innerHTML = ''; container.classList.remove('hidden'); const suggestions = Object.entries(extracted).filter(([key, value]) => value); if (suggestions.length === 0) { container.innerHTML = `
We couldn't extract specific information. Try being more specific with names, dates, and numbers.
`; lucide.createIcons(); return; } suggestions.forEach(([field, value]) => { const div = document.createElement('div'); div.className = 'ai-suggestion p-3 bg-white border border-verify-teal/30 rounded-xl flex items-center justify-between gap-3'; const displayField = field.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase()); const displayValue = Array.isArray(value) ? value.join(', ') : value; div.innerHTML = `
${displayField}

${displayValue}

`; container.appendChild(div); }); lucide.createIcons(); } window.applyAIValue = function(field, value) { const input = document.querySelector(`[name="${field}"]`); if (!input) return; if (field === 'conditions') { const conditions = value.split(','); conditions.forEach(c => { const checkbox = document.querySelector(`[name="conditions"][value="${c}"]`); if (checkbox) checkbox.checked = true; }); } else { input.value = value; } updateFieldStatus(input); updateProgress(); // Remove applied suggestion const suggestions = document.getElementById('aiSuggestions'); const applied = suggestions.querySelector(`button[onclick*="${field}"]`)?.closest('.ai-suggestion'); if (applied) { applied.style.opacity = '0.5'; applied.querySelector('button').textContent = 'Applied'; applied.querySelector('button').disabled = true; } showToast(`${field.replace(/([A-Z])/g, ' $1')} applied`); }; // Allergy tag toggle window.toggleAllergy = function(btn, allergy) { const input = document.getElementById('allergiesInput'); const current = input.value.split(',').map(s => s.trim()).filter(s => s); if (btn.classList.contains('bg-verify-teal')) { // Remove btn.classList.remove('bg-verify-teal', 'text-white'); btn.classList.add('bg-gray-100', 'text-verify-muted'); const idx = current.indexOf(allergy); if (idx > -1) current.splice(idx, 1); } else { // Add btn.classList.remove('bg-gray-100', 'text-verify-muted'); btn.classList.add('bg-verify-teal', 'text-white'); if (!current.includes(allergy)) current.push(allergy); } input.value = current.join(', '); updateFieldStatus(input); }; // Form submission window.submitForm = async function() { const form = document.getElementById('intakeForm'); const required = form.querySelectorAll('[required]'); let valid = true; required.forEach(field => { validateField(field); if (!field.value.trim()) valid = false; }); if (!valid) { showToast('Please fill in all required fields'); // Scroll to first error const firstError = form.querySelector('.error'); if (firstError) { firstError.scrollIntoView({ behavior: 'smooth', block: 'center' }); firstError.focus(); } return; } // Simulate submission const submitBtn = document.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.disabled = true; submitBtn.innerHTML = `Submitting...`; lucide.createIcons(); await new Promise(r => setTimeout(r, 2000)); // Clear saved data localStorage.removeItem('verifyMC_intakeDraft'); // Show success document.getElementById('successModal').classList.remove('hidden'); document.getElementById('successModal').classList.add('flex'); }; window.closeModal = function() { document.getElementById('successModal').classList.add('hidden'); document.getElementById('successModal').classList.remove('flex'); // Reset form document.getElementById('intakeForm').reset(); updateProgress(); }; // Draft saving window.saveDraft = function() { const form = document.getElementById('intakeForm'); const data = new FormData(form); const saved = {}; for (const [key, value] of data.entries()) { if (saved[key]) { if (!Array.isArray(saved[key])) saved[key] = [saved[key]]; saved[key].push(value); } else { saved[key] = value; } } localStorage.setItem('verifyMC_intakeDraft', JSON.stringify(saved)); showToast('Draft saved — you can return anytime'); }; function loadSavedData() { const saved = localStorage.getItem('verifyMC_intakeDraft'); if (!saved) return; try { const data = JSON.parse(saved); Object.entries(data).forEach(([key, value]) => { const field = document.querySelector(`[name="${key}"]`); if (!field) return; if (field.type === 'checkbox') { field.checked = true; } else if (Array.isArray(value)) { // Handle multi-select checkboxes value.forEach(v => { const cb = document.querySelector(`[name="${key}"][value="${v}"]`); if (cb) cb.checked = true; }); } else { field.value = value; } }); } catch (e) { console.error('Failed to load saved data:', e); } } let saveDebounceTimer; function debouncedSave() { clearTimeout(saveDebounceTimer); saveDebounceTimer = setTimeout(() => { const form = document.getElementById('intakeForm'); const data = new FormData(form); const saved = {}; for (const [key, value] of data.entries()) { saved[key] = value; } localStorage.setItem('verifyMC_intakeDraft', JSON.stringify(saved)); }, 2000); } // Toast notifications function showToast(message) { const toast = document.getElementById('toast'); document.getElementById('toastMessage').textContent = message; toast.classList.remove('hidden'); toast.classList.add('flex'); setTimeout(() => { toast.classList.add('hidden'); toast.classList.remove('flex'); }, 3000); } // AI hints for specific fields function showAIHint(input) { // Could show contextual AI suggestions based on field type // Currently minimal to avoid intrusion } })();