|
74 | 74 | </div> |
75 | 75 | </div> |
76 | 76 |
|
77 | | - <!-- Pipeline Breakdown Bar --> |
78 | | - <div v-if="pipelineSegments.length > 0" class="mb-6"> |
79 | | - <h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Pipeline Breakdown</h4> |
80 | | - <div class="flex h-6 rounded-full overflow-hidden bg-gray-100 dark:bg-gray-700"> |
81 | | - <div |
82 | | - v-for="seg in pipelineSegments" |
83 | | - :key="seg.state" |
84 | | - :class="seg.color" |
85 | | - :style="{ width: seg.pct + '%' }" |
86 | | - :title="`${seg.label}: ${seg.count}`" |
87 | | - class="transition-all" |
88 | | - ></div> |
| 77 | + <!-- Triage Outcomes + Autofix Progress --> |
| 78 | + <div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6"> |
| 79 | + <!-- Triage Outcomes --> |
| 80 | + <div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-5"> |
| 81 | + <div class="flex items-center justify-between mb-4"> |
| 82 | + <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2"> |
| 83 | + Triage Outcomes |
| 84 | + <div class="relative group"> |
| 85 | + <svg class="h-3.5 w-3.5 text-gray-400 dark:text-gray-500 cursor-help" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 86 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> |
| 87 | + </svg> |
| 88 | + <div class="absolute left-0 top-6 z-20 hidden group-hover:block w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg dark:shadow-gray-900/50 p-3 text-xs text-gray-700 dark:text-gray-300"> |
| 89 | + <div class="space-y-1"> |
| 90 | + <div class="flex justify-between"><span class="font-medium">Ready for AI</span><span class="text-gray-400">Qualified for autofix</span></div> |
| 91 | + <div class="flex justify-between"><span class="font-medium">Missing Info</span><span class="text-gray-400">Waiting on reporter</span></div> |
| 92 | + <div class="flex justify-between"><span class="font-medium">Not AI-Fixable</span><span class="text-gray-400">Not suitable for AI</span></div> |
| 93 | + <div class="flex justify-between"><span class="font-medium">External Reporter</span><span class="text-gray-400">Needs RH approval</span></div> |
| 94 | + <div class="flex justify-between"><span class="font-medium">Security Review</span><span class="text-gray-400">Needs human review</span></div> |
| 95 | + <div class="flex justify-between"><span class="font-medium">Stale</span><span class="text-gray-400">No response 14+ days</span></div> |
| 96 | + <div class="flex justify-between"><span class="font-medium">AI Assessing</span><span class="text-gray-400">Bot is evaluating</span></div> |
| 97 | + </div> |
| 98 | + </div> |
| 99 | + </div> |
| 100 | + </h3> |
| 101 | + <span class="text-xs text-gray-400 dark:text-gray-500">{{ triageSegmentTotal }} issues</span> |
| 102 | + </div> |
| 103 | + <div class="flex h-6 rounded-full overflow-hidden bg-gray-100 dark:bg-gray-700 mb-4" v-if="triageSegmentTotal > 0"> |
| 104 | + <div |
| 105 | + v-for="seg in triageSegments" |
| 106 | + :key="seg.label" |
| 107 | + class="transition-all duration-500" |
| 108 | + :class="seg.color" |
| 109 | + :style="{ width: (seg.count / triageSegmentTotal * 100) + '%' }" |
| 110 | + :title="`${seg.label}: ${seg.count}`" |
| 111 | + /> |
| 112 | + </div> |
| 113 | + <div class="space-y-2.5"> |
| 114 | + <div v-for="seg in triageSegments" :key="seg.label" class="flex items-center justify-between"> |
| 115 | + <div class="flex items-center gap-2"> |
| 116 | + <span class="w-2.5 h-2.5 rounded-sm shrink-0" :class="seg.color" /> |
| 117 | + <span class="text-sm text-gray-600 dark:text-gray-300">{{ seg.label }}</span> |
| 118 | + </div> |
| 119 | + <div class="flex items-center gap-2"> |
| 120 | + <span class="text-sm font-semibold" :class="seg.textClass">{{ seg.count }}</span> |
| 121 | + <span class="text-xs text-gray-400 dark:text-gray-500 w-10 text-right">{{ triageSegmentTotal > 0 ? Math.round(seg.count / triageSegmentTotal * 100) : 0 }}%</span> |
| 122 | + </div> |
| 123 | + </div> |
| 124 | + </div> |
89 | 125 | </div> |
90 | | - <div class="flex flex-wrap gap-x-4 gap-y-1 mt-2 text-xs text-gray-500 dark:text-gray-400"> |
91 | | - <span v-for="seg in pipelineSegments" :key="seg.state" class="flex items-center gap-1"> |
92 | | - <span :class="seg.color" class="inline-block w-2.5 h-2.5 rounded-full"></span> |
93 | | - {{ seg.label }}: {{ seg.count }} |
94 | | - </span> |
| 126 | + |
| 127 | + <!-- Autofix Progress --> |
| 128 | + <div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-5"> |
| 129 | + <div class="flex items-center justify-between mb-4"> |
| 130 | + <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2"> |
| 131 | + Autofix Progress |
| 132 | + <div class="relative group"> |
| 133 | + <svg class="h-3.5 w-3.5 text-gray-400 dark:text-gray-500 cursor-help" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 134 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> |
| 135 | + </svg> |
| 136 | + <div class="absolute left-0 top-6 z-20 hidden group-hover:block w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg dark:shadow-gray-900/50 p-3 text-xs text-gray-700 dark:text-gray-300"> |
| 137 | + <div class="space-y-1"> |
| 138 | + <div class="flex justify-between"><span class="font-medium">AI Fix Merged</span><span class="text-gray-400">Fix landed</span></div> |
| 139 | + <div class="flex justify-between"><span class="font-medium">AI Fix Under Review</span><span class="text-gray-400">Human reviewing</span></div> |
| 140 | + <div class="flex justify-between"><span class="font-medium">AI Fix CI Failing</span><span class="text-gray-400">CI is red</span></div> |
| 141 | + <div class="flex justify-between"><span class="font-medium">AI Working</span><span class="text-gray-400">Generating fix</span></div> |
| 142 | + <div class="flex justify-between"><span class="font-medium">Queued for AI</span><span class="text-gray-400">Waiting for bot</span></div> |
| 143 | + <div class="flex justify-between"><span class="font-medium">AI Fix Rejected</span><span class="text-gray-400">MR closed</span></div> |
| 144 | + <div class="flex justify-between"><span class="font-medium">AI Max Retries</span><span class="text-gray-400">Bot gave up</span></div> |
| 145 | + <div class="flex justify-between"><span class="font-medium">AI Blocked</span><span class="text-gray-400">Needs human help</span></div> |
| 146 | + </div> |
| 147 | + </div> |
| 148 | + </div> |
| 149 | + </h3> |
| 150 | + <span class="text-xs text-gray-400 dark:text-gray-500">{{ autofixSegmentTotal }} issues</span> |
| 151 | + </div> |
| 152 | + <div class="flex h-6 rounded-full overflow-hidden bg-gray-100 dark:bg-gray-700 mb-4" v-if="autofixSegmentTotal > 0"> |
| 153 | + <div |
| 154 | + v-for="seg in autofixSegments" |
| 155 | + :key="seg.label" |
| 156 | + class="transition-all duration-500" |
| 157 | + :class="seg.color" |
| 158 | + :style="{ width: (seg.count / autofixSegmentTotal * 100) + '%' }" |
| 159 | + :title="`${seg.label}: ${seg.count}`" |
| 160 | + /> |
| 161 | + </div> |
| 162 | + <div class="space-y-2.5"> |
| 163 | + <div v-for="seg in autofixSegments" :key="seg.label" class="flex items-center justify-between"> |
| 164 | + <div class="flex items-center gap-2"> |
| 165 | + <span class="w-2.5 h-2.5 rounded-sm shrink-0" :class="seg.color" /> |
| 166 | + <span class="text-sm text-gray-600 dark:text-gray-300">{{ seg.label }}</span> |
| 167 | + </div> |
| 168 | + <div class="flex items-center gap-2"> |
| 169 | + <span class="text-sm font-semibold" :class="seg.textClass">{{ seg.count }}</span> |
| 170 | + <span class="text-xs text-gray-400 dark:text-gray-500 w-10 text-right">{{ autofixSegmentTotal > 0 ? Math.round(seg.count / autofixSegmentTotal * 100) : 0 }}%</span> |
| 171 | + </div> |
| 172 | + </div> |
| 173 | + </div> |
95 | 174 | </div> |
96 | 175 | </div> |
97 | 176 |
|
@@ -357,7 +436,6 @@ import { useModules } from '../../../../../src/composables/useModules' |
357 | 436 | import { fetchAutofixData } from '../../services/autofix-api.js' |
358 | 437 | import { |
359 | 438 | STATE_OPTIONS, |
360 | | - PIPELINE_BAR_SEGMENTS, |
361 | 439 | stateLabel, |
362 | 440 | stateColorClass, |
363 | 441 | effortTierColorClass, |
@@ -476,22 +554,39 @@ const successRateTooltip = computed(() => { |
476 | 554 | return `${metrics.value.autofixStates.merged} merged / ${metrics.value.terminalTotal} terminal` |
477 | 555 | }) |
478 | 556 |
|
479 | | -const pipelineSegments = computed(() => { |
480 | | - const total = teamIssues.value.length |
481 | | - if (total === 0) return [] |
482 | | - const counts = {} |
483 | | - for (const issue of teamIssues.value) { |
484 | | - counts[issue.pipelineState] = (counts[issue.pipelineState] || 0) + 1 |
485 | | - } |
486 | | - return PIPELINE_BAR_SEGMENTS |
487 | | - .map(seg => ({ |
488 | | - ...seg, |
489 | | - count: counts[seg.state] || 0, |
490 | | - pct: ((counts[seg.state] || 0) / total * 100) |
491 | | - })) |
492 | | - .filter(seg => seg.count > 0) |
| 557 | +const triageSegments = computed(() => { |
| 558 | + if (!metrics.value) return [] |
| 559 | + const v = metrics.value.triageVerdicts |
| 560 | + return [ |
| 561 | + { label: 'Ready for AI', count: v.ready || 0, color: 'bg-green-500', textClass: 'text-green-600 dark:text-green-400' }, |
| 562 | + { label: 'Missing Info', count: v.missingInfo || 0, color: 'bg-yellow-500', textClass: 'text-yellow-600 dark:text-yellow-400' }, |
| 563 | + { label: 'Not AI-Fixable', count: v.notFixable || 0, color: 'bg-red-500', textClass: 'text-red-600 dark:text-red-400' }, |
| 564 | + { label: 'External Reporter', count: v.external || 0, color: 'bg-purple-500', textClass: 'text-purple-600 dark:text-purple-400' }, |
| 565 | + { label: 'Security Review', count: v.securityReview || 0, color: 'bg-rose-500', textClass: 'text-rose-600 dark:text-rose-400' }, |
| 566 | + { label: 'Stale', count: v.stale || 0, color: 'bg-gray-400', textClass: 'text-gray-500 dark:text-gray-400' }, |
| 567 | + { label: 'AI Assessing', count: v.pending || 0, color: 'bg-gray-300 dark:bg-gray-600', textClass: 'text-gray-500 dark:text-gray-400' } |
| 568 | + ].filter(s => s.count > 0) |
| 569 | +}) |
| 570 | +
|
| 571 | +const triageSegmentTotal = computed(() => triageSegments.value.reduce((s, v) => s + v.count, 0)) |
| 572 | +
|
| 573 | +const autofixSegments = computed(() => { |
| 574 | + if (!metrics.value) return [] |
| 575 | + const a = metrics.value.autofixStates |
| 576 | + return [ |
| 577 | + { label: 'AI Fix Merged', count: a.merged || 0, color: 'bg-green-500', textClass: 'text-green-600 dark:text-green-400' }, |
| 578 | + { label: 'AI Fix Under Review', count: a.review || 0, color: 'bg-blue-500', textClass: 'text-blue-600 dark:text-blue-400' }, |
| 579 | + { label: 'AI Fix CI Failing', count: a.ciFailing || 0, color: 'bg-orange-500', textClass: 'text-orange-600 dark:text-orange-400' }, |
| 580 | + { label: 'AI Working', count: a.pending || 0, color: 'bg-indigo-500', textClass: 'text-indigo-600 dark:text-indigo-400' }, |
| 581 | + { label: 'Queued for AI', count: a.ready || 0, color: 'bg-gray-400', textClass: 'text-gray-500 dark:text-gray-400' }, |
| 582 | + { label: 'AI Fix Rejected', count: a.rejected || 0, color: 'bg-red-500', textClass: 'text-red-600 dark:text-red-400' }, |
| 583 | + { label: 'AI Max Retries', count: a.maxRetries || 0, color: 'bg-orange-500', textClass: 'text-orange-600 dark:text-orange-400' }, |
| 584 | + { label: 'AI Blocked', count: a.blocked || 0, color: 'bg-yellow-500', textClass: 'text-yellow-600 dark:text-yellow-400' } |
| 585 | + ].filter(s => s.count > 0) |
493 | 586 | }) |
494 | 587 |
|
| 588 | +const autofixSegmentTotal = computed(() => autofixSegments.value.reduce((s, v) => s + v.count, 0)) |
| 589 | +
|
495 | 590 | const PRIORITY_COLORS = { |
496 | 591 | Blocker: { bar: 'bg-red-500', text: 'text-red-600 dark:text-red-400' }, |
497 | 592 | Critical: { bar: 'bg-orange-500', text: 'text-orange-600 dark:text-orange-400' }, |
|
0 commit comments