Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 180 additions & 75 deletions src/components/analytics/OEECharts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,53 +13,94 @@ import {
Cell,
} from "recharts";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { useOEEMetrics } from "@/hooks/useOEEMetrics";
import { useTranslation } from "react-i18next";
import { Loader2 } from "lucide-react";

// Centralized styling
const TOOLTIP_STYLE = {
contentStyle: {
backgroundColor: "hsl(var(--popover))",
borderColor: "hsl(var(--border))",
color: "hsl(var(--popover-foreground))",
borderRadius: "8px",
},
cursor: { fill: "hsl(var(--muted)/0.2)" },
};

const AXIS_STYLE = {
stroke: "hsl(var(--muted-foreground))",
};

const OEECharts = () => {
// Mock Data
const oeeData = [
{ name: "Availability", value: 85, fill: "hsl(var(--brand-primary))" },
{ name: "Performance", value: 92, fill: "hsl(var(--brand-accent))" },
{ name: "Quality", value: 98, fill: "hsl(var(--color-success))" },
];
const { t } = useTranslation();
const { data: metrics, isLoading } = useOEEMetrics(30);

const machineStateData = [
{ name: "Running", value: 65, color: "hsl(var(--color-success))" },
{ name: "Idle", value: 20, color: "hsl(var(--color-warning))" },
{ name: "Down", value: 10, color: "hsl(var(--color-error))" },
{ name: "Maintenance", value: 5, color: "hsl(var(--neutral-400))" },
];
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
);
}

const trendData = [
{ name: "Mon", oee: 82 },
{ name: "Tue", oee: 84 },
{ name: "Wed", oee: 88 },
{ name: "Thu", oee: 85 },
{ name: "Fri", oee: 90 },
{ name: "Sat", oee: 87 },
{ name: "Sun", oee: 89 },
if (!metrics) {
return (
<Card className="glass-card">
<CardContent className="p-8 text-center">
<p className="text-muted-foreground">{t("analytics.noOEEData")}</p>
</CardContent>
</Card>
);
}

// Transform OEE breakdown data
const oeeData = [
{ name: t("oee.availability"), value: metrics.availability, fill: "hsl(var(--brand-primary))" },
{ name: t("oee.performance"), value: metrics.performance, fill: "hsl(var(--brand-accent))" },
{ name: t("oee.quality"), value: metrics.quality, fill: "hsl(var(--color-success))" },
];

return (
<div className="grid gap-6 md:grid-cols-2">
{/* OEE Breakdown */}
<Card className="glass-card">
<CardHeader>
<CardTitle>OEE Breakdown</CardTitle>
<CardTitle className="flex items-center justify-between">
<span>{t("oee.breakdown")}</span>
<span className="text-2xl font-bold text-[hsl(var(--brand-primary))]">
{metrics.oee}%
</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="h-[300px] w-full">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={oeeData} layout="vertical" margin={{ left: 20 }}>
<CartesianGrid strokeDasharray="3 3" stroke="hsl(var(--border-subtle))" horizontal={false} />
<XAxis type="number" domain={[0, 100]} stroke="hsl(var(--muted-foreground))" />
<YAxis dataKey="name" type="category" stroke="hsl(var(--muted-foreground))" width={100} />
<BarChart
data={oeeData}
layout="vertical"
margin={{ left: 20 }}
accessibilityLayer
>
<CartesianGrid
strokeDasharray="3 3"
stroke="hsl(var(--border-subtle))"
horizontal={false}
/>
<XAxis
type="number"
domain={[0, 100]}
{...AXIS_STYLE}
/>
<YAxis
dataKey="name"
type="category"
{...AXIS_STYLE}
width={100}
/>
<Tooltip
contentStyle={{
backgroundColor: "hsl(var(--popover))",
borderColor: "hsl(var(--border))",
color: "hsl(var(--popover-foreground))"
}}
cursor={{ fill: "hsl(var(--muted)/0.2)" }}
{...TOOLTIP_STYLE}
formatter={(value: number) => [`${value}%`, ""]}
/>
<Bar dataKey="value" radius={[0, 4, 4, 0]} barSize={32} />
</BarChart>
Expand All @@ -68,70 +109,134 @@ const OEECharts = () => {
</CardContent>
</Card>

{/* Machine States */}
{/* Operation States */}
<Card className="glass-card">
<CardHeader>
<CardTitle>Machine States</CardTitle>
<CardTitle>{t("oee.operationStates")}</CardTitle>
</CardHeader>
<CardContent>
<div className="h-[300px] w-full flex items-center justify-center">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={machineStateData}
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={100}
paddingAngle={5}
dataKey="value"
>
{machineStateData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} stroke="none" />
))}
</Pie>
<Tooltip
contentStyle={{
backgroundColor: "hsl(var(--popover))",
borderColor: "hsl(var(--border))",
color: "hsl(var(--popover-foreground))"
}}
/>
<Legend />
</PieChart>
</ResponsiveContainer>
{metrics.stateBreakdown.length > 0 ? (
<ResponsiveContainer width="100%" height="100%">
<PieChart accessibilityLayer>
<Pie
data={metrics.stateBreakdown}
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={100}
paddingAngle={5}
dataKey="value"
nameKey="name"
>
{metrics.stateBreakdown.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} stroke="none" />
))}
</Pie>
<Tooltip
{...TOOLTIP_STYLE}
formatter={(value: number) => [`${value}%`, ""]}
/>
<Legend />
</PieChart>
</ResponsiveContainer>
) : (
<p className="text-muted-foreground">{t("analytics.noData")}</p>
)}
</div>
</CardContent>
</Card>

