Spaces:
Running
Running
| // Initialize Lucide Icons | |
| lucide.createIcons(); | |
| // Page Management | |
| const pages = { | |
| 'dashboard': { | |
| title: 'Enterprise Dashboard', | |
| subtitle: 'Real-time analytics and reporting' | |
| }, | |
| 'revenue': { | |
| title: 'Revenue Analytics', | |
| subtitle: 'Detailed revenue breakdown and forecasting' | |
| }, | |
| 'patient-intelligence': { | |
| title: 'Patient Intelligence', | |
| subtitle: 'Demographics, conditions, and patient analytics' | |
| }, | |
| 'expiring-patients': { | |
| title: 'Expiring Patient Reports', | |
| subtitle: 'Monitor and manage patient expiration risk' | |
| }, | |
| 'renewal-reports': { | |
| title: 'Renewal & Approval Reports', | |
| subtitle: 'Track renewal rates and approval metrics' | |
| }, | |
| 'provider-performance': { | |
| title: 'Provider Performance', | |
| subtitle: 'Provider analytics and performance tracking' | |
| }, | |
| 'appointment-reports': { | |
| title: 'Appointment Reports', | |
| subtitle: 'Appointment analytics and scheduling insights' | |
| }, | |
| 'one-click-reports': { | |
| title: 'One-Click Reports', | |
| subtitle: 'Pre-built reports for instant insights' | |
| }, | |
| 'custom-reports': { | |
| title: 'Custom Reports Builder', | |
| subtitle: 'Build, save, and schedule custom reports' | |
| } | |
| }; | |
| // Chart instances storage | |
| const charts = {}; | |
| // Initialize the application | |
| document.addEventListener('DOMContentLoaded', function() { | |
| initializeCharts(); | |
| initializeEventListeners(); | |
| }); | |
| // Page Navigation | |
| function showPage(pageId) { | |
| // Hide all pages | |
| document.querySelectorAll('.page').forEach(page => { | |
| page.classList.remove('active'); | |
| }); | |
| // Show selected page | |
| const pageElement = document.getElementById(pageId); | |
| if (pageElement) { | |
| pageElement.classList.add('active'); | |
| // Update page title | |
| const pageInfo = pages[pageId]; | |
| if (pageInfo) { | |
| document.getElementById('page-title').textContent = pageInfo.title; | |
| document.getElementById('page-subtitle').textContent = pageInfo.subtitle; | |
| } | |
| // Update navigation active state | |
| document.querySelectorAll('.nav-item').forEach(item => { | |
| item.classList.remove('active', 'bg-primary-light', 'text-primary'); | |
| item.classList.add('text-gray-700', 'hover:bg-gray-100'); | |
| }); | |
| const activeNav = document.querySelector(`[onclick="showPage('${pageId}')"]`); | |
| if (activeNav) { | |
| activeNav.classList.add('active', 'bg-primary-light', 'text-primary'); | |
| activeNav.classList.remove('text-gray-700', 'hover:bg-gray-100'); | |
| } | |
| // Refresh charts for the new page | |
| setTimeout(() => { | |
| refreshPageCharts(pageId); | |
| }, 100); | |
| } | |
| } | |
| // Slide-out Panel Management | |
| function openSlideOut(panelId) { | |
| const panel = document.getElementById('slide-out-panel'); | |
| panel.classList.add('open'); | |
| // Load panel content based on panelId | |
| loadPanelContent(panelId); | |
| } | |
| function closeSlideOut() { | |
| const panel = document.getElementById('slide-out-panel'); | |
| panel.classList.remove('open'); | |
| } | |
| // Modal Management | |
| function openModal(modalId) { | |
| const modal = document.getElementById(modalId); | |
| if (modal) { | |
| modal.classList.remove('hidden'); | |
| modal.classList.add('flex'); | |
| } | |
| } | |
| function closeModal(modalId) { | |
| const modal = document.getElementById(modalId); | |
| if (modal) { | |
| modal.classList.add('hidden'); | |
| modal.classList.remove('flex'); | |
| } | |
| } | |
| // Initialize Charts | |
| function initializeCharts() { | |
| // Revenue Trend Chart | |
| const revenueCtx = document.getElementById('revenueTrendChart'); | |
| if (revenueCtx) { | |
| charts.revenueTrend = new Chart(revenueCtx, { | |
| type: 'line', | |
| data: { | |
| labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], | |
| datasets: [{ | |
| label: 'Revenue ($)', | |
| data: [320000, 350000, 380000, 420000, 450000, 480000, 510000, 540000, 580000, 620000, 650000, 690000], | |
| borderColor: '#0e9692', | |
| backgroundColor: 'rgba(14, 150, 146, 0.1)', | |
| borderWidth: 3, | |
| fill: true, | |
| tension: 0.4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false | |
| }, | |
| tooltip: { | |
| mode: 'index', | |
| intersect: false | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: false, | |
| grid: { | |
| display: true, | |
| color: 'rgba(0, 0, 0, 0.05)' | |
| }, | |
| ticks: { | |
| callback: function(value) { | |
| return '$' + (value / 1000).toFixed(0) + 'K'; | |
| } | |
| } | |
| }, | |
| x: { | |
| grid: { | |
| display: false | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Patient Distribution Chart | |
| const patientCtx = document.getElementById('patientDistributionChart'); | |
| if (patientCtx) { | |
| charts.patientDistribution = new Chart(patientCtx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['Chronic Pain', 'Anxiety', 'Sleep Disorders', 'PTSD', 'Cancer', 'Other'], | |
| datasets: [{ | |
| data: [35, 25, 15, 12, 8, 5], | |
| backgroundColor: [ | |
| '#0e9692', | |
| '#10b981', | |
| '#3b82f6', | |
| '#8b5cf6', | |
| '#ec4899', | |
| '#6b7280' | |
| ], | |
| borderWidth: 0 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'bottom', | |
| labels: { | |
| padding: 20, | |
| usePointStyle: true | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Expiring Chart | |
| const expiringCtx = document.getElementById('expiringChart'); | |
| if (expiringCtx) { | |
| charts.expiring = new Chart(expiringCtx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['Expiring', 'Active'], | |
| datasets: [{ | |
| data: [312, 8430], | |
| backgroundColor: ['#f59e0b', '#0e9692'], | |
| borderWidth: 0 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Revenue by Category Chart | |
| const categoryCtx = document.getElementById('revenueByCategoryChart'); | |
| if (categoryCtx) { | |
| charts.revenueByCategory = new Chart(categoryCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: ['Consultations', 'Products', 'Renewals', 'Follow-ups', 'Other'], | |
| datasets: [{ | |
| label: 'Revenue ($)', | |
| data: [185000, 245000, 98000, 65000, 42000], | |
| backgroundColor: '#0e9692', | |
| borderRadius: 6 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| grid: { | |
| display: true, | |
| color: 'rgba(0, 0, 0, 0.05)' | |
| }, | |
| ticks: { | |
| callback: function(value) { | |
| return '$' + (value / 1000).toFixed(0) + 'K'; | |
| } | |
| } | |
| }, | |
| x: { | |
| grid: { | |
| display: false | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Monthly Revenue Chart | |
| const monthlyCtx = document.getElementById('monthlyRevenueChart'); | |
| if (monthlyCtx) { | |
| charts.monthlyRevenue = new Chart(monthlyCtx, { | |
| type: 'line', | |
| data: { | |
| labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4'], | |
| datasets: [{ | |
| label: 'Current Month', | |
| data: [85000, 92000, 98000, 105000], | |
| borderColor: '#0e9692', | |
| backgroundColor: 'rgba(14, 150, 146, 0.1)', | |
| borderWidth: 3, | |
| fill: true, | |
| tension: 0.4 | |
| }, { | |
| label: 'Previous Month', | |
| data: [82000, 88000, 91000, 95000], | |
| borderColor: '#9ca3af', | |
| backgroundColor: 'transparent', | |
| borderWidth: 2, | |
| borderDash: [5, 5], | |
| tension: 0.4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'top', | |
| labels: { | |
| usePointStyle: true | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: false, | |
| grid: { | |
| display: true, | |
| color: 'rgba(0, 0, 0, 0.05)' | |
| }, | |
| ticks: { | |
| callback: function(value) { | |
| return '$' + (value / 1000).toFixed(0) + 'K'; | |
| } | |
| } | |
| }, | |
| x: { | |
| grid: { | |
| display: false | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Age Distribution Chart | |
| const ageCtx = document.getElementById('ageDistributionChart'); | |
| if (ageCtx) { | |
| charts.ageDistribution = new Chart(ageCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: ['18-25', '26-35', '36-45', '46-55', '56-65', '65+'], | |
| datasets: [{ | |
| label: 'Patients', | |
| data: [12, 28, 35, 25, 15, 5], | |
| backgroundColor: '#0e9692', | |
| borderRadius: 6 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| grid: { | |
| display: true, | |
| color: 'rgba(0, 0, 0, 0.05)' | |
| }, | |
| ticks: { | |
| callback: function(value) { | |
| return value + '%'; | |
| } | |
| } | |
| }, | |
| x: { | |
| grid: { | |
| display: false | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Conditions Chart | |
| const conditionsCtx = document.getElementById('conditionsChart'); | |
| if (conditionsCtx) { | |
| charts.conditions = new Chart(conditionsCtx, { | |
| type: 'polarArea', | |
| data: { | |
| labels: ['Chronic Pain', 'Anxiety', 'Insomnia', 'PTSD', 'Cancer', 'Migraine'], | |
| datasets: [{ | |
| data: [42, 28, 15, 8, 4, 3], | |
| backgroundColor: [ | |
| '#0e9692', | |
| '#10b981', | |
| '#3b82f6', | |
| '#8b5cf6', | |
| '#ec4899', | |
| '#f59e0b' | |
| ], | |
| borderWidth: 0 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'bottom', | |
| labels: { | |
| padding: 20, | |
| usePointStyle: true | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Growth Gauge Chart | |
| const growthCtx = document.getElementById('growthGauge'); | |
| if (growthCtx) { | |
| charts.growthGauge = new Chart(growthCtx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['Growth', 'Remaining'], | |
| datasets: [{ | |
| data: [24.8, 75.2], | |
| backgroundColor: ['#0e9692', '#e5e7eb'], | |
| borderWidth: 0 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| return context.label + ': ' + context.parsed + '%'; | |
| } | |
| } | |
| } | |
| }, | |
| cutout: '80%' | |
| } | |
| }); | |
| } | |
| // Provider Trend Chart for slide-out | |
| const providerTrendCtx = document.getElementById('providerTrendChart'); | |
| if (providerTrendCtx) { | |
| charts.providerTrend = new Chart(providerTrendCtx, { | |
| type: 'line', | |
| data: { | |
| labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], | |
| datasets: [{ | |
| label: 'Satisfaction Score', | |
| data: [88, 90, 92, 91, 94, 95], | |
| borderColor: '#0e9692', | |
| backgroundColor: 'rgba(14, 150, 146, 0.1)', | |
| borderWidth: 3, | |
| fill: true, | |
| tension: 0.4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: false, | |
| min: 85, | |
| max: 100, | |
| grid: { | |
| display: true, | |
| color: 'rgba(0, 0, 0, 0.05)' | |
| }, | |
| ticks: { | |
| callback: function(value) { | |
| return value + '%'; | |
| } | |
| } | |
| }, | |
| x: { | |
| grid: { | |
| display: false | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| } | |
| // Initialize Event Listeners | |
| function initializeEventListeners() { | |
| // Close modals when clicking outside | |
| document.querySelectorAll('[id$="-modal"]').forEach(modal => { | |
| modal.addEventListener('click', function(e) { | |
| if (e.target === this) { | |
| closeModal(modal.id); | |
| } | |
| }); | |
| }); | |
| // Close slide-out when clicking outside | |
| const slideOut = document.getElementById('slide-out-panel'); | |
| if (slideOut) { | |
| slideOut.addEventListener('click', function(e) { | |
| if (e.target === this) { | |
| closeSlideOut(); | |
| } | |
| }); | |
| } | |
| // Close report detail panel when clicking outside | |
| const reportPanel = document.getElementById('report-detail-panel'); | |
| if (reportPanel) { | |
| reportPanel.addEventListener('click', function(e) { | |
| if (e.target === this) { | |
| closeSlideOut(); | |
| } | |
| }); | |
| } | |
| // Escape key to close modals | |
| document.addEventListener('keydown', function(e) { | |
| if (e.key === 'Escape') { | |
| closeSlideOut(); | |
| closeModal('report-detail-panel'); | |
| document.querySelectorAll('[id$="-modal"]').forEach(modal => { | |
| if (!modal.classList.contains('hidden')) { | |
| closeModal(modal.id); | |
| } | |
| }); | |
| } | |
| }); | |
| // Initialize drag and drop for custom reports | |
| initializeDragAndDrop(); | |
| } | |
| // Refresh charts for specific page | |
| function refreshPageCharts(pageId) { | |
| switch(pageId) { | |
| case 'dashboard': | |
| if (charts.revenueTrend) charts.revenueTrend.update(); | |
| if (charts.patientDistribution) charts.patientDistribution.update(); | |
| if (charts.expiring) charts.expiring.update(); | |
| break; | |
| case 'revenue': | |
| if (charts.revenueByCategory) charts.revenueByCategory.update(); | |
| if (charts.monthlyRevenue) charts.monthlyRevenue.update(); | |
| if (charts.growthGauge) charts.growthGauge.update(); | |
| break; | |
| case 'patient-intelligence': | |
| if (charts.ageDistribution) charts.ageDistribution.update(); | |
| if (charts.conditions) charts.conditions.update(); | |
| break; | |
| } | |
| } | |
| // Load panel content | |
| function loadPanelContent(panelId) { | |
| // In a real application, this would fetch content from a server | |
| // For demo, we'll just update some elements | |
| console.log('Loading panel content for:', panelId); | |
| } | |
| // Generate Report | |
| function generateReport(reportType) { | |
| const reports = { | |
| 'monthly-performance': { | |
| title: 'Monthly Performance Report', | |
| message: 'Generating comprehensive monthly performance report...' | |
| }, | |
| 'provider-metrics': { | |
| title: 'Provider Metrics Report', | |
| message: 'Creating detailed provider performance analysis...' | |
| }, | |
| 'expiration-risk': { | |
| title: 'Expiration Risk Report', | |
| message: 'Compiling expiration risk assessment...' | |
| }, | |
| 'ai-recommendations': { | |
| title: 'AI Recommendations Report', | |
| message: 'Generating personalized AI recommendations...' | |
| } | |
| }; | |
| const report = reports[reportType]; | |
| if (report) { | |
| // Show loading state | |
| const button = event.target.closest('button'); | |
| const originalText = button ? button.innerHTML : ''; | |
| if (button) { | |
| button.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> Generating...'; | |
| button.disabled = true; | |
| } | |
| // Simulate report generation | |
| setTimeout(() => { | |
| if (button) { | |
| button.innerHTML = originalText; | |
| button.disabled = false; | |
| } | |
| // Show success message | |
| alert(`β ${report.title} generated successfully!`); | |
| // In a real app, this would download the report | |
| console.log(`Generated report: ${reportType}`); | |
| }, 1500); | |
| } | |
| } | |
| // Export Report | |
| function exportReport() { | |
| // Show loading | |
| const button = event.target; | |
| const originalText = button.innerHTML; | |
| button.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> Exporting...'; | |
| button.disabled = true; | |
| // Simulate export process | |
| setTimeout(() => { | |
| button.innerHTML = originalText; | |
| button.disabled = false; | |
| closeModal('export-modal'); | |
| // Show success | |
| alert('β Report exported successfully! The download will begin shortly.'); | |
| }, 2000); | |
| } | |
| // Initialize tooltips | |
| function initializeTooltips() { | |
| // This would initialize tooltip library in a real app | |
| console.log('Tooltips initialized'); | |
| } | |
| // Custom Reports Functions | |
| function initializeDragAndDrop() { | |
| const draggableComponents = document.querySelectorAll('.draggable-component'); | |
| const reportCanvas = document.getElementById('report-canvas'); | |
| draggableComponents.forEach(component => { | |
| component.addEventListener('dragstart', function(e) { | |
| e.dataTransfer.setData('text/plain', this.dataset.component); | |
| this.classList.add('opacity-50'); | |
| }); | |
| component.addEventListener('dragend', function() { | |
| this.classList.remove('opacity-50'); | |
| }); | |
| }); | |
| if (reportCanvas) { | |
| reportCanvas.addEventListener('dragover', function(e) { | |
| e.preventDefault(); | |
| this.classList.add('border-primary', 'border-solid'); | |
| this.classList.remove('border-dashed', 'border-gray-300'); | |
| }); | |
| reportCanvas.addEventListener('dragleave', function() { | |
| this.classList.remove('border-primary', 'border-solid'); | |
| this.classList.add('border-dashed', 'border-gray-300'); | |
| }); | |
| reportCanvas.addEventListener('drop', function(e) { | |
| e.preventDefault(); | |
| this.classList.remove('border-primary', 'border-solid'); | |
| this.classList.add('border-dashed', 'border-gray-300'); | |
| const componentType = e.dataTransfer.getData('text/plain'); | |
| addComponentToCanvas(componentType); | |
| }); | |
| } | |
| } | |
| function addComponentToCanvas(type) { | |
| const canvas = document.getElementById('report-canvas'); | |
| // Remove placeholder if present | |
| const placeholder = canvas.querySelector('.text-center'); | |
| if (placeholder) { | |
| placeholder.remove(); | |
| } | |
| let componentHTML = ''; | |
| switch(type) { | |
| case 'kpi': | |
| componentHTML = ` | |
| <div class="bg-gray-50 rounded-lg p-6 border border-gray-200"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h4 class="font-medium text-gray-900">KPI Component</h4> | |
| <button onclick="removeComponent(this)" class="text-gray-400 hover:text-gray-600"> | |
| <i data-lucide="x" class="w-4 h-4"></i> | |
| </button> | |
| </div> | |
| <div class="grid grid-cols-2 gap-4"> | |
| <div class="bg-white rounded-lg p-4"> | |
| <p class="text-sm text-gray-600">Metric 1</p> | |
| <p class="text-xl font-bold text-gray-900">$12,450</p> | |
| </div> | |
| <div class="bg-white rounded-lg p-4"> | |
| <p class="text-sm text-gray-600">Metric 2</p> | |
| <p class="text-xl font-bold text-gray-900">84.2%</p> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| break; | |
| case 'chart': | |
| componentHTML = ` | |
| <div class="bg-gray-50 rounded-lg p-6 border border-gray-200"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h4 class="font-medium text-gray-900">Chart Component</h4> | |
| <div class="flex items-center space-x-2"> | |
| <select class="border border-gray-300 rounded-lg px-2 py-1 text-sm"> | |
| <option>Line Chart</option> | |
| <option>Bar Chart</option> | |
| <option>Pie Chart</option> | |
| </select> | |
| <button onclick="removeComponent(this)" class="text-gray-400 hover:text-gray-600"> | |
| <i data-lucide="x" class="w-4 h-4"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="h-48 bg-white rounded-lg flex items-center justify-center"> | |
| <div class="text-center"> | |
| <i data-lucide="pie-chart" class="w-8 h-8 text-gray-400 mx-auto mb-2"></i> | |
| <p class="text-sm text-gray-600">Chart preview will appear here</p> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| break; | |
| case 'table': | |
| componentHTML = ` | |
| <div class="bg-gray-50 rounded-lg p-6 border border-gray-200"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h4 class="font-medium text-gray-900">Table Component</h4> | |
| <button onclick="removeComponent(this)" class="text-gray-400 hover:text-gray-600"> | |
| <i data-lucide="x" class="w-4 h-4"></i> | |
| </button> | |
| </div> | |
| <div class="bg-white rounded-lg overflow-hidden"> | |
| <table class="min-w-full"> | |
| <thead class="bg-gray-100"> | |
| <tr> | |
| <th class="px-4 py-2 text-left text-sm font-medium text-gray-700">Column 1</th> | |
| <th class="px-4 py-2 text-left text-sm font-medium text-gray-700">Column 2</th> | |
| <th class="px-4 py-2 text-left text-sm font-medium text-gray-700">Column 3</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr class="border-b border-gray-200"> | |
| <td class="px-4 py-2 text-sm text-gray-700">Data 1</td> | |
| <td class="px-4 py-2 text-sm text-gray-700">Data 2</td> | |
| <td class="px-4 py-2 text-sm text-gray-700">Data 3</td> | |
| </tr> | |
| <tr> | |
| <td class="px-4 py-2 text-sm text-gray-700">Data 4</td> | |
| <td class="px-4 py-2 text-sm text-gray-700">Data 5</td> | |
| <td class="px-4 py-2 text-sm text-gray-700">Data 6</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| `; | |
| break; | |
| case 'filter': | |
| componentHTML = ` | |
| <div class="bg-gray-50 rounded-lg p-6 border border-gray-200"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h4 class="font-medium text-gray-900">Filter Component</h4> | |
| <button onclick="removeComponent(this)" class="text-gray-400 hover:text-gray-600"> | |
| <i data-lucide="x" class="w-4 h-4"></i> | |
| </button> | |
| </div> | |
| <div class="flex flex-wrap gap-3"> | |
| <select class="border border-gray-300 rounded-lg px-4 py-2 text-sm"> | |
| <option>Date Range</option> | |
| <option>Last 7 days</option> | |
| <option>Last 30 days</option> | |
| <option>Last quarter</option> | |
| </select> | |
| <select class="border border-gray-300 rounded-lg px-4 py-2 text-sm"> | |
| <option>Location</option> | |
| <option>All locations</option> | |
| <option>Downtown Clinic</option> | |
| <option>Westside Clinic</option> | |
| </select> | |
| <select class="border border-gray-300 rounded-lg px-4 py-2 text-sm"> | |
| <option>Provider</option> | |
| <option>All providers</option> | |
| <option>Dr. Smith</option> | |
| <option>Dr. Johnson</option> | |
| </select> | |
| </div> | |
| </div> | |
| `; | |
| break; | |
| } | |
| canvas.insertAdjacentHTML('beforeend', componentHTML); | |
| lucide.createIcons(); | |
| } | |
| function removeComponent(button) { | |
| const component = button.closest('.bg-gray-50'); | |
| if (component) { | |
| component.remove(); | |
| // Show placeholder if canvas is empty | |
| const canvas = document.getElementById('report-canvas'); | |
| if (canvas.children.length === 0) { | |
| canvas.innerHTML = ` | |
| <div class="text-center py-12"> | |
| <i data-lucide="layout" class="w-12 h-12 text-gray-400 mx-auto mb-4"></i> | |
| <p class="text-gray-600">Drag and drop components here to build your report</p> | |
| <p class="text-sm text-gray-500 mt-1">Start by dragging components from the left panel</p> | |
| </div> | |
| `; | |
| } | |
| } | |
| } | |
| function clearCanvas() { | |
| const canvas = document.getElementById('report-canvas'); | |
| if (canvas) { | |
| canvas.innerHTML = ` | |
| <div class="text-center py-12"> | |
| <i data-lucide="layout" class="w-12 h-12 text-gray-400 mx-auto mb-4"></i> | |
| <p class="text-gray-600">Drag and drop components here to build your report</p> | |
| <p class="text-sm text-gray-500 mt-1">Start by dragging components from the left panel</p> | |
| </div> | |
| `; | |
| } | |
| } | |
| function saveCustomReport() { | |
| const button = event.target; | |
| const originalText = button.innerHTML; | |
| button.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> Saving...'; | |
| button.disabled = true; | |
| setTimeout(() => { | |
| button.innerHTML = originalText; | |
| button.disabled = false; | |
| alert('β Custom report saved successfully!'); | |
| }, 1500); | |
| } | |
| function generateCustomReport() { | |
| const button = event.target; | |
| const originalText = button.innerHTML; | |
| button.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> Generating...'; | |
| button.disabled = true; | |
| setTimeout(() => { | |
| button.innerHTML = originalText; | |
| button.disabled = false; | |
| alert('β Custom report generated successfully!\n\nThe report has been added to your saved reports and is ready for export.'); | |
| }, 2000); | |
| } | |
| function previewReport() { | |
| alert('π Report Preview\n\nThis would open a preview of your custom report in a new window or modal.'); | |
| } | |
| function createNewReport() { | |
| const button = event.target; | |
| const originalText = button.innerHTML; | |
| button.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> Creating...'; | |
| button.disabled = true; | |
| setTimeout(() => { | |
| button.innerHTML = originalText; | |
| button.disabled = false; | |
| closeModal('new-report-modal'); | |
| alert('β New report template created!\n\nYou can now start building your custom report by dragging components to the canvas.'); | |
| }, 1500); | |
| } | |
| function runReport(reportId) { | |
| const button = event.target; | |
| const originalText = button.innerHTML; | |
| button.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> Running...'; | |
| button.disabled = true; | |
| setTimeout(() => { | |
| button.innerHTML = originalText; | |
| button.disabled = false; | |
| alert(`β Report "${reportId}" executed successfully!\n\nThe report has been generated and is ready for review.`); | |
| }, 2000); | |
| } | |
| function deleteReport(reportId) { | |
| if (confirm(`Are you sure you want to delete the "${reportId}" report? This action cannot be undone.`)) { | |
| const button = event.target; | |
| const row = button.closest('tr'); | |
| button.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i>'; | |
| button.disabled = true; | |
| setTimeout(() => { | |
| row.style.opacity = '0.5'; | |
| setTimeout(() => { | |
| row.remove(); | |
| alert(`β Report "${reportId}" has been deleted.`); | |
| }, 300); | |
| }, 1000); | |
| } | |
| } | |
| function saveReportConfig() { | |
| const button = event.target; | |
| const originalText = button.innerHTML; | |
| button.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> Saving...'; | |
| button.disabled = true; | |
| setTimeout(() => { | |
| button.innerHTML = originalText; | |
| button.disabled = false; | |
| closeSlideOut(); | |
| alert('β Report configuration saved successfully!'); | |
| }, 1500); | |
| } | |
| function testReport() { | |
| const button = event.target; | |
| const originalText = button.innerHTML; | |
| button.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> Testing...'; | |
| button.disabled = true; | |
| setTimeout(() => { | |
| button.innerHTML = originalText; | |
| button.disabled = false; | |
| alert('β Report test completed successfully!\n\nThe test report has been generated and sent to your email for review.'); | |
| }, 2000); | |
| } | |
| // Search functionality | |
| function initializeSearch() { | |
| const searchInput = document.querySelector('input[placeholder="Search reports..."]'); | |
| if (searchInput) { | |
| searchInput.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| const query = this.value.trim(); | |
| if (query) { | |
| performSearch(query); | |
| } | |
| } | |
| }); | |
| } | |
| } | |
| function performSearch(query) { | |
| console.log('Searching for:', query); | |
| // In a real app, this would make an API call | |
| alert(`Searching for: ${query}\n\nThis would show search results in a real application.`); | |
| } | |
| // Initialize search on load | |
| initializeSearch(); |