process-aware-ai / frontend /components /analytics-section.tsx
borndeveloper's picture
Backedns erach functionality added
5533d3b
Raw
History Blame Contribute Delete
20.3 kB
"use client";
import { useEffect, useState } from "react";
import axios from "axios";
import {
Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis,
BarChart, Bar, XAxis, YAxis, Tooltip, CartesianGrid, ResponsiveContainer,
LineChart, Line, PieChart, Pie, Cell, Legend
} from 'recharts';
import { InfoTooltip } from "@/components/ui/tooltip";
const API_URL = "/api";
const COLORS = ['#10b981', '#f59e0b', '#ef4444', '#3b82f6'];
export function AnalyticsSection({ definitions }: { definitions: any }) {
const [globalData, setGlobalData] = useState<any>(null);
const [complexity, setComplexity] = useState<any[]>([]);
useEffect(() => {
// Fetch new global analytics
axios.get(`${API_URL}/analytics/global`).then(res => setGlobalData(res.data)).catch(err => console.error(err));
// Keep existing complexity data
axios.get(`${API_URL}/analytics/finish-complexity`).then(res => setComplexity(res.data));
}, []);
if (!globalData) return <div className="p-8 text-neutral-500 animate-pulse">Loading Global Insights...</div>;
const { kpis, distributions, trends, global_waterfall, global_blame } = globalData;
// Prepare blame data for Pie chart
const blameData = global_blame ? [
{ name: 'Policy (norms)', value: global_blame.policy_pct, color: '#f59e0b' },
{ name: 'Execution (planner)', value: global_blame.execution_pct, color: '#3b82f6' },
{ name: 'Process (manufacturing)', value: global_blame.process_pct, color: '#ef4444' }
] : [];
return (
<div className="space-y-8 mb-12">
{/* 1. Global KPI Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<KPICard
label="Total Order Qty"
value={`${(kpis.total_volume_m / 1000000).toFixed(2)}M`}
sub="Meters Processed"
tooltip="Sum of all DORQT1 (Order Quantity) across every Sale Order line. Formula: Σ Order Qty (meters)."
/>
<KPICard
label="Global Processing Efficiency"
value={`${kpis.global_yield_pct}%`}
sub="Weighted Average"
color={kpis.global_yield_pct > 95 ? "text-emerald-400" : "text-amber-400"}
tooltip="Weighted manufacturing efficiency. Formula: (Σ Pack Fresh Output ÷ Σ Greige Issued) × 100. Higher = less process loss."
/>
<KPICard
label="Shortfall Risk Rate"
value={`${kpis.shortfall_risk_pct}%`}
sub="Orders with Deficit Risk"
color={kpis.shortfall_risk_pct < 5 ? "text-emerald-400" : "text-red-400"}
tooltip="Percentage of orders where Output < Order Qty. Formula: (Count of orders with Pack Fresh < Order Qty ÷ Total Orders) × 100."
/>
<KPICard
label="Active Orders"
value={kpis.total_orders}
sub="Total Sale Orders"
tooltip="Count of distinct Sale Order numbers in the loaded 1-year dataset."
/>
</div>
{/* 2. Charts Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Finish Performance */}
<ChartCard
title="Efficiency by Finish"
subtitle="Top 10 Finishes by Order Quantity"
description="Displays the average manufacturing efficiency percentage for the top 10 most heavily produced finish types. Helps identify which chemical or mechanical finishes cause the most process loss."
>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={distributions.finish} layout="vertical" margin={{ left: 40, right: 20 }}>
<CartesianGrid strokeDasharray="3 3" horizontal={true} vertical={false} stroke="#333" />
<XAxis type="number" domain={[80, 100]} stroke="#555" unit="%" />
<YAxis dataKey="Finish" type="category" stroke="#9ca3af" width={100} tick={{ fontSize: 11 }} />
<Tooltip
contentStyle={{ backgroundColor: '#171717', borderColor: '#404040' }}
cursor={{ fill: '#ffffff05' }}
/>
<Bar dataKey="yield" fill="#3b82f6" radius={[0, 4, 4, 0]} name="Efficiency %" />
</BarChart>
</ResponsiveContainer>
</ChartCard>
{/* Route Performance */}
<ChartCard
title="Efficiency by Route"
subtitle="Machine Type Analysis"
description="Compares the production efficiency across different dyeing machines and processing routes. Useful for spotting hardware or technique bottlenecks."
>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={distributions.route}>
<CartesianGrid strokeDasharray="3 3" stroke="#333" />
<XAxis dataKey="Route" stroke="#9ca3af" />
<YAxis domain={[80, 100]} stroke="#555" unit="%" />
<Tooltip contentStyle={{ backgroundColor: '#171717', borderColor: '#404040' }} />
<Bar dataKey="yield" fill="#10b981" radius={[4, 4, 0, 0]} name="Efficiency %" />
</BarChart>
</ResponsiveContainer>
</ChartCard>
{/* NEW: Yield by Shade */}
{distributions.shade && distributions.shade.length > 0 && (
<ChartCard
title="Efficiency by Shade"
subtitle="Shade Type Analysis"
description="Shows the efficiency percentage grouped by base shade categories (e.g., Dyed, RFD, Bleach). Darker or more complex shades often have lower efficiency."
>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={distributions.shade}>
<CartesianGrid strokeDasharray="3 3" stroke="#333" />
<XAxis dataKey="Shade Type" stroke="#9ca3af" tick={{ fontSize: 10 }} />
<YAxis domain={[80, 100]} stroke="#555" unit="%" />
<Tooltip contentStyle={{ backgroundColor: '#171717', borderColor: '#404040' }} />
<Bar dataKey="yield" fill="#8b5cf6" radius={[4, 4, 0, 0]} name="Efficiency %" />
</BarChart>
</ResponsiveContainer>
</ChartCard>
)}
{/* Monthly Trend */}
<ChartCard
title="Efficiency Trend"
subtitle="Monthly Global Average"
description="Tracks the overall manufacturing efficiency rate month-over-month to show macro trends in production efficiency over time."
>
<ResponsiveContainer width="100%" height="100%">
<LineChart data={trends}>
<CartesianGrid strokeDasharray="3 3" stroke="#333" />
<XAxis dataKey="month" stroke="#9ca3af" tick={{ fontSize: 11 }} />
<YAxis domain={['auto', 'auto']} stroke="#555" unit="%" />
<Tooltip contentStyle={{ backgroundColor: '#171717', borderColor: '#404040' }} />
<Line type="monotone" dataKey="yield" stroke="#f59e0b" strokeWidth={2} dot={{ r: 4 }} activeDot={{ r: 6 }} />
</LineChart>
</ResponsiveContainer>
</ChartCard>
{/* Complexity Bar Chart (Fixed from Radar) */}
<ChartCard
title="Complexity & Deviation"
subtitle="Attribute Impact Analysis (Top Finishes)"
description="Highlights which product finishes diverge the most from the standard norms. A high deviation means the planner frequently overrides the system recommendations for this attribute."
>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={complexity} layout="vertical" margin={{ left: 40, right: 20 }}>
<CartesianGrid strokeDasharray="3 3" horizontal={true} vertical={false} stroke="#333" />
<XAxis type="number" stroke="#555" unit="%" />
<YAxis dataKey="attribute" type="category" stroke="#9ca3af" width={100} tick={{ fontSize: 11 }} />
<Tooltip
contentStyle={{ backgroundColor: '#171717', borderColor: '#404040' }}
cursor={{ fill: '#ffffff05' }}
formatter={(value: any) => [`${Number(value).toFixed(2)}%`, 'Avg Deviation']}
/>
<Bar dataKey="avg_deviation" fill="#8884d8" radius={[0, 4, 4, 0]} name="Avg Deviation %" />
</BarChart>
</ResponsiveContainer>
</ChartCard>
{/* NEW: Global Blame/Friction Breakdown */}
{global_blame && (
<ChartCard
title="Global Friction Breakdown"
subtitle="Where Effort is Lost"
description="Analyzes the root cause of shortfalls across all orders. Attributes blame to Policy (Norms too tight), Execution (Planner under-issued), or Process (Manufacturing loss exceeded buffer)."
>
<ResponsiveContainer width="100%" height="100%">
<PieChart margin={{ top: 0, right: 0, left: 0, bottom: 20 }}>
<Pie
data={blameData}
cx="50%"
cy="45%"
innerRadius={50}
outerRadius={80}
paddingAngle={3}
dataKey="value"
nameKey="name"
// Removed the cramped inline labels to rely on Legend & Menu
labelLine={false}
>
{blameData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
<Legend
verticalAlign="bottom"
wrapperStyle={{ fontSize: '11px', paddingTop: '10px' }}
layout="horizontal"
/>
<Tooltip
contentStyle={{ backgroundColor: '#171717', borderColor: '#404040', color: '#fff' }}
itemStyle={{ color: '#fff' }}
formatter={(value: any) => [`${Number(value).toFixed(1)}%`, 'Share of Shortfall']}
/>
</PieChart>
</ResponsiveContainer>
</ChartCard>
)}
</div>
{/* 3. NEW: Top Customers & Segment Analysis */}
{(distributions.customer?.length > 0 || distributions.segment?.length > 0) && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Top Customers Table */}
{distributions.customer && distributions.customer.length > 0 && (
<div className="bg-neutral-900/40 border border-neutral-800 p-6 rounded-xl">
<h3 className="text-lg font-semibold text-neutral-200 mb-1">Top Customers</h3>
<p className="text-sm text-neutral-500 mb-4">By Order Quantity (Meters)</p>
<div className="overflow-auto max-h-[300px]">
<table className="w-full text-sm">
<thead className="text-neutral-400 border-b border-neutral-700">
<tr>
<th className="text-left py-2 pr-4">Customer</th>
<th className="text-right py-2 px-2">Order Qty <InfoTooltip content="Sum of all DORQT1 for this customer. Formula: Σ Order Qty (meters)." /></th>
<th className="text-right py-2 pl-2">Efficiency % <InfoTooltip content="Pack Fresh ÷ Greige Issued × 100 for this customer." /></th>
</tr>
</thead>
<tbody>
{distributions.customer.map((c: any, i: number) => (
<tr key={i} className="border-b border-neutral-800/50 hover:bg-neutral-800/30">
<td className="py-2 pr-4 text-neutral-300 truncate max-w-[200px]" title={c.customer}>{c.customer}</td>
<td className="py-2 px-2 text-right text-neutral-400">{(c.volume / 1000).toFixed(1)}K</td>
<td className={`py-2 pl-2 text-right font-medium ${c.yield > 95 ? 'text-emerald-400' : 'text-amber-400'}`}>{c.yield}%</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{/* Segment Distribution */}
{distributions.segment && distributions.segment.length > 0 && (
<ChartCard
title="Order Qty by Segment"
subtitle="Business Segment Breakdown"
description="Categorizes total mapped order quantity into internal business segments or product families for high-level portfolio analysis."
>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={distributions.segment} layout="vertical" margin={{ left: 60, right: 20 }}>
<CartesianGrid strokeDasharray="3 3" horizontal={true} vertical={false} stroke="#333" />
<XAxis type="number" stroke="#555" tickFormatter={(v) => `${(v / 1000).toFixed(0)}K`} />
<YAxis dataKey="segment" type="category" stroke="#9ca3af" width={120} tick={{ fontSize: 11 }} />
<Tooltip
contentStyle={{ backgroundColor: '#171717', borderColor: '#404040' }}
formatter={(value: any) => [`${(value / 1000).toFixed(1)}K meters`, 'Order Qty']}
/>
<Bar dataKey="volume" fill="#14b8a6" radius={[0, 4, 4, 0]} name="Order Qty" />
</BarChart>
</ResponsiveContainer>
</ChartCard>
)}
</div>
)
}
{/* 4. NEW: Global Waterfall (Opportunity Loss) */}
{
global_waterfall && (
<div className="bg-neutral-900/40 border border-neutral-800 p-6 rounded-xl">
<h3 className="text-lg font-semibold text-neutral-200 mb-1">Global Opportunity Waterfall <InfoTooltip content="Visualizes the material flow from Order Quantity to final Output. Each bar shows how Reserved Qty, Issued Qty, and Process Loss transform the original order into delivered meters." /></h3>
<p className="text-sm text-neutral-500 mb-6">Order Quantity → Delivery Flow (Meters)</p>
<div className="h-[250px] w-full">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={global_waterfall} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#333" />
<XAxis dataKey="label" stroke="#9ca3af" tick={{ fontSize: 11 }} />
<YAxis stroke="#555" tickFormatter={(v) => `${(v / 1000000).toFixed(1)}M`} />
<Tooltip
contentStyle={{ backgroundColor: '#171717', borderColor: '#404040' }}
formatter={(value: any, name: any, props: any) => {
const isPositive = props.payload.value >= 0;
return [`${isPositive ? '+' : ''}${(Number(value) / 1000000).toFixed(2)}M`, props.payload.desc || props.payload.label];
}}
/>
<Bar dataKey="value" radius={[4, 4, 0, 0]}>
{global_waterfall.map((entry: any, index: number) => {
const bgColor = entry.type === 'base' ? '#3b82f6' :
entry.type === 'final' ? '#10b981' :
entry.value >= 0 ? '#f59e0b' : '#ef4444';
return <Cell key={`cell-${index}`} fill={bgColor} />;
})}
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
</div>
)
}
</div >
)
}
function KPICard({ label, value, sub, color = "text-white", tooltip }: { label: string, value: string | number, sub: string, color?: string, tooltip?: string }) {
return (
<div className="bg-neutral-900/40 border border-neutral-800 p-6 rounded-xl relative group">
<h4 className="flex items-center gap-2 text-neutral-400 text-sm font-medium uppercase tracking-wider mb-2">
{label}
{tooltip && <InfoTooltip content={tooltip} />}
</h4>
<div className={`text-3xl font-bold ${color} mb-1`}>{value}</div>
<div className="text-neutral-500 text-xs">{sub}</div>
</div>
)
}
function ChartCard({ title, subtitle, description, children }: { title: string, subtitle: string, description?: string, children: React.ReactNode }) {
return (
<div className="bg-neutral-900/40 border border-neutral-800 p-6 rounded-xl min-h-[350px] flex flex-col group relative">
<div className="mb-6 pr-8">
<h3 className="flex items-center gap-2 text-lg font-semibold text-neutral-200">
{title}
{description && <InfoTooltip content={description} />}
</h3>
<p className="text-sm text-neutral-500">{subtitle}</p>
</div>
<div className="flex-1 min-h-[250px] relative z-10">
{children}
</div>
</div>
)
}