// Initialize Lucide Icons lucide.createIcons(); // ========================================= // AI VOICE ACTIONS WORKFLOW ENGINE // ========================================= const VoiceActions = { isOpen: false, currentStep: 1, totalSteps: 5, providerData: { name: '', email: '' }, automationTasks: [ "Creating provider profile", "Registering in provider directory", "Preparing account access", "Assigning provider role & permissions", "Creating default scheduling profile", "Applying weekly availability (Mon-Fri, 9-5)", "Enabling appointment reminders", "Enabling notification preferences", "Preparing booking visibility", "Queuing invitation email", "Syncing to Scheduling OS", "Finalizing onboarding status" ], // Panel Management togglePanel: function() { if (this.isOpen) { this.closePanel(); } else { this.openPanel(); } }, openPanel: function() { this.isOpen = true; const panel = document.getElementById('voice-panel'); const overlay = document.getElementById('voice-overlay'); const overlayBg = document.getElementById('voice-overlay-bg'); overlay.classList.remove('hidden'); panel.classList.remove('translate-x-full'); // Trigger reflow for animation void overlay.offsetWidth; overlayBg.classList.remove('opacity-0'); // Add mic pulse effect to button in main bar const micBtn = document.getElementById('voice-input-btn'); if(micBtn) { const pulse = document.createElement('div'); pulse.className = 'mic-active-ring'; micBtn.appendChild(pulse); } }, closePanel: function() { this.isOpen = false; const panel = document.getElementById('voice-panel'); const overlay = document.getElementById('voice-overlay'); const overlayBg = document.getElementById('voice-overlay-bg'); panel.classList.add('translate-x-full'); overlayBg.classList.add('opacity-0'); setTimeout(() => { overlay.classList.add('hidden'); this.exitWorkflow(); // Reset to menu if closed }, 300); // Remove mic pulse const micBtn = document.getElementById('voice-input-btn'); if(micBtn) { const pulse = micBtn.querySelector('.mic-active-ring'); if(pulse) pulse.remove(); } }, // Workflow Navigation startWorkflow: function() { this.currentStep = 1; this.providerData = { name: '', email: '' }; document.getElementById('voice-menu-view').classList.add('hidden'); document.getElementById('voice-workflow-view').classList.remove('hidden'); document.getElementById('voice-workflow-view').classList.add('flex'); this.renderStep(); this.updateUIState(); }, exitWorkflow: function() { document.getElementById('voice-menu-view').classList.remove('hidden'); document.getElementById('voice-workflow-view').classList.add('hidden'); document.getElementById('voice-workflow-view').classList.remove('flex'); this.currentStep = 1; }, nextStep: function() { // Validation if (this.currentStep === 1 && !this.providerData.name.trim()) { this.shakeInput('provider-name'); return; } if (this.currentStep === 2 && !this.validateEmail(this.providerData.email)) { this.shakeInput('provider-email'); return; } if (this.currentStep < this.totalSteps) { this.currentStep++; this.renderStep(); this.updateUIState(); } }, prevStep: function() { if (this.currentStep > 1) { this.currentStep--; this.renderStep(); this.updateUIState(); } }, updateUIState: function() { // Update Progress const progressPct = (this.currentStep / this.totalSteps) * 100; document.getElementById('workflow-progress').style.width = `${progressPct}%`; document.getElementById('workflow-step-badge').textContent = `Step ${this.currentStep} of ${this.totalSteps}`; // Update Buttons const backBtn = document.getElementById('workflow-back-btn'); const nextBtn = document.getElementById('workflow-next-btn'); backBtn.disabled = this.currentStep === 1; nextBtn.textContent = this.currentStep === 4 ? 'Run Automation' : this.currentStep === 5 ? 'Done' : 'Continue'; if (this.currentStep === 5) { nextBtn.classList.add('hidden'); backBtn.classList.add('hidden'); } else { nextBtn.classList.remove('hidden'); backBtn.classList.remove('hidden'); } }, renderStep: function() { const container = document.getElementById('workflow-content'); let html = ''; switch(this.currentStep) { case 1: // Name html = `

Provider Name

Enter the provider's display name for profile creation and scheduling setup.

`; break; case 2: // Email html = `

Provider Email

Enter the email address for invitations, login access, and notifications.

`; break; case 3: // Verify html = `

Verify Details

Review provider information before automation begins.

Name
${this.providerData.name}
Email
${this.providerData.email}

Recommended Defaults

`; break; case 4: // Automation Simulation html = `

Setting Up Profile

AI is configuring your provider settings...

Progress 0%
${this.automationTasks.map((task, i) => `
${task}
`).join('')}
`; // Trigger animation after render setTimeout(() => this.runAutomation(), 100); break; case 5: // Success html = `

Provider Onboarding Completed

The provider account and onboarding defaults have been prepared successfully.

Provider ${this.providerData.name}
Account Status Ready
Scheduling Mon-Fri, 9-5
Invitation Queued

Recommended defaults were applied based on standard provider onboarding policies.

