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:
@@ -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: <10 min, Amber: 10–20 min, Red: >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 (10–20 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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user