refactor: migrate remaining components to Design System v4
111 files across 14 directories: common, tree-editor, kb-accelerator, copilot, assistant, analytics, library, procedural, procedural-editor, public, script-editor, ui, admin, step-library. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,12 +39,12 @@ function getFlowCountStyle(count: number) {
|
||||
export default function CoverageHeatmap({ data }: CoverageHeatmapProps) {
|
||||
if (data.domains.length === 0) {
|
||||
return (
|
||||
<div className="glass-card-static p-6">
|
||||
<div className="card-flat p-6">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<MapPin size={16} className="text-foreground" />
|
||||
<h3 className="font-heading text-sm font-semibold text-foreground">Domain Coverage</h3>
|
||||
<MapPin size={16} className="text-[#e2e5eb]" />
|
||||
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb]">Domain Coverage</h3>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<p className="text-sm text-[#848b9b]">
|
||||
No session data for this period. Start using FlowPilot to see coverage metrics.
|
||||
</p>
|
||||
</div>
|
||||
@@ -52,13 +52,13 @@ export default function CoverageHeatmap({ data }: CoverageHeatmapProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="glass-card-static p-3 sm:p-5">
|
||||
<div className="card-flat p-3 sm:p-5">
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<MapPin size={16} className="text-foreground" />
|
||||
<h3 className="font-heading text-sm font-semibold text-foreground">Domain Coverage</h3>
|
||||
<MapPin size={16} className="text-[#e2e5eb]" />
|
||||
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb]">Domain Coverage</h3>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
<p className="text-xs text-[#848b9b]">
|
||||
Resolution coverage and knowledge gaps by problem domain
|
||||
</p>
|
||||
</div>
|
||||
@@ -66,26 +66,26 @@ export default function CoverageHeatmap({ data }: CoverageHeatmapProps) {
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
<th className="px-3 py-2 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<tr className="border-b border-[#1e2130]">
|
||||
<th className="px-3 py-2 text-left font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
|
||||
Domain
|
||||
</th>
|
||||
<th className="px-3 py-2 text-right font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<th className="px-3 py-2 text-right font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
|
||||
Flows
|
||||
</th>
|
||||
<th className="px-3 py-2 text-right font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<th className="px-3 py-2 text-right font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
|
||||
Sessions
|
||||
</th>
|
||||
<th className="px-3 py-2 text-right font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<th className="px-3 py-2 text-right font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
|
||||
Resolution %
|
||||
</th>
|
||||
<th className="px-3 py-2 text-right font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<th className="px-3 py-2 text-right font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
|
||||
Escalation %
|
||||
</th>
|
||||
<th className="px-3 py-2 text-right font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<th className="px-3 py-2 text-right font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
|
||||
Guided %
|
||||
</th>
|
||||
<th className="px-3 py-2 text-right font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"
|
||||
<th className="px-3 py-2 text-right font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]"
|
||||
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>
|
||||
@@ -93,15 +93,15 @@ export default function CoverageHeatmap({ data }: CoverageHeatmapProps) {
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.domains.map((row) => (
|
||||
<tr key={row.domain} className="border-b border-border">
|
||||
<td className="px-3 py-2 text-sm text-foreground font-medium">
|
||||
<tr key={row.domain} className="border-b border-[#1e2130]">
|
||||
<td className="px-3 py-2 text-sm text-[#e2e5eb] font-medium">
|
||||
{row.domain}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm text-right">
|
||||
{row.flow_count === 0 ? (
|
||||
<Link
|
||||
to="/trees/new"
|
||||
className="inline-flex items-center gap-1 text-xs text-primary hover:underline"
|
||||
className="inline-flex items-center gap-1 text-xs text-[#22d3ee] hover:underline"
|
||||
>
|
||||
<Plus size={10} />
|
||||
Create Flow
|
||||
@@ -115,7 +115,7 @@ export default function CoverageHeatmap({ data }: CoverageHeatmapProps) {
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm text-right text-muted-foreground">
|
||||
<td className="px-3 py-2 text-sm text-right text-[#848b9b]">
|
||||
{row.session_count}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm text-right">
|
||||
@@ -144,7 +144,7 @@ export default function CoverageHeatmap({ data }: CoverageHeatmapProps) {
|
||||
</td>
|
||||
<td className="px-3 py-2 text-sm text-right">
|
||||
{row.avg_resolution_minutes == null ? (
|
||||
<span className="text-muted-foreground">—</span>
|
||||
<span className="text-[#848b9b]">—</span>
|
||||
) : (
|
||||
<span
|
||||
className={cn(
|
||||
@@ -167,7 +167,7 @@ export default function CoverageHeatmap({ data }: CoverageHeatmapProps) {
|
||||
{data.unmapped_session_count > 0 && (
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colSpan={7} className="px-3 py-2 text-xs text-muted-foreground">
|
||||
<td colSpan={7} className="px-3 py-2 text-xs text-[#848b9b]">
|
||||
{data.unmapped_session_count} sessions had no domain classification
|
||||
</td>
|
||||
</tr>
|
||||
@@ -176,7 +176,7 @@ export default function CoverageHeatmap({ data }: CoverageHeatmapProps) {
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-4 mt-3 text-[0.625rem] font-label text-muted-foreground">
|
||||
<div className="flex flex-wrap gap-4 mt-3 text-[0.625rem] font-sans text-xs text-[#848b9b]">
|
||||
<span><span className="inline-block w-2 h-2 rounded-full bg-emerald-400 mr-1" />Good</span>
|
||||
<span><span className="inline-block w-2 h-2 rounded-full bg-amber-400 mr-1" />Needs Improvement</span>
|
||||
<span><span className="inline-block w-2 h-2 rounded-full bg-rose-500 mr-1" />Critical</span>
|
||||
|
||||
@@ -54,14 +54,14 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
||||
<Loader2 className="h-6 w-6 animate-spin text-[#848b9b]" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="text-center py-12 text-muted-foreground">
|
||||
<div className="text-center py-12 text-[#848b9b]">
|
||||
No analytics data available for this flow.
|
||||
</div>
|
||||
)
|
||||
@@ -73,11 +73,11 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) {
|
||||
<div className="space-y-6">
|
||||
{/* Period selector */}
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-lg font-semibold text-foreground">Flow Analytics</h3>
|
||||
<h3 className="text-lg font-semibold text-[#e2e5eb]">Flow Analytics</h3>
|
||||
<select
|
||||
value={period}
|
||||
onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)}
|
||||
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-ring"
|
||||
className="rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] focus:outline-hidden focus:ring-1 focus:ring-ring"
|
||||
>
|
||||
{PERIOD_OPTIONS.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
@@ -107,8 +107,8 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) {
|
||||
|
||||
{/* Area chart - Sessions over time */}
|
||||
{time_series.length > 0 && (
|
||||
<div className="bg-card border border-border rounded-xl p-4">
|
||||
<p className="text-sm font-semibold text-foreground mb-3">Sessions Over Time</p>
|
||||
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4">
|
||||
<p className="text-sm font-semibold text-[#e2e5eb] mb-3">Sessions Over Time</p>
|
||||
<ResponsiveContainer width="100%" height={180}>
|
||||
<AreaChart data={time_series}>
|
||||
<CartesianGrid
|
||||
@@ -192,7 +192,7 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) {
|
||||
className="h-2 w-2 rounded-full"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
<span className="text-xs text-muted-foreground capitalize">
|
||||
<span className="text-xs text-[#848b9b] capitalize">
|
||||
{key}
|
||||
</span>
|
||||
</div>
|
||||
@@ -203,16 +203,16 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) {
|
||||
|
||||
{/* Step Feedback Table with Dropoff Metrics */}
|
||||
{step_feedback.length > 0 && (
|
||||
<div className="bg-card border border-border rounded-xl p-4">
|
||||
<p className="text-sm font-semibold text-foreground mb-3">Step Performance</p>
|
||||
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4">
|
||||
<p className="text-sm font-semibold text-[#e2e5eb] mb-3">Step Performance</p>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
<th className="text-left py-2 pr-4 text-foreground font-medium">Step</th>
|
||||
<th className="text-right py-2 pr-4 text-foreground font-medium">Visits</th>
|
||||
<th className="text-right py-2 pr-4 text-foreground font-medium">Dropoffs</th>
|
||||
<th className="text-right py-2 text-foreground font-medium">Dropoff Rate</th>
|
||||
<tr className="border-b border-[#1e2130]">
|
||||
<th className="text-left py-2 pr-4 text-[#e2e5eb] font-medium">Step</th>
|
||||
<th className="text-right py-2 pr-4 text-[#e2e5eb] font-medium">Visits</th>
|
||||
<th className="text-right py-2 pr-4 text-[#e2e5eb] font-medium">Dropoffs</th>
|
||||
<th className="text-right py-2 text-[#e2e5eb] font-medium">Dropoff Rate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -220,23 +220,23 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) {
|
||||
<tr
|
||||
key={step.node_id}
|
||||
className={cn(
|
||||
'border-b border-border last:border-0',
|
||||
'border-b border-[#1e2130] last:border-0',
|
||||
step.dropoff_rate > 0.2 && 'bg-red-400/5'
|
||||
)}
|
||||
>
|
||||
<td className="py-2 pr-4 text-muted-foreground truncate max-w-[200px]">
|
||||
<td className="py-2 pr-4 text-[#848b9b] truncate max-w-[200px]">
|
||||
{step.node_title}
|
||||
</td>
|
||||
<td className="py-2 pr-4 text-right text-muted-foreground">
|
||||
<td className="py-2 pr-4 text-right text-[#848b9b]">
|
||||
{step.visit_count}
|
||||
</td>
|
||||
<td className="py-2 pr-4 text-right text-muted-foreground">
|
||||
<td className="py-2 pr-4 text-right text-[#848b9b]">
|
||||
{step.dropoff_count}
|
||||
</td>
|
||||
<td
|
||||
className={cn(
|
||||
'py-2 text-right font-medium',
|
||||
step.dropoff_rate > 0.2 ? 'text-red-400' : 'text-muted-foreground'
|
||||
step.dropoff_rate > 0.2 ? 'text-red-400' : 'text-[#848b9b]'
|
||||
)}
|
||||
>
|
||||
{(step.dropoff_rate * 100).toFixed(1)}%
|
||||
@@ -251,13 +251,13 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) {
|
||||
|
||||
{/* Recent Comments (Anonymous) */}
|
||||
{recent_comments.length > 0 && (
|
||||
<div className="bg-card border border-border rounded-xl p-4">
|
||||
<p className="text-sm font-semibold text-foreground mb-3">Recent Feedback</p>
|
||||
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4">
|
||||
<p className="text-sm font-semibold text-[#e2e5eb] mb-3">Recent Feedback</p>
|
||||
<div className="space-y-3">
|
||||
{recent_comments.map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-start gap-3 border-b border-border/50 pb-3 last:border-0 last:pb-0"
|
||||
className="flex items-start gap-3 border-b border-[#1e2130]/50 pb-3 last:border-0 last:pb-0"
|
||||
>
|
||||
<div className="flex items-center gap-0.5 shrink-0 pt-0.5">
|
||||
{[1, 2, 3, 4, 5].map((v) => (
|
||||
@@ -267,16 +267,16 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) {
|
||||
className={cn(
|
||||
v <= item.rating
|
||||
? 'fill-yellow-400 text-yellow-400'
|
||||
: 'fill-none text-muted-foreground'
|
||||
: 'fill-none text-[#848b9b]'
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
{item.comment && (
|
||||
<p className="text-sm text-foreground">{item.comment}</p>
|
||||
<p className="text-sm text-[#e2e5eb]">{item.comment}</p>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
<p className="text-xs text-[#848b9b] mt-0.5">
|
||||
{new Date(item.created_at).toLocaleDateString()}
|
||||
</p>
|
||||
</div>
|
||||
@@ -299,11 +299,11 @@ function StatCard({
|
||||
subtitle?: string
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-xl p-4">
|
||||
<p className="text-xs text-muted-foreground">{label}</p>
|
||||
<p className="text-xl font-bold text-foreground mt-1">{value}</p>
|
||||
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4">
|
||||
<p className="text-xs text-[#848b9b]">{label}</p>
|
||||
<p className="text-xl font-bold text-[#e2e5eb] mt-1">{value}</p>
|
||||
{subtitle && (
|
||||
<p className="text-xs text-muted-foreground mt-0.5">{subtitle}</p>
|
||||
<p className="text-xs text-[#848b9b] mt-0.5">{subtitle}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ type SortColumn = 'name' | 'usage_count' | 'success_rate' | 'last_matched_at' |
|
||||
type SortDir = 'asc' | 'desc'
|
||||
|
||||
const TYPE_LABELS: Record<string, { label: string; color: string }> = {
|
||||
troubleshooting: { label: 'Troubleshooting', color: 'text-primary' },
|
||||
troubleshooting: { label: 'Troubleshooting', color: 'text-[#22d3ee]' },
|
||||
procedural: { label: 'Project', color: 'text-amber-400' },
|
||||
maintenance: { label: 'Maintenance', color: 'text-blue-400' },
|
||||
}
|
||||
@@ -33,7 +33,7 @@ function formatRelativeTime(dateStr: string | null): string {
|
||||
}
|
||||
|
||||
function rateColor(value: number | null): string {
|
||||
if (value === null) return 'text-muted-foreground'
|
||||
if (value === null) return 'text-[#848b9b]'
|
||||
if (value > 75) return 'text-emerald-400'
|
||||
if (value >= 50) return 'text-amber-400'
|
||||
return 'text-rose-500'
|
||||
@@ -110,9 +110,9 @@ export default function FlowQualityTable({ data }: FlowQualityTableProps) {
|
||||
|
||||
if (data.flows.length === 0) {
|
||||
return (
|
||||
<div className="glass-card-static p-3 sm:p-5">
|
||||
<div className="card-flat p-3 sm:p-5">
|
||||
<div className="flex items-center justify-center min-h-[200px]">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<p className="text-sm text-[#848b9b]">
|
||||
No flows found for this account. Create your first flow to start tracking quality.
|
||||
</p>
|
||||
</div>
|
||||
@@ -130,20 +130,20 @@ export default function FlowQualityTable({ data }: FlowQualityTableProps) {
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="glass-card-static p-3 sm:p-5">
|
||||
<div className="card-flat p-3 sm:p-5">
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="font-heading text-sm font-semibold text-foreground">
|
||||
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb]">
|
||||
Flow Quality Scores
|
||||
</h3>
|
||||
<span className="group relative">
|
||||
<Info size={14} className="text-muted-foreground cursor-help" />
|
||||
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 w-64 p-2 rounded-lg bg-card border border-border text-xs text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-10">
|
||||
<Info size={14} className="text-[#848b9b] cursor-help" />
|
||||
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 w-64 p-2 rounded-lg bg-[#14161d] border border-[#1e2130] text-xs text-[#848b9b] opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-10">
|
||||
Quality score combines success rate (50%), AI confidence level (30%), and recent usage (20%). Higher is better.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
<p className="text-xs text-[#848b9b] mt-1">
|
||||
Performance and usage metrics for your troubleshooting flows
|
||||
</p>
|
||||
</div>
|
||||
@@ -151,7 +151,7 @@ export default function FlowQualityTable({ data }: FlowQualityTableProps) {
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
<tr className="border-b border-[#1e2130]">
|
||||
{columns.map((col) => {
|
||||
const isActive = sortCol === col.key
|
||||
const SortIcon = isActive
|
||||
@@ -160,7 +160,7 @@ export default function FlowQualityTable({ data }: FlowQualityTableProps) {
|
||||
return (
|
||||
<th
|
||||
key={col.key}
|
||||
className="text-left py-2 px-2 font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground cursor-pointer select-none hover:text-foreground transition-colors"
|
||||
className="text-left py-2 px-2 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] cursor-pointer select-none hover:text-[#e2e5eb] transition-colors"
|
||||
onClick={() => handleSort(col.key)}
|
||||
title={col.title}
|
||||
>
|
||||
@@ -207,7 +207,7 @@ function FlowRow({
|
||||
return (
|
||||
<tr
|
||||
className={cn(
|
||||
'border-b border-border/50 hover:bg-card/30 transition-colors',
|
||||
'border-b border-[#1e2130]/50 hover:bg-[#14161d]/30 transition-colors',
|
||||
isTopPerformer && 'border-l-2 border-l-emerald-400',
|
||||
needsAttention && 'border-l-2 border-l-rose-500',
|
||||
)}
|
||||
@@ -217,15 +217,15 @@ function FlowRow({
|
||||
<div className="flex items-center gap-2">
|
||||
<Link
|
||||
to={getTreeEditorPath(flow.flow_id, flow.tree_type)}
|
||||
className="text-foreground hover:text-primary transition-colors font-medium truncate max-w-[200px]"
|
||||
className="text-[#e2e5eb] hover:text-[#22d3ee] transition-colors font-medium truncate max-w-[200px]"
|
||||
>
|
||||
{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')}>
|
||||
<span className={cn('font-sans text-xs text-[0.5rem] uppercase tracking-wider ml-2 shrink-0', TYPE_LABELS[flow.tree_type]?.color || 'text-[#848b9b]')}>
|
||||
{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">
|
||||
<span className="shrink-0 bg-amber-400/10 text-amber-400 font-sans text-xs text-[0.625rem] px-1.5 py-0.5 rounded">
|
||||
Needs attention
|
||||
</span>
|
||||
)}
|
||||
@@ -233,7 +233,7 @@ function FlowRow({
|
||||
</td>
|
||||
|
||||
{/* Usage */}
|
||||
<td className="py-2.5 px-2 text-foreground">{flow.usage_count}</td>
|
||||
<td className="py-2.5 px-2 text-[#e2e5eb]">{flow.usage_count}</td>
|
||||
|
||||
{/* Success Rate */}
|
||||
<td className={cn('py-2.5 px-2', rateColor(flow.success_rate))}>
|
||||
@@ -241,7 +241,7 @@ function FlowRow({
|
||||
</td>
|
||||
|
||||
{/* Last Used */}
|
||||
<td className="py-2.5 px-2 text-muted-foreground">
|
||||
<td className="py-2.5 px-2 text-[#848b9b]">
|
||||
{formatRelativeTime(flow.last_matched_at)}
|
||||
</td>
|
||||
|
||||
@@ -259,7 +259,7 @@ function FlowRow({
|
||||
style={{ width: `${flow.quality_score * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="text-xs text-foreground">
|
||||
<span className="text-xs text-[#e2e5eb]">
|
||||
{(flow.quality_score * 100).toFixed(0)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -19,9 +19,9 @@ export default function PsaMetricsPanel({ data }: PsaMetricsPanelProps) {
|
||||
|
||||
if (!hasActivity) {
|
||||
return (
|
||||
<div className="glass-card-static p-3 sm:p-5">
|
||||
<div className="card-flat p-3 sm:p-5">
|
||||
<div className="flex items-center justify-center min-h-[200px]">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<p className="text-sm text-[#848b9b]">
|
||||
No PSA activity data for this period. Link sessions to PSA tickets to see metrics.
|
||||
</p>
|
||||
</div>
|
||||
@@ -33,20 +33,20 @@ export default function PsaMetricsPanel({ data }: PsaMetricsPanelProps) {
|
||||
<div className="space-y-4">
|
||||
{/* Row 1 — Metric cards */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
<div className="glass-card-static p-4">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-[#5a6170]">Time Entries</p>
|
||||
<p className="text-gradient-brand font-heading text-2xl mt-1">{data.total_time_entries}</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">logged to PSA</p>
|
||||
<div className="card-flat p-4">
|
||||
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#5a6170]">Time Entries</p>
|
||||
<p className="text-[#67e8f9] font-heading text-2xl mt-1">{data.total_time_entries}</p>
|
||||
<p className="text-xs text-[#848b9b] mt-1">logged to PSA</p>
|
||||
</div>
|
||||
<div className="glass-card-static p-4">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-[#5a6170]">Hours Logged</p>
|
||||
<p className="text-gradient-brand font-heading text-2xl mt-1">{data.total_hours_logged.toFixed(1)}</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">total hours tracked</p>
|
||||
<div className="card-flat p-4">
|
||||
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#5a6170]">Hours Logged</p>
|
||||
<p className="text-[#67e8f9] font-heading text-2xl mt-1">{data.total_hours_logged.toFixed(1)}</p>
|
||||
<p className="text-xs text-[#848b9b] mt-1">total hours tracked</p>
|
||||
</div>
|
||||
<div className="glass-card-static p-4">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-[#5a6170]">Avg Hours/Session</p>
|
||||
<p className="text-gradient-brand font-heading text-2xl mt-1">{data.avg_hours_per_session.toFixed(2)}</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">per resolved session</p>
|
||||
<div className="card-flat p-4">
|
||||
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#5a6170]">Avg Hours/Session</p>
|
||||
<p className="text-[#67e8f9] font-heading text-2xl mt-1">{data.avg_hours_per_session.toFixed(2)}</p>
|
||||
<p className="text-xs text-[#848b9b] mt-1">per resolved session</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -55,8 +55,8 @@ export default function PsaMetricsPanel({ data }: PsaMetricsPanelProps) {
|
||||
|
||||
{/* Row 3 — Daily Trend Chart */}
|
||||
{data.daily_trend.length > 0 && (
|
||||
<div className="glass-card-static p-3 sm:p-5">
|
||||
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">
|
||||
<div className="card-flat p-3 sm:p-5">
|
||||
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb] mb-4">
|
||||
PSA Activity Trend
|
||||
</h3>
|
||||
<ResponsiveContainer width="100%" height={250}>
|
||||
@@ -129,8 +129,8 @@ function FunnelCard({ funnel }: { funnel: PsaFunnel }) {
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="glass-card-static p-3 sm:p-5">
|
||||
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">
|
||||
<div className="card-flat p-3 sm:p-5">
|
||||
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb] mb-4">
|
||||
Documentation Push Funnel
|
||||
</h3>
|
||||
|
||||
@@ -139,15 +139,15 @@ function FunnelCard({ funnel }: { funnel: PsaFunnel }) {
|
||||
{steps.map((step, i) => (
|
||||
<div key={step.label} className="flex items-center gap-2 flex-1">
|
||||
<div className="bg-primary/5 border border-primary/20 rounded-lg p-3 text-center flex-1">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
|
||||
{step.label}
|
||||
</p>
|
||||
<p className="text-lg font-heading text-foreground">{step.count}</p>
|
||||
<p className="text-lg font-heading text-[#e2e5eb]">{step.count}</p>
|
||||
</div>
|
||||
{i < steps.length - 1 && (
|
||||
<div className="flex flex-col items-center shrink-0 px-1">
|
||||
<ArrowRight size={14} className="text-muted-foreground" />
|
||||
<span className="text-[0.625rem] text-muted-foreground font-label">
|
||||
<ArrowRight size={14} className="text-[#848b9b]" />
|
||||
<span className="text-[0.625rem] text-[#848b9b] font-sans text-xs">
|
||||
{funnelPct(steps[i].count, steps[i + 1].count)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -161,15 +161,15 @@ function FunnelCard({ funnel }: { funnel: PsaFunnel }) {
|
||||
{steps.map((step, i) => (
|
||||
<div key={step.label} className="flex flex-col items-center gap-2 w-full">
|
||||
<div className="bg-primary/5 border border-primary/20 rounded-lg p-3 text-center w-full">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
|
||||
{step.label}
|
||||
</p>
|
||||
<p className="text-lg font-heading text-foreground">{step.count}</p>
|
||||
<p className="text-lg font-heading text-[#e2e5eb]">{step.count}</p>
|
||||
</div>
|
||||
{i < steps.length - 1 && (
|
||||
<div className="flex items-center gap-1">
|
||||
<ArrowDown size={14} className="text-muted-foreground" />
|
||||
<span className="text-[0.625rem] text-muted-foreground font-label">
|
||||
<ArrowDown size={14} className="text-[#848b9b]" />
|
||||
<span className="text-[0.625rem] text-[#848b9b] font-sans text-xs">
|
||||
{funnelPct(steps[i].count, steps[i + 1].count)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user