`; break; } container.innerHTML = html; lucide.createIcons(); // Focus inputs on Steps 1 & 2 if(this.currentStep === 1) setTimeout(() => document.getElementById('provider-name')?.focus(), 50); if(this.currentStep === 2) setTimeout(() => document.getElementById('provider-email')?.focus(), 50); }, // Logic Helpers updateData: function(key, value) { this.providerData[key] = value; // Clear errors if(key === 'email') { const err = document.getElementById('email-error'); if(err) err.classList.add('hidden'); } }, goToStep: function(step) { this.currentStep = step; this.renderStep(); this.updateUIState(); }, validateEmail: function(email) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); }, shakeInput: function(id) { const el = document.getElementById(id); if(el) { el.classList.add('border-red-500', 'animate-pulse'); setTimeout(() => el.classList.remove('border-red-500', 'animate-pulse'), 1000); } if(id === 'provider-email') { document.getElementById('email-error').classList.remove('hidden'); } }, runAutomation: async function() { const tasks = this.automationTasks; const bar = document.getElementById('automation-bar'); const text = document.getElementById('automation-percent'); for (let i = 0; i < tasks.length; i++) { // Update UI const item = document.getElementById(`task-${i}`); item.classList.add('active'); item.scrollIntoView({ behavior: 'smooth', block: 'center' }); // Wait random time await new Promise(r => setTimeout(r, 400 + Math.random() * 400)); // Complete Item item.classList.remove('active'); item.classList.add('completed'); // Update Progress Bar const pct = Math.round(((i + 1) / tasks.length) * 100); bar.style.width = `${pct}%`; text.textContent = `${pct}%`; } // Auto advance to success after short delay setTimeout(() => { this.nextStep(); }, 800); } }; // Attach to Voice Button (If exists in DOM, otherwise listener added below) document.addEventListener('DOMContentLoaded', () => { const voiceBtn = document.getElementById('voice-input-btn'); if (voiceBtn) { // Remove any existing listeners to avoid duplicates if script re-runs const newBtn = voiceBtn.cloneNode(true); voiceBtn.parentNode.replaceChild(newBtn, voiceBtn); newBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); VoiceActions.togglePanel(); }); } // Close panel on overlay click document.getElementById('voice-overlay').addEventListener('click', (e) => { if(e.target === document.getElementById('voice-overlay') || e.target.id === 'voice-overlay-bg') { VoiceActions.closePanel(); } }); // Close on ESC document.addEventListener('keydown', (e) => { if(e.key === 'Escape' && VoiceActions.isOpen) { VoiceActions.closePanel(); } }); }); // ========================================= // VOICE & CONVERSATIONAL MODE (VCM V1) // ========================================= const ChatContext = { conversationHistory: [], lastAction: null, userIntent: null }; // Voice Recognition Setup const VoiceRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; let recognition = null; let isRecording = false; if (VoiceRecognition) { recognition = new VoiceRecognition(); recognition.continuous = false; recognition.lang = 'en-US'; recognition.interimResults = true; recognition.onstart = () => { isRecording = true; const btn = document.getElementById('voice-input-btn'); const visualizer = document.getElementById('voice-visualizer'); if (btn) btn.classList.add('active'); if (visualizer) visualizer.classList.add('active'); }; recognition.onresult = (event) => { const transcript = Array.from(event.results) .map(result => result[0]) .map(result => result.transcript) .join(''); const input = document.getElementById('ai-command-input'); if (input && event.results[0].isFinal) { input.value = transcript; // Trigger search/command processing const event = new KeyboardEvent('keydown', { key: 'Enter' }); input.dispatchEvent(event); stopVoiceRecording(); } else if (input) { input.value = transcript; } }; recognition.onerror = (event) => { console.error('Voice recognition error', event.error); stopVoiceRecording(); }; recognition.onend = () => { stopVoiceRecording(); }; } function startVoiceRecording() { if (!recognition) { showToast('Voice recognition not supported in this browser'); return; } // Open Copilot Chat Panel openVCMCopilot(); try { recognition.start(); } catch (e) { // Already started } } function stopVoiceRecording() { if (recognition && isRecording) { recognition.stop(); } const btn = document.getElementById('voice-input-btn'); const visualizer = document.getElementById('voice-visualizer'); if (btn) btn.classList.remove('active'); if (visualizer) visualizer.classList.remove('active'); isRecording = false; } // Attach Voice Button Listener - UPDATED FOR AI VOICE ACTIONS document.addEventListener('DOMContentLoaded', () => { const voiceBtn = document.getElementById('voice-input-btn'); if (voiceBtn) { voiceBtn.addEventListener('click', () => { // Open the AI Voice Actions Panel instead of just recording toggleAIVoicePanel(); }); } // Chat Input Enter Key const chatInput = document.getElementById('vcm-chat-input'); if (chatInput) { chatInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { sendVCMMessage(); } }); } }); // Copilot Panel Functions function openVCMCopilot() { const overlay = document.getElementById('vcm-copilot-overlay'); const panel = document.getElementById('vcm-copilot-panel'); if (!overlay || !panel) return; overlay.classList.remove('hidden'); panel.classList.remove('translate-x-full'); // Delay opacity for smooth fade setTimeout(() => overlay.classList.remove('opacity-0'), 10); // Focus input setTimeout(() => { const input = document.getElementById('vcm-chat-input'); if (input) input.focus(); }, 300); } function closeVCMCopilot() { const overlay = document.getElementById('vcm-copilot-overlay'); const panel = document.getElementById('vcm-copilot-panel'); if (!overlay || !panel) return; panel.classList.add('translate-x-full'); overlay.classList.add('opacity-0'); setTimeout(() => { overlay.classList.add('hidden'); }, 300); stopVoiceRecording(); } function minimizeVCMCopilot() { closeVCMCopilot(); } // Chat Message Logic function sendVCMMessage() { const input = document.getElementById('vcm-chat-input'); const text = input.value.trim(); if (!text) return; // Add User Message addVCMMessage('user', text); input.value = ''; // Show Typing Indicator showVCMTyping(); // Simulate AI Processing setTimeout(() => { hideVCMTyping(); processVCMIntent(text); }, 1000 + Math.random() * 1000); } function addVCMMessage(sender, text, actions = []) { const history = document.getElementById('vcm-chat-history'); if (!history) return; const messageDiv = document.createElement('div'); messageDiv.className = sender === 'user' ? 'vcm-message-user' : 'vcm-message-ai'; let contentHTML = ''; if (sender === 'user') { contentHTML = `
${text}
`; } else { let avatarHTML = `
`; let actionsHTML = ''; if (actions.length > 0) { actionsHTML = `
${actions.map(action => ` `).join('')}
`; } contentHTML = ` ${avatarHTML}
${text}
${actionsHTML}
`; } messageDiv.innerHTML = contentHTML; history.appendChild(messageDiv); // Re-init icons lucide.createIcons(); // Scroll to bottom history.scrollTop = history.scrollHeight; } function showVCMTyping() { const indicator = document.getElementById('vcm-typing-indicator'); if (indicator) indicator.classList.remove('hidden'); } function hideVCMTyping() { const indicator = document.getElementById('vcm-typing-indicator'); if (indicator) indicator.classList.add('hidden'); } // Contextual AI Response Logic function processVCMIntent(text) { const lowerText = text.toLowerCase(); // Pattern Matching for Intent if (lowerText.includes('add provider') || lowerText.includes('new doctor') || lowerText.includes('hire')) { ChatContext.userIntent = 'add-provider'; addVCMMessage('ai', "I can help you add a new provider. Should I create the profile using your standard template (Dr. [Name], Cardiology)?", [ { id: 'create-provider', label: 'Yes, Create Profile', primary: true }, { id: 'open-operator', label: 'Use Full Workflow' } ]); } else if (lowerText.includes('sms') || lowerText.includes('reminder') || lowerText.includes('text')) { addVCMMessage('ai', "I can connect Twilio for SMS reminders. This typically reduces no-shows by 70%. Shall I connect it now?", [ { id: 'enable-sms', label: 'Connect Twilio', primary: true }, { id: 'details', label: 'Show Details' } ]); } else if (lowerText.includes('payment') || lowerText.includes('stripe') || lowerText.includes('billing')) { addVCMMessage('ai', "Your payment processor isn't connected yet. I can set up Stripe for you. Do you want to enable online payments immediately?", [ { id: 'connect-payments', label: 'Setup Stripe', primary: true }, { id: 'test-payments', label: 'Test Transaction' } ]); } else if (lowerText.includes('schedule') || lowerText.includes('availability') || lowerText.includes('hours')) { addVCMMessage('ai', "I can set up availability rules. Would you like to apply the 'Standard 9-5' template again?", [ { id: 'apply-schedule', label: 'Apply Template', primary: true }, { id: 'custom-schedule', label: 'Customize' } ]); } else if (lowerText.includes('status') || lowerText.includes('health') || lowerText.includes('progress')) { const progress = document.getElementById('setup-progress-percentage').textContent; addVCMMessage('ai', `Your setup progress is currently at ${progress}. You have 2 critical alerts regarding Payments and SMS. Would you like me to fix them?`, [ { id: 'auto-fix', label: 'Auto-Fix All', primary: true } ]); } else { // Fallback / General addVCMMessage('ai', "I can help with providers, scheduling, payments, or notifications. What would you like to do?", [ { id: 'add-provider', label: 'Add Provider' }, { id: 'connect-payments', label: 'Setup Payments' }, { id: 'enable-sms', label: 'Enable SMS' } ]); } } // Action Execution from Chat function executeVCMAction(actionId, label) { // Add "Performing action" feedback message addVCMMessage('user', `I want to ${label.toLowerCase()}`); showVCMTyping(); // Simulate processing setTimeout(() => { hideVCMTyping(); // Execute based on action ID switch(actionId) { case 'create-provider': // Update Dashboard (Live Sync) updateDashboardState({ progress: 5, scores: { health: 2 } }); addVCMMessage('ai', "I've created a placeholder provider profile. I've assigned standard permissions. You can edit the specific details in the Providers tab.", [ { id: 'navigate-providers', label: 'Go to Providers', primary: true } ]); break; case 'enable-sms': // Update Alerts & Scores updateDashboardState({ progress: 4, alerts: ['sms'], scores: { automation: 10 } }); addVCMMessage('ai', "Twilio is now connected! SMS reminders are active. I've set the default timing to 24 hours before appointments.", [ { id: 'test-sms', label: 'Send Test SMS' } ]); break; case 'connect-payments': updateDashboardState({ progress: 8, alerts: ['payments'], scores: { revenue: 20 } }); addVCMMessage('ai', "Stripe is connected and online payments are enabled. I've configured the deposit requirement to None for now.", [ { id: 'navigate-payments', label: 'Configure Billing', primary: true } ]); break; case 'auto-fix': updateDashboardState({ progress: 12, alerts: ['sms', 'payments'], scores: { health: 5, automation: 10, revenue: 15 } }); addVCMMessage('ai', "Done! I've fixed both the Payments and SMS alerts. Your system health score has improved. Is there anything else?", [ { id: 'optimize', label: 'Optimize Further' } ]); break; case 'apply-schedule': addVCMMessage('ai', "Schedule template 'Standard 9-5' applied to all active providers. Buffer times set to 10 minutes."); updateDashboardState({ progress: 3 }); break; case 'open-operator': closeVCMCopilot(); setTimeout(() => triggerAIWorkflow('auto-configure'), 300); break; default: addVCMMessage('ai', "Action noted. I've updated the relevant settings in the background."); } // Trigger Toast for main UI feedback showToast(`AI executed: ${label}`); }, 800); } // ========================================= // AI OPERATOR MODE (AOM V1) - WORKFLOW ENGINE // ========================================= const AIContext = { lastScheduleTemplate: 'Standard 9-5', notificationDefaults: ['Email', 'SMS'], currency: 'USD' }; const Workflows = { 'add-provider': { title: 'Add New Provider', steps: [ { id: 'create-profile', title: 'Create Provider Profile', description: 'Generate unique ID and basic record', action: 'createProviderProfile' }, { id: 'assign-permissions', title: 'Assign Default Permissions', description: 'Apply role-based access controls', action: 'assignPermissions' }, { id: 'notify-admin', title: 'Notify Administrators', description: 'Send onboarding email to admin team', action: 'notifyAdmin' } ] }, 'enable-sms': { title: 'Enable SMS Reminders', steps: [ { id: 'check-twilio', title: 'Verify Twilio Connection', description: 'Ping API gateway for status', action: 'checkTwilio' }, { id: 'activate-reminders', title: 'Activate Reminder Engine', description: 'Enable automated SMS logic', action: 'activateReminders' }, { id: 'send-test', title: 'Send Test SMS', description: 'Verify delivery to admin phone', action: 'sendTestSMS' } ], impact: { progress: 4, alerts: ['sms'], scores: { automation: 5 } } }, 'auto-configure': { title: 'Auto-Configure Platform', steps: [ { id: 'audit-system', title: 'Audit System State', description: 'Analyze current configuration gaps', action: 'auditSystem' }, { id: 'fix-payments', title: 'Fix Payment Integration', description: 'Connect Stripe gateway with defaults', action: 'fixPayments' }, { id: 'enable-notifications', title: 'Enable Notifications', description: 'Turn on email and SMS channels', action: 'enableNotifications' }, { id: 'optimize-scheduling', title: 'Optimize Scheduling', description: 'Apply AI-optimized buffer times', action: 'optimizeScheduling' } ], impact: { progress: 20, alerts: ['payments', 'sms'], scores: { health: 5, revenue: 15, automation: 10 } } }, 'setup-onboarding': { title: 'Set Up Provider Onboarding', steps: [ { id: 'create-provider', title: 'Create Provider Profile', description: 'Dr. Sarah Johnson (Cardiology)', action: 'createProviderProfile' }, { id: 'assign-schedule', title: 'Assign Schedule', description: `Apply template: ${AIContext.lastScheduleTemplate}`, action: 'assignSchedule' }, { id: 'enable-reminders', title: 'Enable Patient Reminders', description: `Enable: ${AIContext.notificationDefaults.join(' & ')}`, action: 'enableReminders' }, { id: 'generate-link', title: 'Generate Booking Link', description: 'Create unique provider booking URL', action: 'generateLink' }, { id: 'send-welcome', title: 'Send Welcome Email', description: 'Dispatch credentials and handbook', action: 'sendWelcomeEmail' } ], impact: { progress: 8, alerts: [], scores: { automation: 5, health: 2 } } } }; // Command Parser function parseCommand(input) { const lowerInput = input.toLowerCase().trim(); if (lowerInput.includes('onboarding') || lowerInput.includes('set up new provider')) return 'setup-onboarding'; if (lowerInput.includes('add provider')) return 'add-provider'; if (lowerInput.includes('sms') || lowerInput.includes('reminders')) return 'enable-sms'; if (lowerInput.includes('payment') || lowerInput.includes('billing')) return 'enable-payments'; if (lowerInput.includes('auto') || lowerInput.includes('configure') || lowerInput.includes('optimize')) return 'auto-configure'; return null; } // Trigger workflow from UI function triggerAIWorkflow(workflowKey) { const workflow = Workflows[workflowKey]; if (!workflow) return; openAIOperator(workflow); } // Open AI Operator Panel function openAIOperator(workflow) { const overlay = document.getElementById('ai-operator-overlay'); const panel = document.getElementById('ai-operator-panel'); const content = document.getElementById('ai-operator-content'); const footer = document.getElementById('ai-operator-footer'); // Reset UI overlay.classList.remove('hidden'); panel.classList.remove('translate-x-full'); setTimeout(() => overlay.classList.remove('opacity-0'), 10); // Render Execution Plan renderExecutionPlan(workflow, content, footer); lucide.createIcons(); } function closeAIOperator() { const overlay = document.getElementById('ai-operator-overlay'); const panel = document.getElementById('ai-operator-panel'); panel.classList.add('translate-x-full'); overlay.classList.add('opacity-0'); setTimeout(() => { overlay.classList.add('hidden'); }, 300); } // Render Execution Plan function renderExecutionPlan(workflow, container, footer) { container.innerHTML = `
AI Plan Generated

${workflow.title}

AI will perform the following ${workflow.steps.length} actions to complete this request.

