/** * ds-quick-wins.js * Identifies low-effort, high-impact opportunities for design system adoption * UI Team Tool #5 */ import { ComponentHelpers } from '../../utils/component-helpers.js'; import contextStore from '../../stores/context-store.js'; import toolBridge from '../../services/tool-bridge.js'; class DSQuickWins extends HTMLElement { constructor() { super(); this.quickWins = null; this.isAnalyzing = false; } async connectedCallback() { this.render(); this.setupEventListeners(); await this.loadCachedResults(); } async loadCachedResults() { try { const context = contextStore.getMCPContext(); if (!context.project_id) return; const cached = localStorage.getItem(`quickwins_${context.project_id}`); if (cached) { this.quickWins = JSON.parse(cached); this.renderResults(); } } catch (error) { console.error('[DSQuickWins] Failed to load cached results:', error); } } setupEventListeners() { const analyzeBtn = this.querySelector('#analyze-quick-wins-btn'); const pathInput = this.querySelector('#project-path-input'); if (analyzeBtn) { analyzeBtn.addEventListener('click', () => this.analyzeQuickWins()); } if (pathInput) { pathInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { this.analyzeQuickWins(); } }); } } async analyzeQuickWins() { const pathInput = this.querySelector('#project-path-input'); const projectPath = pathInput?.value.trim() || ''; if (!projectPath) { ComponentHelpers.showToast?.('Please enter a project path', 'error'); return; } this.isAnalyzing = true; this.updateLoadingState(); try { // Call dss_find_quick_wins MCP tool const result = await toolBridge.executeTool('dss_find_quick_wins', { path: projectPath }); this.quickWins = result; // Cache results const context = contextStore.getMCPContext(); if (context.project_id) { localStorage.setItem(`quickwins_${context.project_id}`, JSON.stringify(result)); } this.renderResults(); ComponentHelpers.showToast?.('Quick wins analysis complete', 'success'); } catch (error) { console.error('[DSQuickWins] Analysis failed:', error); ComponentHelpers.showToast?.(`Analysis failed: ${error.message}`, 'error'); const resultsContainer = this.querySelector('#results-container'); if (resultsContainer) { resultsContainer.innerHTML = ComponentHelpers.renderError('Quick wins analysis failed', error); } } finally { this.isAnalyzing = false; this.updateLoadingState(); } } updateLoadingState() { const analyzeBtn = this.querySelector('#analyze-quick-wins-btn'); const resultsContainer = this.querySelector('#results-container'); if (!analyzeBtn || !resultsContainer) return; if (this.isAnalyzing) { analyzeBtn.disabled = true; analyzeBtn.textContent = '⏳ Analyzing...'; resultsContainer.innerHTML = ComponentHelpers.renderLoading('Identifying quick win opportunities...'); } else { analyzeBtn.disabled = false; analyzeBtn.textContent = '⚡ Find Quick Wins'; } } renderResults() { const resultsContainer = this.querySelector('#results-container'); if (!resultsContainer || !this.quickWins) return; const opportunities = this.quickWins.opportunities || []; const totalImpact = opportunities.reduce((sum, opp) => sum + (opp.impact || 0), 0); const avgEffort = opportunities.length > 0 ? (opportunities.reduce((sum, opp) => sum + (opp.effort || 0), 0) / opportunities.length).toFixed(1) : 0; resultsContainer.innerHTML = `
${this.createStatCard('Opportunities', opportunities.length, '⚡')} ${this.createStatCard('Total Impact', `${totalImpact}%`, '📈')} ${this.createStatCard('Avg Effort', `${avgEffort}h`, '⏱️')}
${opportunities.length === 0 ? ComponentHelpers.renderEmpty('No quick wins found', '✨') : `
${opportunities.sort((a, b) => (b.impact || 0) - (a.impact || 0)).map((opp, idx) => `

${ComponentHelpers.escapeHtml(opp.title)}

${this.renderPriorityBadge(opp.priority || 'medium')}

${ComponentHelpers.escapeHtml(opp.description)}

Impact
${opp.impact || 0}%
Effort
${opp.effort || 0} hours
Files Affected
${opp.filesAffected || 0}
Type
${ComponentHelpers.escapeHtml(opp.type || 'refactor')}
${opp.files && opp.files.length > 0 ? `
Affected Files (${opp.files.length})
${opp.files.slice(0, 10).map(file => `
${ComponentHelpers.escapeHtml(file)}
`).join('')} ${opp.files.length > 10 ? `
...and ${opp.files.length - 10} more
` : ''}
` : ''}
`).join('')}
`}
`; // Setup button handlers const applyBtns = resultsContainer.querySelectorAll('.apply-quick-win-btn'); const viewBtns = resultsContainer.querySelectorAll('.view-files-btn'); applyBtns.forEach(btn => { btn.addEventListener('click', () => { const idx = parseInt(btn.dataset.idx); this.applyQuickWin(opportunities[idx]); }); }); viewBtns.forEach(btn => { btn.addEventListener('click', () => { const idx = parseInt(btn.dataset.idx); this.viewFiles(opportunities[idx]); }); }); } createStatCard(label, value, icon) { return `
${icon}
${value}
${label}
`; } renderPriorityBadge(priority) { const config = { high: { color: '#f48771', label: 'High Priority' }, medium: { color: '#ffbf00', label: 'Medium Priority' }, low: { color: '#89d185', label: 'Low Priority' } }; const { color, label } = config[priority] || config.medium; return `${label}`; } applyQuickWin(opportunity) { ComponentHelpers.showToast?.(`Applying: ${opportunity.title}`, 'info'); // In real implementation, this would trigger automated refactoring console.log('Apply quick win:', opportunity); } viewFiles(opportunity) { if (!opportunity.files || opportunity.files.length === 0) { ComponentHelpers.showToast?.('No files associated with this opportunity', 'info'); return; } console.log('View files:', opportunity.files); ComponentHelpers.showToast?.(`${opportunity.files.length} files affected`, 'info'); } render() { this.innerHTML = `

Quick Wins Identification

💡 Identifies low-effort, high-impact opportunities for design system adoption

Ready to Find Quick Wins

Enter your project path above to identify low-effort, high-impact improvements

`; } } customElements.define('ds-quick-wins', DSQuickWins); export default DSQuickWins;