Skip to content

Commit 1ecb692

Browse files
EmilienMclaude
andauthored
fix: replace broken Pipeline Breakdown with Triage Outcomes + Autofix Progress (#49)
The Pipeline Breakdown widget had two bugs: PIPELINE_BAR_SEGMENTS only covered 6 of 15+ pipeline states (issues in triage states showed as a grey gap), and it used unfiltered data so the timeline filter had no effect. Replace with two side-by-side widgets that cover all states and respect the timeline filter via the existing computeTeamMetrics() output. Both widgets include "?" tooltips explaining each state. Remove the now-unused PIPELINE_BAR_SEGMENTS constant. Signed-off-by: Emilien Macchi <emacchi@redhat.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 80ae172 commit 1ecb692

2 files changed

Lines changed: 127 additions & 41 deletions

File tree

modules/team-tracker/client/components/autofix/TeamAutofixTab.vue

Lines changed: 127 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -74,24 +74,103 @@
7474
</div>
7575
</div>
7676

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>
89125
</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>
95174
</div>
96175
</div>
97176

@@ -357,7 +436,6 @@ import { useModules } from '../../../../../src/composables/useModules'
357436
import { fetchAutofixData } from '../../services/autofix-api.js'
358437
import {
359438
STATE_OPTIONS,
360-
PIPELINE_BAR_SEGMENTS,
361439
stateLabel,
362440
stateColorClass,
363441
effortTierColorClass,
@@ -476,22 +554,39 @@ const successRateTooltip = computed(() => {
476554
return `${metrics.value.autofixStates.merged} merged / ${metrics.value.terminalTotal} terminal`
477555
})
478556
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)
493586
})
494587
588+
const autofixSegmentTotal = computed(() => autofixSegments.value.reduce((s, v) => s + v.count, 0))
589+
495590
const PRIORITY_COLORS = {
496591
Blocker: { bar: 'bg-red-500', text: 'text-red-600 dark:text-red-400' },
497592
Critical: { bar: 'bg-orange-500', text: 'text-orange-600 dark:text-orange-400' },

modules/team-tracker/client/components/autofix/autofix-constants.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,6 @@ export const STATE_OPTIONS = [
2222
{ value: 'autofix-blocked', label: 'AI Blocked' }
2323
]
2424

25-
export const PIPELINE_BAR_SEGMENTS = [
26-
{ state: 'autofix-merged', label: 'Merged', color: 'bg-indigo-500' },
27-
{ state: 'autofix-review', label: 'Review', color: 'bg-blue-500' },
28-
{ state: 'autofix-pending', label: 'Pending', color: 'bg-gray-400' },
29-
{ state: 'autofix-ci-failing', label: 'CI Failing', color: 'bg-orange-500' },
30-
{ state: 'autofix-blocked', label: 'Blocked', color: 'bg-yellow-500' },
31-
{ state: 'autofix-max-retries', label: 'Max Retries', color: 'bg-red-500' }
32-
]
33-
3425
export function stateLabel(state) {
3526
const opt = STATE_OPTIONS.find(o => o.value === state)
3627
return opt ? opt.label : state

0 commit comments

Comments
 (0)