RavindranadhM's picture
Simplify deployed UX and remove surface gate flow
c36c5e5 verified
Raw
History Blame Contribute Delete
11.4 kB
import { useEffect, useState } from "react"
import {
Area,
AreaChart,
Bar,
BarChart,
CartesianGrid,
Cell,
Pie,
PieChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts"
import MetricCard from "../components/MetricCard"
import { fetchAnalyticsSummary } from "../lib/api"
import { formatShortTime, titleCase } from "../lib/formatters"
const CHART_COLORS = ["#38bdf8", "#f59e0b", "#f43f5e", "#22c55e", "#94a3b8", "#a855f7"]
export default function Analytics() {
const [analytics, setAnalytics] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState("")
useEffect(() => {
let active = true
fetchAnalyticsSummary(120)
.then((payload) => {
if (active) {
setAnalytics(payload)
}
})
.catch((requestError) => {
if (active) {
setError(requestError.message)
}
})
.finally(() => {
if (active) {
setLoading(false)
}
})
return () => {
active = false
}
}, [])
const decisionData = analytics
? Object.entries(analytics.decision_breakdown).map(([name, value]) => ({ name, value }))
: []
const severityData = analytics
? Object.entries(analytics.severity_totals).map(([name, value]) => ({ name, value }))
: []
const sourceData = analytics
? Object.entries(analytics.source_breakdown).map(([name, value]) => ({ name: titleCase(name), value }))
: []
const defectData = analytics
? Object.entries(analytics.defect_breakdown)
.map(([name, value]) => ({ name: titleCase(name), value }))
.sort((left, right) => right.value - left.value)
.slice(0, 6)
: []
const timeline = analytics
? analytics.timeline.map((entry) => ({
...entry,
label: formatShortTime(entry.timestamp),
}))
: []
return (
<div className="space-y-6 pb-10">
<section className="surface-card p-6 md:p-8">
<div className="grid gap-6 xl:grid-cols-[1.1fr_0.9fr]">
<div>
<p className="eyebrow">Analytics Page</p>
<h2 className="hero-title mt-3">Track quality trends, decision mix, and defect patterns across saved inspections.</h2>
<p className="body-copy mt-5 max-w-3xl">
Use this page to spot recurring defects, monitor severity changes, and review how inspection results move over time.
</p>
</div>
<div className="grid gap-4 sm:grid-cols-3">
<MetricCard label="Reports" value={analytics?.report_count ?? "--"} detail="Trend window size." tone="steel" />
<MetricCard label="Avg Defects" value={analytics?.average_total_defects ?? "--"} detail="Per inspection." tone="accent" />
<MetricCard label="Sources" value={sourceData.length || "--"} detail="Capture modes in the dataset." tone="warning" />
</div>
</div>
</section>
{loading ? (
<section className="surface-card p-6 text-sm text-slate-400">Loading analytics...</section>
) : error ? (
<section className="surface-card p-6 text-sm text-rose-100">{error}</section>
) : (
<>
<section className="grid gap-6 xl:grid-cols-[1.1fr_0.9fr]">
<div className="surface-card p-6">
<p className="eyebrow">Trend Over Time</p>
<h3 className="section-title mt-2">Defect volume per inspection</h3>
<div className="mt-6 h-[340px]">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={timeline}>
<defs>
<linearGradient id="totalGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#38bdf8" stopOpacity={0.45} />
<stop offset="100%" stopColor="#38bdf8" stopOpacity={0.02} />
</linearGradient>
</defs>
<CartesianGrid stroke="rgba(148, 163, 184, 0.12)" vertical={false} />
<XAxis dataKey="label" tick={{ fill: "#94a3b8", fontSize: 12 }} axisLine={false} tickLine={false} />
<YAxis tick={{ fill: "#94a3b8", fontSize: 12 }} axisLine={false} tickLine={false} />
<Tooltip
contentStyle={{
background: "rgba(15, 23, 42, 0.96)",
border: "1px solid rgba(148, 163, 184, 0.18)",
borderRadius: "18px",
color: "#e2e8f0",
}}
/>
<Area type="monotone" dataKey="total_defects" stroke="#38bdf8" strokeWidth={3} fill="url(#totalGradient)" />
</AreaChart>
</ResponsiveContainer>
</div>
</div>
<div className="surface-card p-6">
<p className="eyebrow">Decision Mix</p>
<h3 className="section-title mt-2">PASS, REVIEW, and FAIL distribution</h3>
<div className="mt-6 h-[340px]">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Tooltip
contentStyle={{
background: "rgba(15, 23, 42, 0.96)",
border: "1px solid rgba(148, 163, 184, 0.18)",
borderRadius: "18px",
color: "#e2e8f0",
}}
/>
<Pie data={decisionData} dataKey="value" nameKey="name" innerRadius={70} outerRadius={110} paddingAngle={4}>
{decisionData.map((entry, index) => (
<Cell key={entry.name} fill={CHART_COLORS[index % CHART_COLORS.length]} />
))}
</Pie>
</PieChart>
</ResponsiveContainer>
</div>
</div>
</section>
<section className="grid gap-6 xl:grid-cols-[0.9fr_1.1fr]">
<div className="surface-card p-6">
<p className="eyebrow">Severity Totals</p>
<h3 className="section-title mt-2">Minor, moderate, and critical volume</h3>
<div className="mt-6 h-[340px]">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={severityData}>
<CartesianGrid stroke="rgba(148, 163, 184, 0.12)" vertical={false} />
<XAxis dataKey="name" tick={{ fill: "#94a3b8", fontSize: 12 }} axisLine={false} tickLine={false} />
<YAxis tick={{ fill: "#94a3b8", fontSize: 12 }} axisLine={false} tickLine={false} />
<Tooltip
contentStyle={{
background: "rgba(15, 23, 42, 0.96)",
border: "1px solid rgba(148, 163, 184, 0.18)",
borderRadius: "18px",
color: "#e2e8f0",
}}
/>
<Bar dataKey="value" radius={[14, 14, 0, 0]}>
{severityData.map((entry, index) => (
<Cell key={entry.name} fill={CHART_COLORS[index % CHART_COLORS.length]} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
</div>
<div className="surface-card p-6">
<p className="eyebrow">Defect Types</p>
<h3 className="section-title mt-2">Most frequent defect categories</h3>
<div className="mt-6 h-[340px]">
<ResponsiveContainer width="100%" height="100%">
<BarChart layout="vertical" data={defectData}>
<CartesianGrid stroke="rgba(148, 163, 184, 0.12)" horizontal={false} />
<XAxis type="number" tick={{ fill: "#94a3b8", fontSize: 12 }} axisLine={false} tickLine={false} />
<YAxis type="category" dataKey="name" tick={{ fill: "#cbd5e1", fontSize: 12 }} axisLine={false} tickLine={false} width={120} />
<Tooltip
contentStyle={{
background: "rgba(15, 23, 42, 0.96)",
border: "1px solid rgba(148, 163, 184, 0.18)",
borderRadius: "18px",
color: "#e2e8f0",
}}
/>
<Bar dataKey="value" radius={[0, 14, 14, 0]}>
{defectData.map((entry, index) => (
<Cell key={entry.name} fill={CHART_COLORS[index % CHART_COLORS.length]} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
</div>
</section>
<section className="grid gap-6 xl:grid-cols-2">
<div className="surface-card p-6">
<p className="eyebrow">Capture Sources</p>
<h3 className="section-title mt-2">Which input modes are being used</h3>
<div className="mt-5 space-y-3">
{sourceData.length ? (
sourceData.map((entry, index) => (
<div key={entry.name} className="flex items-center justify-between rounded-3xl border border-white/10 bg-white/5 px-4 py-4">
<div className="flex items-center gap-3">
<span
className="inline-flex h-3.5 w-3.5 rounded-full"
style={{ backgroundColor: CHART_COLORS[index % CHART_COLORS.length] }}
/>
<p className="text-sm text-slate-200">{entry.name}</p>
</div>
<p className="text-sm font-medium text-slate-100">{entry.value}</p>
</div>
))
) : (
<div className="rounded-3xl border border-dashed border-white/15 bg-white/5 p-5 text-sm text-slate-400">
Source analytics will appear once reports are available.
</div>
)}
</div>
</div>
<div className="surface-card p-6">
<p className="eyebrow">Monitoring Notes</p>
<h3 className="section-title mt-2">Practical guidance for better inspection quality</h3>
<div className="mt-5 grid gap-4 text-sm leading-7 text-slate-300">
<div className="rounded-3xl border border-white/10 bg-white/5 p-4">
Keep the steel surface centered with a top-down view for more stable live monitoring.
</div>
<div className="rounded-3xl border border-white/10 bg-white/5 p-4">
Use uploaded images for detailed review and save live logging for confirmed production checks.
</div>
<div className="rounded-3xl border border-white/10 bg-white/5 p-4">
Enable deep AI analysis only when you need slower recommendation text beyond the core inspection result.
</div>
</div>
</div>
</section>
</>
)}
</div>
)
}