let allIssues = []; let ws = null; let wsConnected = false; // WebSocket connection function connectWebSocket() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = protocol + '//' + window.location.host + '/ws'; ws = new WebSocket(wsUrl); ws.onopen = function() { console.log('WebSocket connected'); wsConnected = true; updateConnectionStatus(true); }; ws.onmessage = function(event) { console.log('WebSocket message:', event.data); const mutation = JSON.parse(event.data); handleMutation(mutation); }; ws.onerror = function(error) { console.error('WebSocket error:', error); wsConnected = false; updateConnectionStatus(false); }; ws.onclose = function() { console.log('WebSocket disconnected'); wsConnected = false; updateConnectionStatus(false); // Reconnect after 5 seconds setTimeout(connectWebSocket, 5000); }; } // Update connection status indicator function updateConnectionStatus(connected) { const statusEl = document.getElementById('connection-status'); const dotEl = document.getElementById('connection-dot'); const textEl = document.getElementById('connection-text'); if (connected) { statusEl.className = 'connection-status connected'; dotEl.className = 'connection-dot connected'; textEl.textContent = 'Connected'; } else { statusEl.className = 'connection-status disconnected'; dotEl.className = 'connection-dot disconnected'; textEl.textContent = 'Disconnected'; } } // Show/hide loading overlay function setLoading(isLoading) { const overlay = document.getElementById('loading-overlay'); if (isLoading) { overlay.classList.add('active'); } else { overlay.classList.remove('active'); } } // Show error message function showError(message) { const errorEl = document.getElementById('error-message'); errorEl.textContent = message; errorEl.classList.add('active'); setTimeout(() => { errorEl.classList.remove('active'); }, 5000); } // Handle mutation event function handleMutation(mutation) { console.log('Mutation:', mutation.type, mutation.issue_id); // Refresh data on mutation loadStats(); loadIssues(); } // Load statistics async function loadStats() { try { const response = await fetch('/api/stats'); if (!response.ok) throw new Error('Failed to load statistics'); const stats = await response.json(); document.getElementById('stat-total').textContent = stats.total_issues || 0; document.getElementById('stat-in-progress').textContent = stats.in_progress_issues || 0; document.getElementById('stat-open').textContent = stats.open_issues || 0; document.getElementById('stat-closed').textContent = stats.closed_issues || 0; } catch (error) { console.error('Error loading statistics:', error); showError('Failed to load statistics: ' + error.message); } } // Load all issues async function loadIssues() { try { const response = await fetch('/api/issues'); if (!response.ok) throw new Error('Failed to load issues'); allIssues = await response.json(); renderIssues(allIssues); } catch (error) { console.error('Error loading issues:', error); showError('Failed to load issues: ' + error.message); document.getElementById('issues-tbody').innerHTML = '
Error loading issues
Create your first issue to get started!
Status: ' + issue.status + '
'; html += 'Priority: P' + issue.priority + '
'; html += 'Type: ' + issue.issue_type + '
'; html += 'Assignee: ' + (issue.assignee || 'Unassigned') + '
'; html += 'Created: ' + new Date(issue.created_at).toLocaleString() + '
'; html += 'Updated: ' + new Date(issue.updated_at).toLocaleString() + '
'; if (issue.description) html += '' + issue.description + ''; if (issue.design) html += '
' + issue.design + ''; if (issue.notes) html += '
' + issue.notes + ''; if (issue.labels && issue.labels.length > 0) html += '
Labels: ' + issue.labels.join(', ') + '
'; modalBody.innerHTML = html; } catch (error) { console.error('Error loading issue details:', error); showError('Failed to load issue details: ' + error.message); modalBody.innerHTML = 'Error loading issue details