${workflow.steps.map((step, index) => `
${index + 1}
${step.title}
${step.description}
`).join('')}

This will modify system settings. Review the plan above before proceeding.

`; footer.innerHTML = ` `; } // Execute Workflow async function runWorkflow(title) { const workflow = Object.values(Workflows).find(w => w.title === title); if (!workflow) return; const footer = document.getElementById('ai-operator-footer'); const status = document.getElementById('ai-operator-status'); // Update footer to show running state footer.innerHTML = `
Executing...
`; status.textContent = 'Executing workflow...'; // Execute steps sequentially for (let i = 0; i < workflow.steps.length; i++) { await executeStep(i, workflow.steps[i]); } // Workflow Complete completeWorkflow(workflow); } async function executeStep(index, step) { const stepEl = document.getElementById(`step-${index}`); const progressBar = stepEl.querySelector('.step-progress-bar'); const statusIcon = stepEl.querySelector('.step-status-icon'); // Mark as Active stepEl.classList.add('active', 'running'); statusIcon.innerHTML = ''; lucide.createIcons(); // Simulate Progress progressBar.style.width = '30%'; await wait(600); progressBar.style.width = '70%'; await wait(600); progressBar.style.width = '100%'; // Mark as Completed await wait(400); stepEl.classList.remove('active', 'running'); stepEl.classList.add('completed'); statusIcon.innerHTML = ''; lucide.createIcons(); } function completeWorkflow(workflow) { const status = document.getElementById('ai-operator-status'); const footer = document.getElementById('ai-operator-footer'); const content = document.getElementById('ai-operator-content'); status.textContent = 'Complete'; status.classList.add('text-green-600'); // Update Content to Success State content.innerHTML = `

Workflow Complete

All ${workflow.steps.length} steps executed successfully.

Updates Applied

