/** * Prism Console - Merge Dashboard * * Real-time dashboard for PR and merge queue management. */ class MergeDashboard { constructor(apiBaseUrl = '/api/operator') { this.apiBaseUrl = apiBaseUrl; this.prs = new Map(); this.queueStats = {}; this.refreshInterval = null; this.refreshRate = 5000; // 5 seconds } /** * Initialize the dashboard */ async init() { console.log('[Prism] Initializing Merge Dashboard...'); // Load initial data await this.refresh(); // Start auto-refresh this.startAutoRefresh(); // Setup event listeners this.setupEventListeners(); console.log('[Prism] Merge Dashboard initialized'); } /** * Start auto-refresh timer */ startAutoRefresh() { if (this.refreshInterval) { clearInterval(this.refreshInterval); } this.refreshInterval = setInterval(() => { this.refresh(); }, this.refreshRate); console.log(`[Prism] Auto-refresh started (${this.refreshRate}ms)`); } /** * Stop auto-refresh timer */ stopAutoRefresh() { if (this.refreshInterval) { clearInterval(this.refreshInterval); this.refreshInterval = null; console.log('[Prism] Auto-refresh stopped'); } } /** * Refresh all data */ async refresh() { try { await Promise.all([ this.fetchQueueStats(), this.fetchActivePRs(), ]); this.render(); } catch (error) { console.error('[Prism] Refresh error:', error); this.showError('Failed to refresh data'); } } /** * Fetch queue statistics */ async fetchQueueStats() { const response = await fetch(`${this.apiBaseUrl}/queue/stats`); if (!response.ok) throw new Error('Failed to fetch queue stats'); this.queueStats = await response.json(); console.log('[Prism] Queue stats:', this.queueStats); } /** * Fetch active PRs * (In production, this would come from GitHub API or a database) */ async fetchActivePRs() { // TODO: Implement actual PR fetching // For now, return mock data this.prs = new Map([ [1, { number: 1, title: 'feat: Phase Q2 — PR Action Intelligence', repo: 'BlackRoad-Operating-System', owner: 'blackboxprogramming', status: 'open', checks: 'passing', labels: ['claude-auto', 'backend', 'core'], queueStatus: 'queued', }], ]); } /** * Fetch actions for a specific PR */ async fetchPRActions(owner, repo, prNumber) { const response = await fetch( `${this.apiBaseUrl}/queue/pr/${owner}/${repo}/${prNumber}` ); if (!response.ok) throw new Error('Failed to fetch PR actions'); return await response.json(); } /** * Trigger a PR action */ async triggerAction(actionType, owner, repo, prNumber, params = {}) { try { // This would call an API endpoint to enqueue the action console.log(`[Prism] Triggering ${actionType} for ${owner}/${repo}#${prNumber}`); const response = await fetch(`${this.apiBaseUrl}/queue/enqueue`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ action_type: actionType, repo_owner: owner, repo_name: repo, pr_number: prNumber, params: params, }), }); if (!response.ok) throw new Error('Failed to enqueue action'); const result = await response.json(); console.log('[Prism] Action queued:', result); this.showSuccess(`Action ${actionType} queued successfully`); await this.refresh(); return result; } catch (error) { console.error('[Prism] Action trigger error:', error); this.showError(`Failed to trigger ${actionType}`); throw error; } } /** * Render the dashboard */ render() { this.renderQueueStats(); this.renderPRList(); } /** * Render queue statistics */ renderQueueStats() { const statsContainer = document.getElementById('queue-stats'); if (!statsContainer) return; const { queued, processing, completed, failed, running } = this.queueStats; statsContainer.innerHTML = `
${action.action_type}