fix(analytics): 4 UX polish fixes — resolution time column, empty states, type badges, select styling

- CoverageHeatmap: add Avg Resolution column with inverse color thresholds (green <10 min, amber 10–20 min, red >20 min); updated colSpan to 7
- FlowPilotAnalyticsPage: show positive empty state when knowledge gaps list is empty instead of rendering nothing
- FlowQualityTable: add tree_type badge next to flow name (Troubleshooting/Project/Maintenance with distinct colors)
- FlowPilotAnalyticsPage: add [&>option] Tailwind classes to period select for improved dark-theme contrast in Chrome/Firefox

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-20 01:07:40 +00:00
parent acf98d3a01
commit 7bbcf0face
3 changed files with 43 additions and 3 deletions

View File

@@ -85,6 +85,10 @@ export default function CoverageHeatmap({ data }: CoverageHeatmapProps) {
<th className="px-3 py-2 text-right font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
Guided %
</th>
<th className="px-3 py-2 text-right font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"
title="Average time to resolve sessions in this domain. Green: &lt;10 min, Amber: 1020 min, Red: &gt;20 min. Lower is better.">
Avg Resolution
</th>
</tr>
</thead>
<tbody>
@@ -138,13 +142,32 @@ export default function CoverageHeatmap({ data }: CoverageHeatmapProps) {
{(row.guided_rate * 100).toFixed(1)}%
</span>
</td>
<td className="px-3 py-2 text-sm text-right">
{row.avg_resolution_minutes == null ? (
<span className="text-muted-foreground"></span>
) : (
<span
className={cn(
'inline-block rounded px-1.5 py-0.5 text-xs font-medium',
row.avg_resolution_minutes < 10
? 'bg-emerald-400/10 text-emerald-400'
: row.avg_resolution_minutes <= 20
? 'bg-amber-400/10 text-amber-400'
: 'bg-rose-500/10 text-rose-500',
)}
title={`Avg resolution: ${row.avg_resolution_minutes.toFixed(1)} min — ${row.avg_resolution_minutes < 10 ? 'Good (<10 min)' : row.avg_resolution_minutes <= 20 ? 'Needs Improvement (1020 min)' : 'Slow (>20 min)'}`}
>
{row.avg_resolution_minutes.toFixed(1)} min
</span>
)}
</td>
</tr>
))}
</tbody>
{data.unmapped_session_count > 0 && (
<tfoot>
<tr>
<td colSpan={6} className="px-3 py-2 text-xs text-muted-foreground">
<td colSpan={7} className="px-3 py-2 text-xs text-muted-foreground">
{data.unmapped_session_count} sessions had no domain classification
</td>
</tr>

View File

@@ -12,6 +12,12 @@ interface FlowQualityTableProps {
type SortColumn = 'name' | 'usage_count' | 'success_rate' | 'last_matched_at' | 'avg_confidence' | 'quality_score'
type SortDir = 'asc' | 'desc'
const TYPE_LABELS: Record<string, { label: string; color: string }> = {
troubleshooting: { label: 'Troubleshooting', color: 'text-primary' },
procedural: { label: 'Project', color: 'text-amber-400' },
maintenance: { label: 'Maintenance', color: 'text-blue-400' },
}
function formatRelativeTime(dateStr: string | null): string {
if (!dateStr) return 'Never'
const diff = Date.now() - new Date(dateStr).getTime()
@@ -215,6 +221,9 @@ function FlowRow({
>
{flow.name}
</Link>
<span className={cn('font-label text-[0.5rem] uppercase tracking-wider ml-2 shrink-0', TYPE_LABELS[flow.tree_type]?.color || 'text-muted-foreground')}>
{TYPE_LABELS[flow.tree_type]?.label || flow.tree_type}
</span>
{needsAttention && (
<span className="shrink-0 bg-amber-400/10 text-amber-400 font-label text-[0.625rem] px-1.5 py-0.5 rounded">
Needs attention

View File

@@ -179,7 +179,7 @@ export default function FlowPilotAnalyticsPage() {
<select
value={period}
onChange={(e) => setPeriod(e.target.value)}
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-hidden focus:ring-1 focus:ring-primary/20"
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-hidden focus:ring-1 focus:ring-primary/20 [&>option]:bg-[#1a1c21] [&>option]:text-foreground"
>
{PERIOD_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
@@ -390,7 +390,7 @@ export default function FlowPilotAnalyticsPage() {
</div>
{/* Fourth row — Knowledge Gaps */}
{gaps && gaps.gaps.length > 0 && (
{gaps && gaps.gaps.length > 0 ? (
<div className="space-y-3">
<div className="flex items-center gap-2 px-1">
<AlertTriangle size={14} className="text-amber-400" />
@@ -404,6 +404,14 @@ export default function FlowPilotAnalyticsPage() {
))}
</div>
</div>
) : gaps && (
<div className="glass-card-static p-4 flex items-center gap-3">
<CheckCircle2 size={20} className="text-emerald-400 shrink-0" />
<div>
<p className="text-sm text-foreground">No knowledge gaps detected</p>
<p className="text-xs text-muted-foreground">Your flow coverage looks healthy for this period.</p>
</div>
</div>
)}
</div>
)}