${workflow.impact ? ` ${workflow.impact.progress ? `
Setup Progress+${workflow.impact.progress}%
` : ''} ${workflow.impact.scores ? Object.entries(workflow.impact.scores).map(([k, v]) => `
${k} Score+${v}%
`).join('') : ''} ` : '
System configuration updated
'}
`; footer.innerHTML = ` `; lucide.createIcons(); // Update Main Dashboard UI (No Reload) if (workflow.impact) updateDashboardState(workflow.impact); showToast(`${workflow.title} completed successfully`); } function updateDashboardState(impact) { // Update Progress Bar if (impact.progress) { const currentBar = document.getElementById('setup-progress-bar'); const currentText = document.getElementById('setup-progress-percentage'); if (currentBar && currentText) { let currentWidth = parseInt(currentBar.style.width); let newWidth = Math.min(100, currentWidth + impact.progress); currentBar.style.width = `${newWidth}%`; currentText.textContent = `${newWidth}%`; } } // Update System Scores if (impact.scores) { Object.entries(impact.scores).forEach(([key, value]) => { const scoreEl = document.getElementById(`${key}-score`); const barEl = document.getElementById(`${key}-bar`); if (scoreEl && barEl) { let currentScore = parseInt(scoreEl.textContent); let newScore = Math.min(100, currentScore + value); scoreEl.textContent = `${newScore}%`; barEl.style.width = `${newScore}%`; } }); } // Update Alerts if (impact.alerts && impact.alerts.length > 0) { impact.alerts.forEach(alertKey => { const alertEl = document.querySelector(`.alert-item[data-alert="${alertKey}"]`); if (alertEl) { // Simulate fixing alert by changing style alertEl.style.opacity = '0.5'; alertEl.querySelector('.status-amber-700').textContent = 'Resolved'; alertEl.querySelector('.status-amber-100').classList.replace('status-amber-100', 'status-green-100'); alertEl.querySelector('.status-amber-700').classList.replace('status-amber-700', 'status-green-700'); } }); } } function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Enhanced Command Bar Listener document.addEventListener('DOMContentLoaded', () => { const commandInput = document.getElementById('ai-command-input'); if (commandInput) { commandInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { const workflowKey = parseCommand(commandInput.value); if (workflowKey) { commandInput.value = ''; triggerAIWorkflow(workflowKey); } else { showToast('Command not recognized. Try "Add provider" or "Auto-configure"'); } } }); } }); // App State const appState = { currentView: 'dashboard', settingsSidebarExpanded: true, aiPanelExpanded: false, sidebarSectionsState: { organization: true, // Expanded by default (contains dashboard) scheduling: false, payments: false, communication: false, automation: false, security: false }, progress: { total: 15, completed: 11 }, checklist: [ { id: 'org-details', completed: true, group: 'Organization Setup', description: 'Add organization details' }, { id: 'upload-logo', completed: false, group: 'Organization Setup', description: 'Upload logo & branding' }, { id: 'add-locations', completed: true, group: 'Organization Setup', description: 'Add clinic locations' }, { id: 'configure-providers', completed: true, group: 'Scheduling Setup', description: 'Configure providers' }, { id: 'set-availability', completed: false, group: 'Scheduling Setup', description: 'Set availability rules' }, { id: 'appointment-types', completed: true, group: 'Scheduling Setup', description: 'Create appointment types' }, { id: 'connect-payments', completed: false, group: 'Payments Setup', description: 'Connect payment processor' }, { id: 'billing-setup', completed: true, group: 'Payments Setup', description: 'Set up billing & invoicing' }, { id: 'insurance-setup', completed: false, group: 'Payments Setup', description: 'Configure insurance' }, { id: 'enable-notifications', completed: true, group: 'Communication Setup', description: 'Enable notifications' }, { id: 'setup-sms', completed: false, group: 'Communication Setup', description: 'Setup SMS & Email' }, { id: 'create-templates', completed: true, group: 'Communication Setup', description: 'Create communication templates' }, { id: 'compliance-setup', completed: true, group: 'Compliance Setup', description: 'Configure compliance settings' }, { id: 'security-audit', completed: true, group: 'Compliance Setup', description: 'Complete security audit' }, { id: 'ai-optimization', completed: false, group: 'AI Optimization', description: 'Enable AI scheduling' } ], settings: { organization: { name: 'MediCare Clinic', locations: 3, providers: 12 }, scheduling: { appointmentTypes: 8, weeklyHours: 45 }, payments: { processorConnected: false, insuranceNetworks: 2 }, notifications: { templates: 5, smsEnabled: true } } }; // Calculate progress percentage function calculateProgress() { const completed = appState.checklist.filter(item => item.completed).length; const total = appState.checklist.length; return Math.round((completed / total) * 100); } // Update progress display function updateProgressDisplay() { const percentage = calculateProgress(); const progressBar = document.querySelector('.progress-bar'); const progressText = document.querySelector('.text-2xl.font-bold.text-gray-900'); if (progressBar) { progressBar.style.background = `linear-gradient(90deg, #089e97 0%, #089e97 ${percentage}%, #e5e7eb ${percentage}%, #e5e7eb 100%)`; } if (progressText) { progressText.textContent = `${percentage}%`; } } // Toggle checklist item function toggleChecklistItem(itemId) { const item = appState.checklist.find(item => item.id === itemId); if (item) { item.completed = !item.completed; // Update UI const checklistItem = document.querySelector(`[data-item="${itemId}"]`); if (checklistItem) { checklistItem.classList.toggle('completed'); const checkIcon = checklistItem.querySelector('.check-circle i'); if (checkIcon) { checkIcon.classList.toggle('hidden'); } } updateProgressDisplay(); showToast('Checklist updated'); } } // ========================================= // GIS V1: ACTION FEEDBACK SYSTEM // ========================================= // Apply immediate loading state to any button function applyActionFeedback(buttonElement) { if (!buttonElement) return; // Store original text/content const originalHTML = buttonElement.innerHTML; const originalWidth = buttonElement.offsetWidth; // Apply loading state buttonElement.classList.add('btn-loading'); buttonElement.style.width = originalWidth + 'px'; // Prevent width collapse // Remove loading state after short delay (simulating action processing) setTimeout(() => { buttonElement.classList.remove('btn-loading'); buttonElement.style.width = ''; }, 600); } // Show toast notification function showToast(message) { const toast = document.getElementById('toast'); if (toast) { toast.querySelector('span').textContent = message; toast.classList.remove('hidden'); // Fade out smoothly setTimeout(() => { toast.style.opacity = '0'; toast.style.transition = 'opacity 0.3s ease'; setTimeout(() => { toast.classList.add('hidden'); toast.style.opacity = '1'; }, 300); }, 2500); } } // Show modal function showModal(title, content) { const modalOverlay = document.getElementById('modal-overlay'); const modalContent = document.getElementById('modal-content'); const modalHTML = `

${title}

${content}
`; modalContent.innerHTML = modalHTML; modalOverlay.classList.remove('hidden'); // Re-initialize icons lucide.createIcons(); // Add event listeners document.getElementById('close-modal').addEventListener('click', hideModal); document.getElementById('cancel-modal').addEventListener('click', hideModal); document.getElementById('save-modal').addEventListener('click', () => { showToast('Settings saved successfully'); hideModal(); }); } // Hide modal function hideModal() { document.getElementById('modal-overlay').classList.add('hidden'); } // Enterprise Configuration System - Settings Detail Page V3 const configSystem = { availability: { title: 'Availability Rules', description: 'Configure schedules, time blocks, and recurring availability patterns for optimal patient flow.', status: 'needs-setup', statusText: 'Needs Setup', modules: [ { id: 'working-hours', title: 'Working Hours', description: 'Set standard operating hours for each day of the week', icon: 'clock', fields: [ { type: 'toggle', id: 'monday-enabled', label: 'Monday', default: true }, { type: 'time-range', id: 'monday-hours', label: 'Hours', start: '09:00', end: '17:00' }, { type: 'toggle', id: 'tuesday-enabled', label: 'Tuesday', default: true }, { type: 'time-range', id: 'tuesday-hours', label: 'Hours', start: '09:00', end: '17:00' }, { type: 'toggle', id: 'wednesday-enabled', label: 'Wednesday', default: true }, { type: 'time-range', id: 'wednesday-hours', label: 'Hours', start: '09:00', end: '17:00' }, { type: 'toggle', id: 'thursday-enabled', label: 'Thursday', default: true }, { type: 'time-range', id: 'thursday-hours', label: 'Hours', start: '09:00', end: '17:00' }, { type: 'toggle', id: 'friday-enabled', label: 'Friday', default: true }, { type: 'time-range', id: 'friday-hours', label: 'Hours', start: '09:00', end: '17:00' }, { type: 'toggle', id: 'saturday-enabled', label: 'Saturday', default: false }, { type: 'toggle', id: 'sunday-enabled', label: 'Sunday', default: false } ], warning: 'Short lunch breaks may cause provider fatigue. Consider 60-minute midday breaks.' }, { id: 'time-blocks', title: 'Time Blocks & Buffers', description: 'Configure appointment duration defaults and buffer times between visits', icon: 'hourglass', fields: [ { type: 'select', id: 'default-duration', label: 'Default Appointment Duration', options: ['15 min', '30 min', '45 min', '60 min'], default: '30 min' }, { type: 'select', id: 'buffer-time', label: 'Buffer Between Appointments', options: ['0 min', '5 min', '10 min', '15 min', '30 min'], default: '10 min' }, { type: 'select', id: 'lunch-buffer', label: 'Lunch Break Duration', options: ['30 min', '45 min', '60 min', '90 min'], default: '60 min' }, { type: 'toggle', id: 'enforce-buffers', label: 'Strictly enforce buffer times', default: true } ], suggestion: 'Adding 15-minute buffers reduces scheduling conflicts by 45%.' }, { id: 'blackout-dates', title: 'Blackout Dates', description: 'Block specific dates for holidays, training, or facility closures', icon: 'calendar-x', type: 'blackout-list', fields: [ { type: 'date-list', id: 'blackout-dates', label: 'Blocked Dates', items: ['2024-12-25', '2024-12-26', '2025-01-01'] } ], action: { label: 'Add Blackout Date', icon: 'plus' } }, { id: 'recurring-rules', title: 'Recurring Availability Rules', description: 'Set up templates for provider schedules that repeat weekly', icon: 'repeat', fields: [ { type: 'toggle', id: 'auto-optimize', label: 'AI Auto-Optimization', description: 'Let AI adjust availability based on demand patterns', default: false }, { type: 'toggle', id: 'dynamic-scheduling', label: 'Dynamic Scheduling', description: 'Automatically extend hours during high-demand periods', default: false }, { type: 'number', id: 'max-daily-appointments', label: 'Max Daily Appointments per Provider', default: 16, min: 1, max: 40 } ], aiInsight: 'Enable AI Auto-Optimization to increase booking capacity by up to 23%.' } ], aiRecommendations: [ { id: 'buffer-optimization', title: 'Increase Buffer Times', description: 'Current 5-min buffers causing 12% overlap rate', impact: '+45% fewer conflicts', action: 'Apply' }, { id: 'extend-friday', title: 'Extend Friday Hours', description: 'High demand detected on Friday afternoons', impact: '+8 appointments/week', action: 'Apply' }, { id: 'add-saturday', title: 'Enable Saturday Mornings', description: 'Patient survey shows 34% want weekend slots', impact: '+15% patient satisfaction', action: 'Review' } ], tests: [ { id: 'booking-simulation', name: 'Test Booking Flow', description: 'Simulate patient booking experience' }, { id: 'conflict-check', name: 'Check Conflicts', description: 'Identify scheduling overlaps' }, { id: 'capacity-analysis', name: 'Capacity Analysis', description: 'Analyze max daily booking potential' } ] }, providers: { title: 'Provider Settings', description: 'Configure provider profiles, permissions, scheduling preferences, and workflow automation.', status: 'optimized', statusText: 'Optimized', modules: [ { id: 'provider-profiles', title: 'Provider Profiles', description: 'Manage provider information and default settings', icon: 'user-check', fields: [ { type: 'select', id: 'default-role', label: 'Default Provider Role', options: ['Primary Care Physician', 'Specialist', 'Nurse Practitioner', 'Physician Assistant'], default: 'Primary Care Physician' }, { type: 'toggle', id: 'auto-accept', label: 'Auto-accept routine appointments', default: true }, { type: 'toggle', id: 'require-approval', label: 'Require approval for new patient appointments', default: false } ] }, { id: 'permissions', title: 'Permissions & Access', description: 'Control what providers can see and modify', icon: 'shield', fields: [ { type: 'select', id: 'calendar-access', label: 'Calendar Access Level', options: ['Own Only', 'Department', 'All Providers'], default: 'Department' }, { type: 'select', id: 'patient-access', label: 'Patient Record Access', options: ['Assigned Only', 'All Patients', 'Read-Only'], default: 'Assigned Only' }, { type: 'toggle', id: 'can-modify-schedule', label: 'Can modify own schedule', default: true }, { type: 'toggle', id: 'can-view-revenue', label: 'Can view revenue reports', default: false } ] }, { id: 'notifications', title: 'Notification Preferences', description: 'Configure how providers receive alerts and updates', icon: 'bell', fields: [ { type: 'toggle', id: 'new-booking-sms', label: 'SMS for new bookings', default: true }, { type: 'toggle', id: 'cancellation-email', label: 'Email for cancellations', default: true }, { type: 'toggle', id: 'reminder-push', label: 'Push notifications for reminders', default: false }, { type: 'select', id: 'digest-frequency', label: 'Daily Digest Frequency', options: ['Never', 'Morning', 'Evening', 'Both'], default: 'Morning' } ] } ], aiRecommendations: [ { id: 'auto-scheduling', title: 'Enable Auto-Scheduling', description: 'Let AI optimize provider calendars automatically', impact: '+35% efficiency gain', action: 'Enable' }, { id: 'conflict-prevention', title: 'Conflict Prevention', description: 'Block double-booking and enforce buffer times', impact: 'Zero double bookings', action: 'Apply' } ], tests: [ { id: 'permission-check', name: 'Test Permissions', description: 'Verify access controls work correctly' }, { id: 'notification-test', name: 'Test Notifications', description: 'Send test alert to providers' } ] }, 'payment-processors': { title: 'Payment Processors', description: 'Connect and manage payment gateways for seamless revenue collection.', status: 'at-risk', statusText: 'At Risk', modules: [ { id: 'processor-connection', title: 'Payment Gateway', description: 'Connect your preferred payment processor', icon: 'credit-card', fields: [ { type: 'select', id: 'processor', label: 'Payment Processor', options: ['Stripe', 'PayPal', 'Square', 'Authorize.net'], default: '' }, { type: 'text', id: 'api-key', label: 'API Key', placeholder: 'sk_live_...', secure: true }, { type: 'text', id: 'webhook-url', label: 'Webhook URL', value: 'https://api.verifymc.com/webhooks/payments', readonly: true }, { type: 'toggle', id: 'test-mode', label: 'Test Mode (Sandbox)', default: true } ], warning: 'No payment processor connected. Revenue collection is currently disabled.' }, { id: 'payment-settings', title: 'Payment Settings', description: 'Configure payment behavior and options', icon: 'settings', fields: [ { type: 'toggle', id: 'online-payments', label: 'Accept online payments', default: false }, { type: 'toggle', id: 'auto-charge', label: 'Auto-charge on appointment completion', default: false }, { type: 'select', id: 'deposit-requirement', label: 'Require Deposit', options: ['None', '25%', '50%', 'Fixed Amount'], default: 'None' }, { type: 'number', id: 'fixed-deposit-amount', label: 'Fixed Deposit Amount ($)', default: 50, showIf: { field: 'deposit-requirement', value: 'Fixed Amount' } } ] }, { id: 'payouts', title: 'Payout Configuration', description: 'Manage when and how you receive funds', icon: 'dollar-sign', fields: [ { type: 'select', id: 'payout-schedule', label: 'Payout Schedule', options: ['Daily', 'Weekly', 'Monthly', 'Manual'], default: 'Weekly' }, { type: 'select', id: 'payout-day', label: 'Payout Day (if weekly)', options: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'], default: 'Monday' }, { type: 'toggle', id: 'instant-payout', label: 'Enable instant payouts (2% fee)', default: false } ] } ], aiRecommendations: [ { id: 'connect-stripe', title: 'Connect Stripe Immediately', description: 'Payment processor missing - revenue opportunity loss: $12,400/month', impact: 'Restore revenue flow', action: 'Connect' }, { id: 'enable-deposits', title: 'Enable 25% Deposits', description: 'Reduce no-shows and secure commitment', impact: '+40% show rate', action: 'Apply' } ], tests: [ { id: 'payment-test', name: 'Test Transaction', description: 'Process $1 test charge' }, { id: 'webhook-test', name: 'Test Webhooks', description: 'Verify payment notifications' }, { id: 'refund-test', name: 'Test Refund Flow', description: 'Process test refund' } ] }, notifications: { title: 'Notifications & Communication', description: 'Configure automated messages, reminders, and patient communication workflows.', status: 'optimized', statusText: 'Optimized', modules: [ { id: 'reminder-settings', title: 'Appointment Reminders', description: 'Automated reminders to reduce no-shows', icon: 'bell', fields: [ { type: 'toggle', id: 'sms-reminders', label: 'SMS Reminders', default: true }, { type: 'toggle', id: 'email-reminders', label: 'Email Reminders', default: true }, { type: 'select', id: 'reminder-timing', label: 'Reminder Timing', options: ['24 hours', '48 hours', '1 hour', '15 minutes'], default: '24 hours' }, { type: 'number', id: 'reminder-count', label: 'Number of reminders', default: 2, min: 0, max: 5 } ], suggestion: 'Multiple reminders (24h + 1h) reduce no-shows by 70%.' }, { id: 'templates', title: 'Message Templates', description: 'Customize communication content', icon: 'file-text', fields: [ { type: 'textarea', id: 'confirmation-template', label: 'Confirmation Message', default: 'Your appointment with {{provider}} is confirmed for {{date}} at {{time}}.' }, { type: 'textarea', id: 'reminder-template', label: 'Reminder Message', default: 'Reminder: You have an appointment tomorrow at {{time}} with {{provider}}.' }, { type: 'textarea', id: 'cancellation-template', label: 'Cancellation Message', default: 'Your appointment has been cancelled. Reply to reschedule or call {{phone}}.' } ] }, { id: 'channels', title: 'Communication Channels', description: 'Manage SMS, email, and push notification settings', icon: 'message-square', fields: [ { type: 'toggle', id: 'sms-enabled', label: 'SMS Enabled', default: true }, { type: 'text', id: 'sms-from', label: 'SMS From Number', value: '+1 (555) 123-4567' }, { type: 'toggle', id: 'email-enabled', label: 'Email Enabled', default: true }, { type: 'text', id: 'email-from', label: 'Email From Name', value: 'MediCare Clinic' } ] } ], aiRecommendations: [ { id: 'add-followup', title: 'Add Follow-up Messages', description: 'Send post-appointment care instructions automatically', impact: '+25% patient satisfaction', action: 'Enable' }, { id: 'personalize', title: 'Personalize Message Timing', description: 'AI adjusts reminder timing based on patient behavior', impact: '+15% open rate', action: 'Apply' } ], tests: [ { id: 'send-test-sms', name: 'Send Test SMS', description: 'Verify SMS delivery' }, { id: 'send-test-email', name: 'Send Test Email', description: 'Verify email delivery' }, { id: 'spam-check', name: 'Spam Score Check', description: 'Check message deliverability' } ] }, insurance: { title: 'Insurance Configuration', description: 'Manage payer networks, eligibility verification, and claims processing.', status: 'needs-setup', statusText: 'Needs Setup', modules: [ { id: 'payer-networks', title: 'Payer Networks', description: 'Configure accepted insurance providers', icon: 'shield', fields: [ { type: 'multiselect', id: 'insurance-providers', label: 'Accepted Insurance', options: ['Aetna', 'Blue Cross', 'Cigna', 'UnitedHealthcare', 'Medicare', 'Medicaid'], default: [] }, { type: 'toggle', id: 'verify-eligibility', label: 'Auto-verify eligibility', default: false }, { type: 'toggle', id: 'out-of-network', label: 'Accept out-of-network', default: true } ] }, { id: 'claims', title: 'Claims Processing', description: 'Configure automated claims submission', icon: 'file-text', fields: [ { type: 'toggle', id: 'auto-submit', label: 'Auto-submit claims', default: false }, { type: 'select', id: 'submission-timing', label: 'Submission Timing', options: ['Immediately', 'Daily Batch', 'Weekly'], default: 'Daily Batch' }, { type: 'toggle', id: 'denial-management', label: 'AI Denial Management', description: 'Automatically handle claim denials', default: false } ] } ], aiRecommendations: [ { id: 'enable-eligibility', title: 'Enable Real-time Eligibility', description: 'Check insurance before every appointment', impact: '-90% claim denials', action: 'Enable' }, { id: 'add-payers', title: 'Add Top 5 Local Payers', description: 'Expand network to capture 40% more patients', impact: '+40% patient base', action: 'Review' } ], tests: [ { id: 'eligibility-test', name: 'Test Eligibility Check', description: 'Verify insurance lookup' }, { id: 'claim-test', name: 'Submit Test Claim', description: 'Process test insurance claim' } ] } }; // Page header templates for different sections const pageHeaders = { providers: { title: 'Providers Settings', description: 'Configure provider-related configuration, profile behavior, permissions, scheduling defaults, and workflow preferences.', breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: 'Providers', action: () => navigateToSection('providers'), clickable: false } ], actions: ['help', 'ai', 'save'] }, 'appointment-types': { title: 'Appointment Types', description: 'Define visit categories, durations, color coding, and workflow behavior used across scheduling, booking links, and patient intake flows.', breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: 'Appointment Types', action: null, clickable: false } ], actions: ['help', 'ai', 'save'] }, organization: { title: 'Organization Settings', description: 'Manage your clinic details, branding, locations, and organizational structure.', breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: 'Organization', action: null, clickable: false } ], actions: ['help', 'ai', 'save'] }, locations: { title: 'Location Management', description: 'Configure clinic locations, operating hours, room assignments, and facility settings.', breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: 'Locations', action: null, clickable: false } ], actions: ['help', 'ai', 'save'] }, users: { title: 'Users & Permissions', description: 'Manage staff accounts, role-based access controls, and security permissions.', breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: 'Users & Permissions', action: null, clickable: false } ], actions: ['help', 'ai', 'save'] }, availability: { title: 'Availability Rules', description: 'Set provider schedules, time blocks, blackouts, and recurring availability patterns for optimal patient flow.', breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: 'Availability', action: null, clickable: false } ], actions: ['help', 'ai', 'save'], status: 'not-configured', statusText: 'Not Configured', secondaryActions: ['test', 'preview'] }, 'payment-processors': { title: 'Payment Processors', description: 'Connect and manage Stripe, PayPal, or other payment gateways for revenue collection.', breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: 'Payment Processors', action: null, clickable: false } ], actions: ['help', 'ai', 'save'] }, billing: { title: 'Billing & Invoicing', description: 'Configure invoice templates, payment terms, auto-billing, and financial workflows.', breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: 'Billing & Invoicing', action: null, clickable: false } ], actions: ['help', 'ai', 'save'] }, insurance: { title: 'Insurance Configuration', description: 'Manage payer networks, eligibility verification, and claims processing settings.', breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: 'Insurance', action: null, clickable: false } ], actions: ['help', 'ai', 'save'] }, notifications: { title: 'Notifications & Communication', description: 'Configure automated patient communications, reminders, and messaging workflows.', breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: 'Notifications', action: null, clickable: false } ], actions: ['help', 'ai', 'save'] } }; // Unsaved changes tracker let hasUnsavedChanges = false; let formChangeListeners = []; function markUnsaved() { hasUnsavedChanges = true; const badge = document.getElementById('unsaved-badge'); const saveBtn = document.getElementById('header-save-btn'); if (badge) badge.classList.remove('hidden'); if (saveBtn) { saveBtn.classList.add('ring-2', 'ring-[#089e97]', 'ring-offset-2'); saveBtn.innerHTML = `Save Changes`; lucide.createIcons(); } } // Sidebar Collapsible Sections V2 function toggleSidebarSection(sectionId, forceExpand = false) { const section = document.querySelector(`.sidebar-section[data-section="${sectionId}"]`); const content = section?.querySelector('.sidebar-section-content'); const icon = section?.querySelector('.sidebar-section-icon'); if (!section || !content || !icon) return; const isExpanded = content.classList.contains('expanded') || appState.sidebarSectionsState[sectionId]; if (forceExpand || !isExpanded) { // Expand this section, collapse others document.querySelectorAll('.sidebar-section-content').forEach(el => { el.classList.remove('expanded'); el.style.display = 'none'; }); document.querySelectorAll('.sidebar-section-icon').forEach(el => { el.classList.remove('expanded'); }); // Expand current section content.classList.add('expanded'); content.style.display = 'block'; icon.classList.add('expanded'); appState.sidebarSectionsState[sectionId] = true; // Update other sections to false Object.keys(appState.sidebarSectionsState).forEach(key => { if (key !== sectionId) { appState.sidebarSectionsState[key] = false; } }); } else { // Collapse this section content.classList.remove('expanded'); content.style.display = 'none'; icon.classList.remove('expanded'); appState.sidebarSectionsState[sectionId] = false; } // Save state to localStorage try { localStorage.setItem('sidebarSectionsState', JSON.stringify(appState.sidebarSectionsState)); } catch (e) { console.log('Could not save sidebar state to localStorage'); } } function initializeSidebarSections() { // Load saved state from localStorage try { const savedState = localStorage.getItem('sidebarSectionsState'); if (savedState) { const parsedState = JSON.parse(savedState); Object.keys(parsedState).forEach(key => { if (appState.sidebarSectionsState.hasOwnProperty(key)) { appState.sidebarSectionsState[key] = parsedState[key]; } }); } } catch (e) { console.log('Could not load sidebar state from localStorage'); } // Apply the state to the UI Object.keys(appState.sidebarSectionsState).forEach(sectionId => { if (appState.sidebarSectionsState[sectionId]) { const content = document.querySelector(`.sidebar-section-content[data-section="${sectionId}"]`); const icon = document.querySelector(`.sidebar-section-icon[data-section="${sectionId}"]`); if (content) { content.classList.add('expanded'); content.style.display = 'block'; } if (icon) { icon.classList.add('expanded'); } } }); // Add click handlers to section headers document.querySelectorAll('.sidebar-section-header').forEach(header => { header.addEventListener('click', (e) => { const sectionId = header.dataset.section; if (sectionId) { toggleSidebarSection(sectionId); } }); }); } function expandSectionForView(sectionId) { // Map view to section const viewToSectionMap = { 'dashboard': 'organization', 'organization': 'organization', 'locations': 'organization', 'providers': 'organization', 'users': 'organization', 'availability': 'scheduling', 'appointment-types': 'scheduling', 'buffer-time': 'scheduling', 'booking-rules': 'scheduling', 'payment-processors': 'payments', 'billing': 'payments', 'insurance': 'payments', 'notifications': 'communication', 'sms': 'communication', 'templates': 'communication', 'ai-scheduling': 'automation', 'workflows': 'automation', 'reminders': 'automation', 'security': 'security', 'compliance': 'security', 'audit': 'security' }; const targetSection = viewToSectionMap[sectionId]; if (targetSection) { toggleSidebarSection(targetSection, true); } } // Module Dependency System (MDS V1) const moduleData = { organization: { title: "Organization System", description: "Manage clinic details, locations, providers, and organizational structure", status: "optimized", completion: 85, missingDependencies: [ { id: "logo-branding", title: "Logo & Branding", subtitle: "Upload clinic logo and configure brand colors", icon: "image", action: () => navigateToSection('organization'), actionText: "Configure" } ], recommendedOptimizations: [ { id: "advanced-reporting", title: "Enable Advanced Reporting", subtitle: "Get detailed insights on provider performance", icon: "bar-chart-3", action: () => showToast('Advanced reporting enabled'), actionText: "Enable" }, { id: "provider-roles", title: "Define Provider Roles", subtitle: "Create custom roles for different provider types", icon: "users", action: () => navigateToSection('users'), actionText: "Setup" } ], configuredItems: [ { id: "clinic-details", title: "Clinic Details", subtitle: "Name, contact info, and tax ID configured", icon: "building" }, { id: "locations", title: "Clinic Locations", subtitle: "3 locations with addresses and hours", icon: "map-pin" }, { id: "providers", title: "Provider Directory", subtitle: "12 providers with profiles", icon: "user-check" } ] }, scheduling: { title: "Scheduling System", description: "Configure appointment types, availability rules, and booking workflows", status: "in-progress", completion: 65, missingDependencies: [ { id: "availability-rules", title: "Availability Rules", subtitle: "No availability rules configured for providers", icon: "clock", action: () => navigateToSection('availability'), actionText: "Fix" }, { id: "buffer-time", title: "Buffer Time", subtitle: "No buffer time set between appointments", icon: "shield", action: () => navigateToSection('buffer-time'), actionText: "Configure" } ], recommendedOptimizations: [ { id: "sms-reminders", title: "Enable SMS Reminders", subtitle: "Reduce no-shows by 70% with automated SMS", icon: "message-square", action: () => navigateToSection('sms'), actionText: "Enable" }, { id: "booking-rules", title: "Optimize Booking Rules", subtitle: "Set smart rules for patient booking limits", icon: "list-checks", action: () => navigateToSection('booking-rules'), actionText: "Optimize" } ], configuredItems: [ { id: "appointment-types", title: "Appointment Types", subtitle: "5 appointment types with durations", icon: "calendar" }, { id: "calendar-sync", title: "Calendar Sync", subtitle: "Google Calendar integration active", icon: "refresh-cw" } ] }, payments: { title: "Payments System", description: "Configure payment processors, billing, and insurance integration", status: "not-configured", completion: 25, missingDependencies: [ { id: "payment-processor", title: "Payment Processor", subtitle: "No payment processor connected", icon: "credit-card", action: () => navigateToSection('payment-processors'), actionText: "Setup" }, { id: "insurance-setup", title: "Insurance Setup", subtitle: "Insurance networks not configured", icon: "shield", action: () => navigateToSection('insurance'), actionText: "Configure" } ], recommendedOptimizations: [ { id: "auto-invoicing", title: "Enable Auto-Invoicing", subtitle: "Automatically generate and send invoices", icon: "file-text", action: () => showToast('Auto-invoicing enabled'), actionText: "Enable" }, { id: "payment-plans", title: "Set Up Payment Plans", subtitle: "Allow patients to pay in installments", icon: "dollar-sign", action: () => showToast('Payment plans configured'), actionText: "Setup" } ], configuredItems: [ { id: "billing-setup", title: "Billing Setup", subtitle: "Basic billing configuration complete", icon: "receipt" } ] }, notifications: { title: "Notifications System", description: "Configure automated communications, reminders, and messaging workflows", status: "optimized", completion: 92, missingDependencies: [], recommendedOptimizations: [ { id: "personalized-timing", title: "Personalized Timing", subtitle: "AI adjusts reminder timing per patient", icon: "zap", action: () => showToast('Personalized timing enabled'), actionText: "Apply" } ], configuredItems: [ { id: "sms-templates", title: "SMS Templates", subtitle: "5 SMS templates for different scenarios", icon: "message-square" }, { id: "email-templates", title: "Email Templates", subtitle: "8 email templates configured", icon: "mail" }, { id: "appointment-reminders", title: "Appointment Reminders", subtitle: "24h & 1h reminders active", icon: "bell" }, { id: "automated-followups", title: "Automated Follow-ups", subtitle: "Post-appointment follow-ups enabled", icon: "repeat" } ] }, security: { title: "Security & Compliance", description: "Configure HIPAA compliance, security settings, and audit logging", status: "in-progress", completion: 78, missingDependencies: [ { id: "audit-logs", title: "Audit Logs", subtitle: "Full audit logging not enabled", icon: "history", action: () => navigateToSection('audit'), actionText: "Enable" } ], recommendedOptimizations: [ { id: "two-factor-auth", title: "Enable Two-Factor Auth", subtitle: "Add extra security layer for all users", icon: "shield", action: () => showToast('2FA enabled'), actionText: "Enable" }, { id: "data-retention", title: "Data Retention Policy", subtitle: "Set automated data cleanup rules", icon: "trash-2", action: () => navigateToSection('compliance'), actionText: "Setup" } ], configuredItems: [ { id: "hipaa-compliance", title: "HIPAA Compliance", subtitle: "Basic HIPAA settings configured", icon: "clipboard-check" }, { id: "user-permissions", title: "User Permissions", subtitle: "Role-based access control active", icon: "lock" }, { id: "data-encryption", title: "Data Encryption", subtitle: "End-to-end encryption enabled", icon: "key" } ] } }; function openModulePanel(moduleId) { const module = moduleData[moduleId]; if (!module) return; // Update panel content document.getElementById('module-panel-title').textContent = module.title; document.getElementById('module-description').textContent = module.description; document.getElementById('module-completion').textContent = module.completion + '%'; document.getElementById('module-progress-bar').style.width = module.completion + '%'; // Update status badge const statusBadge = document.getElementById('module-status-badge'); statusBadge.className = 'status-badge'; statusBadge.textContent = module.status === 'optimized' ? 'Optimized' : module.status === 'in-progress' ? 'In Progress' : 'Not Configured'; statusBadge.classList.add(module.status === 'optimized' ? 'status-optimized' : module.status === 'in-progress' ? 'status-in-progress' : 'status-not-configured'); // Render missing dependencies const missingContainer = document.getElementById('missing-dependencies'); if (module.missingDependencies.length > 0) { missingContainer.innerHTML = module.missingDependencies.map(item => `
${item.title}
${item.subtitle}
`).join(''); } else { missingContainer.innerHTML = `

No missing dependencies! All critical items are configured.

`; } // Render recommended optimizations const recommendationsContainer = document.getElementById('recommended-optimizations'); if (module.recommendedOptimizations.length > 0) { recommendationsContainer.innerHTML = module.recommendedOptimizations.map(item => ` `).join(''); } else { recommendationsContainer.innerHTML = `

No optimizations recommended at this time.

`; } // Render configured items const configuredContainer = document.getElementById('configured-items'); if (module.configuredItems.length > 0) { configuredContainer.innerHTML = module.configuredItems.map(item => `
${item.title}
${item.subtitle}
`).join(''); } else { configuredContainer.innerHTML = `

No items configured yet.

`; } // Show panel document.getElementById('module-panel-overlay').classList.add('active'); document.getElementById('module-panel').classList.add('active'); // Reinitialize icons setTimeout(() => lucide.createIcons(), 100); } function closeModulePanel() { document.getElementById('module-panel-overlay').classList.remove('active'); document.getElementById('module-panel').classList.remove('active'); } function fixAllModuleIssues() { showToast('Fixing all module issues...'); // In a real app, this would trigger automatic fixes setTimeout(() => { showToast('All issues fixed successfully!'); closeModulePanel(); }, 1500); } function autoConfigureModule() { showToast('AI is auto-configuring the module...'); // In a real app, this would use AI to configure optimal settings setTimeout(() => { showToast('Module auto-configured with AI recommendations!'); }, 2000); } function testModuleFlow() { showToast('Testing module flow...'); // In a real app, this would run integration tests setTimeout(() => { showToast('Module flow test completed successfully!'); }, 1500); } function autoApplyOptimization(optimizationId) { showToast(`AI is applying optimization: ${optimizationId}`); // In a real app, this would apply the optimization automatically setTimeout(() => { showToast('Optimization applied successfully!'); }, 1000); } function clearUnsaved() { hasUnsavedChanges = false; const badge = document.getElementById('unsaved-badge'); const saveBtn = document.getElementById('header-save-btn'); if (badge) badge.classList.add('hidden'); if (saveBtn) { saveBtn.classList.remove('ring-2', 'ring-[#089e97]', 'ring-offset-2'); } } function setupFormChangeDetection() { // Remove old listeners formChangeListeners.forEach(({ element, handler }) => { element.removeEventListener('change', handler); element.removeEventListener('input', handler); }); formChangeListeners = []; // Add new listeners const inputs = document.querySelectorAll('#settings-form input, #settings-form select, #settings-form textarea'); inputs.forEach(input => { const handler = () => markUnsaved(); input.addEventListener('change', handler); input.addEventListener('input', handler); formChangeListeners.push({ element: input, handler }); }); } function renderPremiumHeader(headerConfig) { const breadcrumbHTML = headerConfig.breadcrumb.map((crumb, index) => { const isLast = index === headerConfig.breadcrumb.length - 1; const arrow = index > 0 ? `/` : ''; const baseClasses = "text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-[#089e97] focus:ring-offset-1 rounded px-1 -mx-1"; if (isLast) { return `${arrow}${crumb.label}`; } else { return `${arrow}`; } }).join(''); const actionsHTML = headerConfig.actions.map(action => { switch(action) { case 'help': return ``; case 'ai': return ``; case 'save': return ``; default: return ''; } }).join(''); return ` `; } function openContextHelp() { showModal('Help: Provider Settings', `

About Provider Settings

Configure how providers interact with the system, their default availability, notification preferences, and scheduling permissions.

Quick Links

`); lucide.createIcons(); } function openAICopilot() { const aiActions = [ { label: 'Review provider setup', icon: 'clipboard-check', action: () => { hideModal(); showToast('AI is reviewing provider configuration...'); } }, { label: 'Recommend best defaults', icon: 'zap', action: () => { hideModal(); applyAIRecommendations(); } }, { label: 'Audit scheduling settings', icon: 'search', action: () => { hideModal(); showToast('AI audit started...'); } }, { label: 'Explain this setting', icon: 'help-circle', action: () => { hideModal(); showToast('Select a setting to explain'); } } ]; const actionsHTML = aiActions.map((action, idx) => ` `).join(''); showModal('AI Copilot', `

Select an AI-powered action to optimize your provider settings:

${actionsHTML}
`); lucide.createIcons(); } function applyAIRecommendations() { const settings = { 'setting-provider-notifications': true, 'setting-auto-scheduling': true, 'setting-conflict-prevention': true }; Object.keys(settings).forEach(id => { const el = document.getElementById(id); if (el) el.checked = settings[id]; }); markUnsaved(); showToast('AI has applied recommended settings. Review and save.'); } function saveSettings() { // GIS V1: Immediate Action Feedback const btn = document.getElementById('header-save-btn'); if (btn) applyActionFeedback(btn); // Simulate processing setTimeout(() => { clearUnsaved(); showToast('Changes saved successfully'); // Success state on save button if (btn) { btn.innerHTML = `Saved`; btn.classList.add('bg-green-600'); lucide.createIcons(); setTimeout(() => { btn.innerHTML = `Save Changes`; btn.classList.remove('bg-green-600'); lucide.createIcons(); }, 2000); } }, 400); // 400ms simulated delay for feedback feel } // Appointment Types specific functions function saveAppointmentTypes() { clearUnsaved(); showToast('Appointment types configuration saved'); // Visual feedback on save button const btn = document.getElementById('header-save-btn'); if (btn) { const originalHTML = btn.innerHTML; btn.innerHTML = `Saved`; btn.classList.add('bg-green-600'); btn.classList.remove('bg-[#089e97]', 'hover:bg-[#078b85]'); lucide.createIcons(); setTimeout(() => { btn.innerHTML = `Save Changes`; btn.classList.remove('bg-green-600'); btn.classList.add('bg-[#089e97]', 'hover:bg-[#078b85]'); lucide.createIcons(); }, 2000); } } function openAddAppointmentTypeModal() { showModal('Add Appointment Type', `
Used in calendar
`); // Override the save button behavior for this modal setTimeout(() => { const saveBtn = document.getElementById('save-modal'); if (saveBtn) { saveBtn.onclick = () => { const name = document.getElementById('new-type-name')?.value; if (!name) { showToast('Please enter an appointment name'); return; } hideModal(); showToast(`Created appointment type: ${name}`); markUnsaved(); }; } }, 100); } function openAIOptimizeModal() { const optimizations = [ { id: 'buffer-optimization', name: 'Smart Buffer Times', description: 'Add 15min buffers between high-stress appointments', impact: '+28% provider satisfaction', selected: true }, { id: 'duration-optimization', name: 'Duration Calibration', description: 'Adjust durations based on historical data', impact: '+12% schedule efficiency', selected: true }, { id: 'color-optimization', name: 'Color Psychology', description: 'Optimize color coding for patient perception', impact: 'Better patient experience', selected: false }, { id: 'name-optimization', name: 'Patient-Friendly Names', description: 'Simplify clinical terminology in booking flows', impact: '+18% booking completion', selected: true } ]; const optimizationsHTML = optimizations.map(opt => ` `).join(''); showModal('AI Optimization', `
AI Analysis Complete

Based on your scheduling patterns and provider feedback, AI recommends these optimizations:

${optimizationsHTML}
`); setTimeout(() => { const saveBtn = document.getElementById('save-modal'); if (saveBtn) { saveBtn.textContent = 'Apply Optimizations'; saveBtn.onclick = () => { hideModal(); showToast('AI optimizations applied successfully'); markUnsaved(); }; } }, 100); } function editAppointmentType(index) { showToast(`Editing appointment type ${index + 1}`); } function duplicateAppointmentType(index) { showToast('Appointment type duplicated'); markUnsaved(); } function deleteAppointmentType(index) { if (confirm('Are you sure you want to delete this appointment type? This will affect existing bookings.')) { showToast('Appointment type deleted'); markUnsaved(); } } function applyBufferRecommendation() { const bufferSelect = document.getElementById('setting-buffer-time'); if (bufferSelect) { bufferSelect.value = '15 minutes'; markUnsaved(); showToast('AI recommendation applied: 15-minute buffers enabled'); } } // Availability Page Functions function initializeAvailabilityPage() { // Set up event listeners for all inputs to mark unsaved document.querySelectorAll('#settings-form input, #settings-form select').forEach(input => { input.addEventListener('change', () => markUnsaved()); }); } function toggleAllDays() { const checkboxes = document.querySelectorAll('.toggle-day'); const allEnabled = Array.from(checkboxes).every(cb => cb.checked); checkboxes.forEach(cb => { cb.checked = !allEnabled; toggleDayRow(cb); }); showToast(allEnabled ? 'All days disabled' : 'All days enabled'); markUnsaved(); } function toggleDayRow(checkbox) { const dayRow = checkbox.closest('.day-row'); const timeInputs = dayRow.querySelectorAll('input[type="time"]'); timeInputs.forEach(input => { input.disabled = !checkbox.checked; if (!checkbox.checked) { input.value = ''; } else if (input.getAttribute('data-day')) { // Restore default values for weekdays const day = checkbox.getAttribute('data-day'); if (['monday', 'tuesday', 'wednesday', 'thursday', 'friday'].includes(day)) { if (input.previousElementSibling.textContent.includes('Start')) { input.value = '09:00'; } else { input.value = '17:00'; } } } }); } function copyDaySettings(day) { // In a real app, this would copy settings to other days showToast(`Copied ${day} settings to clipboard`); } function addTimeBlock() { const blocksList = document.getElementById('time-blocks-list'); if (!blocksList) return; const newBlock = document.createElement('div'); newBlock.className = 'time-block-item flex items-center justify-between p-3 border border-gray-200 rounded-lg test-result'; newBlock.innerHTML = `
`; blocksList.appendChild(newBlock); lucide.createIcons(); markUnsaved(); showToast('New time block added'); } function removeTimeBlock(button) { if (confirm('Are you sure you want to remove this time block?')) { const block = button.closest('.time-block-item'); block.style.opacity = '0'; block.style.transform = 'translateX(-10px)'; setTimeout(() => { block.remove(); markUnsaved(); showToast('Time block removed'); }, 300); } } function addBlackoutDate() { const today = new Date(); const nextWeek = new Date(today); nextWeek.setDate(today.getDate() + 7); const dateStr = nextWeek.toISOString().split('T')[0]; const formattedDate = new Date(dateStr).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' }); const datesList = document.getElementById('blackout-dates-list'); if (!datesList) return; const newDate = document.createElement('div'); newDate.className = 'blackout-item flex items-center justify-between p-3 border border-gray-200 rounded-lg test-result'; newDate.innerHTML = `
${formattedDate} (Custom blackout)
`; datesList.appendChild(newDate); lucide.createIcons(); markUnsaved(); showToast('Blackout date added'); } function addBlackoutDateFromInput() { const dateInput = document.getElementById('new-blackout-date'); const descInput = document.getElementById('new-blackout-description'); if (!dateInput.value) { showToast('Please select a date'); dateInput.focus(); return; } const date = new Date(dateInput.value); const formattedDate = date.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' }); const datesList = document.getElementById('blackout-dates-list'); if (!datesList) return; const newDate = document.createElement('div'); newDate.className = 'blackout-item flex items-center justify-between p-3 border border-gray-200 rounded-lg test-result'; newDate.innerHTML = `
${formattedDate} ${descInput.value ? '(' + descInput.value + ')' : ''}
`; datesList.appendChild(newDate); lucide.createIcons(); // Clear inputs dateInput.value = ''; descInput.value = ''; markUnsaved(); showToast('Blackout date added'); } function removeBlackoutDate(button) { if (confirm('Are you sure you want to remove this blackout date?')) { const dateItem = button.closest('.blackout-item'); dateItem.style.opacity = '0'; dateItem.style.transform = 'translateX(-10px)'; setTimeout(() => { dateItem.remove(); markUnsaved(); showToast('Blackout date removed'); }, 300); } } function testAvailabilityConfig() { const testResults = document.getElementById('test-results'); if (!testResults) return; // Clear previous results testResults.innerHTML = ''; // Simulate tests const tests = [ { name: 'Working Hours Validation', status: 'passed', message: 'All weekdays configured correctly' }, { name: 'Time Block Overlap Check', status: 'passed', message: 'No overlapping blocks detected' }, { name: 'Blackout Date Validation', status: 'warning', message: 'Only 2 blackout dates configured' }, { name: 'Schedule Consistency', status: 'passed', message: 'No gaps in provider schedules' } ]; tests.forEach(test => { const testEl = document.createElement('div'); testEl.className = 'flex items-center justify-between py-2 border-b border-gray-100 last:border-0'; testEl.innerHTML = `
${test.name}
${test.message}
${test.status === 'passed' ? '✓ Passed' : '⚠ Warning'} `; testResults.appendChild(testEl); }); showToast('Availability configuration tested successfully'); } function saveAvailabilitySettings() { clearUnsaved(); // Visual feedback const btn = document.getElementById('header-save-btn'); if (btn) { const originalHTML = btn.innerHTML; btn.innerHTML = `Saved`; btn.classList.add('bg-green-600'); btn.classList.remove('bg-[#089e97]', 'hover:bg-[#078b85]'); lucide.createIcons(); setTimeout(() => { btn.innerHTML = `Save Changes`; btn.classList.remove('bg-green-600'); btn.classList.add('bg-[#089e97]', 'hover:bg-[#078b85]'); lucide.createIcons(); }, 2000); } showToast('Availability settings saved successfully'); // Update status pill if this is the first save const statusPill = document.querySelector('.status-pill'); if (statusPill && statusPill.classList.contains('status-needs-setup')) { statusPill.classList.remove('status-needs-setup'); statusPill.classList.add('status-optimized'); statusPill.innerHTML = `Optimized`; statusPill.querySelector('span').textContent = 'Optimized'; } } function openAIOptimizeAvailability() { const optimizations = [ { id: 'extend-hours', name: 'Extend Friday Hours', description: 'High demand detected on Friday afternoons', impact: '+8 appointments/week', selected: true }, { id: 'add-saturday', name: 'Enable Saturday Mornings', description: 'Patient survey shows 34% want weekend slots', impact: '+15% patient satisfaction', selected: true }, { id: 'buffer-optimization', name: 'Smart Buffer Times', description: 'Add 15min buffers between appointments', impact: '+45% fewer overlaps', selected: true }, { id: 'lunch-optimization', name: 'Optimize Lunch Breaks', description: 'Extend to 90 minutes on busy days', impact: '+28% provider satisfaction', selected: false } ]; const optimizationsHTML = optimizations.map(opt => ` `).join(''); showModal('AI Availability Optimization', `
AI Analysis Complete

Based on your patient demand patterns and provider capacity, AI recommends these optimizations:

${optimizationsHTML}
`); setTimeout(() => { const saveBtn = document.getElementById('save-modal'); if (saveBtn) { saveBtn.textContent = 'Apply Optimizations'; saveBtn.onclick = () => { // Apply selected optimizations optimizations.forEach(opt => { if (opt.selected) { switch(opt.id) { case 'extend-hours': // Enable Friday and extend hours const fridayCheckbox = document.querySelector('.toggle-day[data-day="friday"]'); if (fridayCheckbox) { fridayCheckbox.checked = true; toggleDayRow(fridayCheckbox); const fridayEndInput = document.querySelector('.toggle-day[data-day="friday"]').closest('.day-row').querySelectorAll('input[type="time"]')[1]; if (fridayEndInput) fridayEndInput.value = '18:00'; } break; case 'add-saturday': const saturdayCheckbox = document.querySelector('.toggle-day[data-day="saturday"]'); if (saturdayCheckbox) { saturdayCheckbox.checked = true; toggleDayRow(saturdayCheckbox); const saturdayStartInput = saturdayCheckbox.closest('.day-row').querySelectorAll('input[type="time"]')[0]; const saturdayEndInput = saturdayCheckbox.closest('.day-row').querySelectorAll('input[type="time"]')[1]; if (saturdayStartInput) saturdayStartInput.value = '09:00'; if (saturdayEndInput) saturdayEndInput.value = '13:00'; } break; case 'buffer-optimization': addTimeBlock(); break; } } }); hideModal(); markUnsaved(); showToast('AI optimizations applied successfully'); }; } }, 100); } function applyAIRecommendation(type) { switch(type) { case 'buffer': addTimeBlock(); showToast('Buffer time recommendation applied'); break; case 'weekend': const saturdayCheckbox = document.querySelector('.toggle-day[data-day="saturday"]'); if (saturdayCheckbox) { saturdayCheckbox.checked = true; toggleDayRow(saturdayCheckbox); const saturdayStartInput = saturdayCheckbox.closest('.day-row').querySelectorAll('input[type="time"]')[0]; const saturdayEndInput = saturdayCheckbox.closest('.day-row').querySelectorAll('input[type="time"]')[1]; if (saturdayStartInput) saturdayStartInput.value = '09:00'; if (saturdayEndInput) saturdayEndInput.value = '13:00'; showToast('Saturday hours enabled (9 AM - 1 PM)'); } break; case 'lunch': const lunchBlock = document.querySelector('.time-block-item:first-child input[type="text"]'); if (lunchBlock && lunchBlock.value === 'Lunch Break') { const endInput = lunchBlock.closest('.time-block-item').querySelectorAll('input[type="time"]')[1]; if (endInput) endInput.value = '13:30'; showToast('Lunch break extended to 90 minutes'); } break; } markUnsaved(); } function autoApplyAIRecommendation(type) { applyAIRecommendation(type); showToast('AI automatically applied the recommendation'); } // Sticky header scroll behavior function initStickyHeader() { const scrollContainer = document.getElementById('main-content-scroll'); const header = document.getElementById('page-header'); if (!scrollContainer || !header) return; scrollContainer.addEventListener('scroll', () => { if (scrollContainer.scrollTop > 10) { header.classList.add('shadow-sm', 'bg-white/95'); header.classList.remove('bg-white/80'); } else { header.classList.remove('shadow-sm', 'bg-white/95'); header.classList.add('bg-white/80'); } }); } // Navigation to settings section function navigateToSection(sectionId) { // GIS V1: Immediate feedback on navigation trigger // (Only if triggered by a button click, not programmatic navigation) if (event && event.currentTarget && event.currentTarget.tagName === 'A') { event.currentTarget.style.transform = 'scale(0.97)'; setTimeout(() => { event.currentTarget.style.transform = ''; }, 150); } // Check for unsaved changes before navigating if (hasUnsavedChanges && sectionId !== appState.currentView) { if (!confirm('You have unsaved changes. Are you sure you want to leave this page?')) { return; } clearUnsaved(); } // Update active state in sidebar document.querySelectorAll('#settings-sidebar a').forEach(link => { link.classList.remove('sidebar-item-active'); if (link.dataset.section === sectionId) { link.classList.add('sidebar-item-active'); } }); // Update app state appState.currentView = sectionId; const mainContent = document.getElementById('main-content'); if (sectionId === 'dashboard') { location.reload(); return; } // Check if we have a premium header config for this section const headerConfig = pageHeaders[sectionId] || { title: sectionId.charAt(0).toUpperCase() + sectionId.slice(1).replace(/-/g, ' ') + ' Settings', description: `Configure your ${sectionId.replace(/-/g, ' ')} settings`, breadcrumb: [ { label: 'Settings', action: () => navigateToSection('dashboard'), clickable: true }, { label: sectionId.charAt(0).toUpperCase() + sectionId.slice(1).replace(/-/g, ' '), action: null, clickable: false } ], actions: ['help', 'ai', 'save'] }; // Premium Appointment Types Settings Page with Enterprise Command Header if (sectionId === 'appointment-types') { const appointmentTypesData = { count: 8, status: 'Active', lastUpdated: '2 hours ago', usageContexts: ['Scheduling OS', 'Booking Links', 'Patient Intake'], activeTypes: [ { name: 'Initial Consultation', duration: '60 min', color: '#089e97', usage: 'High' }, { name: 'Follow-up Visit', duration: '30 min', color: '#3b82f6', usage: 'High' }, { name: 'Urgent Care', duration: '20 min', color: '#f59e0b', usage: 'Medium' }, { name: 'Telehealth', duration: '45 min', color: '#8b5cf6', usage: 'High' } ] }; mainContent.innerHTML = `

Active Appointment Types

${appointmentTypesData.activeTypes.map((type, index) => `

${type.name}

${type.usage} Usage
${type.duration} Used in scheduling & intake
`).join('')}

Global Behavior

Time between appointments

Minimum advance notice

AI Insight

Adding 15-minute buffer times between appointments could reduce provider stress by 28% and prevent scheduling conflicts.

Usage Analytics

Most Booked Follow-up Visit
Growth Trend +12% this month

Integration Status

Scheduling OS
Connected
Booking Links
Active
Intake Forms
4 of 8 linked
`; lucide.createIcons(); setupFormChangeDetection(); initStickyHeader(); } else if (sectionId === 'providers') { mainContent.innerHTML = ` ${renderPremiumHeader(headerConfig)}

Provider Configuration

Permissions & Access

Pro Tip

Enable Auto-Scheduling to let the AI optimize your provider calendars, reducing gaps by up to 35%.

Current Stats

Active Providers 12
Avg. Daily Appointments 48
Utilization Rate 87%
`; lucide.createIcons(); setupFormChangeDetection(); initStickyHeader(); } else { // Generic premium page for other sections mainContent.innerHTML = ` ${renderPremiumHeader(headerConfig)}

Configuration Options

`; lucide.createIcons(); setupFormChangeDetection(); initStickyHeader(); } } // Quick action handlers function handleQuickAction(action) { const actions = { 'add-provider': { title: 'Add New Provider', content: `
` }, 'set-availability': { title: 'Set Availability Rules', content: `
to
` }, 'create-appointment': { title: 'Create Appointment Type', content: `
` }, 'enable-payments': { title: 'Enable Payments', content: `

Select Payment Processor

` } }; if (actions[action]) { showModal(actions[action].title, actions[action].content); } else { showModal('Quick Action', `

This action would open the ${action} configuration panel.

`); } } // Initialize event listeners function initializeEventListeners() { // Checklist item toggles document.querySelectorAll('.checklist-item').forEach(item => { item.addEventListener('click', (e) => { if (e.target.tagName === 'BUTTON') { const itemId = e.currentTarget.dataset.item; navigateToSection(itemId.split('-')[0]); } else { const itemId = e.currentTarget.dataset.item; toggleChecklistItem(itemId); } }); }); // Quick actions document.querySelectorAll('.quick-action').forEach(button => { button.addEventListener('click', () => { const action = button.dataset.action; handleQuickAction(action); }); }); // Settings sidebar navigation - Updated for V2 document.querySelectorAll('#settings-sidebar a').forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const section = link.dataset.section; navigateToSection(section); }); }); // Settings category cards document.querySelectorAll('[data-section]').forEach(card => { if (card.classList.contains('card-hover')) { card.addEventListener('click', () => { const section = card.dataset.section; navigateToSection(section); }); } }); // Adaptive quick actions buttons document.querySelectorAll('#adaptive-actions button').forEach(button => { button.addEventListener('click', (e) => { // Let the onclick attribute handle navigation }); }); // Settings module cards document.querySelectorAll('#settings-cards > div').forEach(card => { card.addEventListener('click', (e) => { // Let the onclick attribute handle navigation }); }); // Collapse sidebar button document.getElementById('collapse-sidebar').addEventListener('click', () => { const sidebar = document.getElementById('settings-sidebar'); sidebar.classList.toggle('hidden'); appState.settingsSidebarExpanded = !appState.settingsSidebarExpanded; }); // AI Assistant toggle document.getElementById('ai-assistant-toggle').addEventListener('click', () => { const panel = document.getElementById('ai-assistant-panel'); if (appState.aiPanelExpanded) { panel.style.width = '0'; } else { panel.style.width = '20rem'; } appState.aiPanelExpanded = !appState.aiPanelExpanded; }); // Close AI panel document.getElementById('close-ai-panel').addEventListener('click', () => { const panel = document.getElementById('ai-assistant-panel'); panel.style.width = '0'; appState.aiPanelExpanded = false; }); // Save all changes button document.querySelector('button.bg-\\[\\#089e97\\]').addEventListener('click', () => { showToast('All settings saved successfully'); }); // Modal overlay click to close document.getElementById('modal-overlay').addEventListener('click', (e) => { if (e.target === document.getElementById('modal-overlay')) { hideModal(); } }); // System health fix buttons document.querySelectorAll('.health-warning button').forEach(button => { button.addEventListener('click', () => { navigateToSection('payments'); }); }); // AI recommendation buttons document.querySelectorAll('.bg-gray-50 button').forEach(button => { button.addEventListener('click', () => { showToast('Navigating to recommended setting'); }); }); } // Account Setup Progress Functions function toggleSetupItem(element) { // GIS V1: Add immediate scale feedback element.style.transform = 'scale(0.98)'; element.style.transition = 'transform 80ms ease'; setTimeout(() => { element.style.transform = ''; }, 100); const circle = element.querySelector('.check-circle'); const icon = circle.querySelector('i'); const text = element.querySelector('span'); const isCompleted = circle.classList.contains('bg-[#089e97]'); if (isCompleted) { // Uncheck circle.classList.remove('bg-[#089e97]', 'text-white'); circle.classList.add('bg-white', 'border-2', 'border-gray-300'); icon.classList.add('hidden'); text.classList.remove('line-through', 'text-gray-400'); text.classList.add('text-gray-700'); } else { // Check circle.classList.remove('bg-white', 'border-2', 'border-gray-300'); circle.classList.add('bg-[#089e97]', 'text-white'); icon.classList.remove('hidden'); text.classList.add('line-through', 'text-gray-400'); text.classList.remove('text-gray-700'); } updateSetupProgress(); showToast(isCompleted ? 'Task marked incomplete' : 'Task completed'); } function updateSetupProgress() { const totalItems = document.querySelectorAll('#settings-dashboard-top-module .checklist-item').length; const completedItems = document.querySelectorAll('#settings-dashboard-top-module .check-circle.bg-\\[\\#089e97\\]').length; const percentage = Math.round((completedItems / totalItems) * 100) || 56; const percentageEl = document.getElementById('setup-progress-percentage'); const barEl = document.getElementById('setup-progress-bar'); if (percentageEl) percentageEl.textContent = percentage + '%'; if (barEl) barEl.style.width = percentage + '%'; } // Initialize the app document.addEventListener('DOMContentLoaded', () => { initializeEventListeners(); updateProgressDisplay(); updateSetupProgress(); // Initialize setup progress on load // Show welcome toast setTimeout(() => { showToast('Welcome to Verify MC Settings Dashboard'); }, 1000); // Ensure settings sidebar is expanded on load const sidebar = document.getElementById('settings-sidebar'); sidebar.classList.remove('hidden'); // Initialize module panel event listeners const modulePanelOverlay = document.getElementById('module-panel-overlay'); if (modulePanelOverlay) { modulePanelOverlay.addEventListener('click', (e) => { if (e.target === modulePanelOverlay) { closeModulePanel(); } }); } // Initialize collapsible sidebar sections setTimeout(() => { initializeSidebarSections(); // Auto-expand the section for current view if (appState.currentView && appState.currentView !== 'dashboard') { expandSectionForView(appState.currentView); } }, 100); });