{/* OEE Trend */}
<Card className="glass-card md:col-span-2">
<CardHeader>
<CardTitle>OEE Trend (Last 7 Days)</CardTitle>
<CardTitle>{t("oee.trend")}</CardTitle>
</CardHeader>
<CardContent>
<div className="h-[300px] w-full">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={trendData}>
<CartesianGrid strokeDasharray="3 3" stroke="hsl(var(--border-subtle))" vertical={false} />
<XAxis dataKey="name" stroke="hsl(var(--muted-foreground))" />
<YAxis domain={[0, 100]} stroke="hsl(var(--muted-foreground))" />
<Tooltip
contentStyle={{
backgroundColor: "hsl(var(--popover))",
borderColor: "hsl(var(--border))",
color: "hsl(var(--popover-foreground))"
}}
cursor={{ fill: "hsl(var(--muted)/0.2)" }}
/>
<Bar dataKey="oee" fill="hsl(var(--brand-primary))" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
{metrics.trend.length > 0 ? (
<ResponsiveContainer width="100%" height="100%">
<BarChart data={metrics.trend} accessibilityLayer>
<CartesianGrid
strokeDasharray="3 3"
stroke="hsl(var(--border-subtle))"
vertical={false}
/>
<XAxis dataKey="date" {...AXIS_STYLE} />
<YAxis domain={[0, 100]} {...AXIS_STYLE} />
<Tooltip
{...TOOLTIP_STYLE}
formatter={(value: number, name: string) => [
`${value}%`,
name === "oee" ? "OEE" : name
]}
/>
<Bar
dataKey="oee"
fill="hsl(var(--brand-primary))"
radius={[4, 4, 0, 0]}
name="OEE"
/>
</BarChart>
</ResponsiveContainer>
) : (
<div className="flex items-center justify-center h-full">
<p className="text-muted-foreground">{t("analytics.noTrendData")}</p>
</div>
)}
</div>
</CardContent>
</Card>

{/* OEE by Cell */}
{metrics.byCell.length > 0 && (
<Card className="glass-card md:col-span-2">
<CardHeader>
<CardTitle>{t("oee.byCell")}</CardTitle>
</CardHeader>
<CardContent>
<div className="h-[300px] w-full">
<ResponsiveContainer width="100%" height="100%">
<BarChart
data={metrics.byCell}
layout="vertical"
margin={{ left: 80 }}
accessibilityLayer
>
<CartesianGrid
strokeDasharray="3 3"
stroke="hsl(var(--border-subtle))"
horizontal={false}
/>
<XAxis type="number" domain={[0, 100]} {...AXIS_STYLE} />
<YAxis
dataKey="cellName"
type="category"
{...AXIS_STYLE}
width={75}
/>
<Tooltip
{...TOOLTIP_STYLE}
formatter={(value: number) => [`${value}%`, ""]}
/>
<Legend />
<Bar
dataKey="oee"
fill="hsl(var(--brand-primary))"
radius={[0, 4, 4, 0]}
name="OEE"
barSize={20}
/>
</BarChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
)}
</div>
);
};

OEECharts.displayName = "OEECharts";

export { OEECharts };
Loading
Loading