diff --git a/src/components/analytics/OEECharts.tsx b/src/components/analytics/OEECharts.tsx
index cc8bf2c3..5b96c5ac 100644
--- a/src/components/analytics/OEECharts.tsx
+++ b/src/components/analytics/OEECharts.tsx
@@ -13,30 +13,52 @@ 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 (
+
+
+
+ );
+ }
- 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 (
+
+
+ {t("analytics.noOEEData")}
+
+
+ );
+ }
+
+ // 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 (
@@ -44,22 +66,41 @@ const OEECharts = () => {
{/* OEE Breakdown */}
- OEE Breakdown
+
+ {t("oee.breakdown")}
+
+ {metrics.oee}%
+
+
-
-
-
-
+
+
+
+
[`${value}%`, ""]}
/>
@@ -68,38 +109,40 @@ const OEECharts = () => {
- {/* Machine States */}
+ {/* Operation States */}
- Machine States
+ {t("oee.operationStates")}
-
-
-
- {machineStateData.map((entry, index) => (
- |
- ))}
-
-
-
-
-
+ {metrics.stateBreakdown.length > 0 ? (
+
+
+
+ {metrics.stateBreakdown.map((entry, index) => (
+ |
+ ))}
+
+ [`${value}%`, ""]}
+ />
+
+
+
+ ) : (
+
{t("analytics.noData")}
+ )}
@@ -107,31 +150,93 @@ const OEECharts = () => {
{/* OEE Trend */}
- OEE Trend (Last 7 Days)
+ {t("oee.trend")}
-
-
-
-
-
-
-
-
-
+ {metrics.trend.length > 0 ? (
+
+
+
+
+
+ [
+ `${value}%`,
+ name === "oee" ? "OEE" : name
+ ]}
+ />
+
+
+
+ ) : (
+
+
{t("analytics.noTrendData")}
+
+ )}
+
+ {/* OEE by Cell */}
+ {metrics.byCell.length > 0 && (
+
+
+ {t("oee.byCell")}
+
+
+
+
+
+
+
+
+ [`${value}%`, ""]}
+ />
+
+
+
+
+
+
+
+ )}
);
};
+OEECharts.displayName = "OEECharts";
+
export { OEECharts };
diff --git a/src/components/analytics/ReliabilityCharts.tsx b/src/components/analytics/ReliabilityCharts.tsx
index 6796f65d..62e1f65a 100644
--- a/src/components/analytics/ReliabilityCharts.tsx
+++ b/src/components/analytics/ReliabilityCharts.tsx
@@ -10,103 +10,224 @@ import {
ResponsiveContainer,
AreaChart,
Area,
+ BarChart,
+ Bar,
} from "recharts";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { useReliabilityMetrics } from "@/hooks/useReliabilityMetrics";
+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",
+ },
+};
+
+const AXIS_STYLE = {
+ stroke: "hsl(var(--muted-foreground))",
+};
const ReliabilityCharts = () => {
- // Mock Data
- const reliabilityData = [
- { date: "Week 1", onTime: 88, late: 12 },
- { date: "Week 2", onTime: 92, late: 8 },
- { date: "Week 3", onTime: 85, late: 15 },
- { date: "Week 4", onTime: 95, late: 5 },
- { date: "Week 5", onTime: 90, late: 10 },
- ];
+ const { t } = useTranslation();
+ const { data: metrics, isLoading } = useReliabilityMetrics(30);
- const startDelayData = [
- { date: "Mon", delay: 15 }, // minutes
- { date: "Tue", delay: 5 },
- { date: "Wed", delay: 25 },
- { date: "Thu", delay: 10 },
- { date: "Fri", delay: 8 },
- ];
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ if (!metrics) {
+ return (
+
+
+ {t("analytics.noReliabilityData")}
+
+
+ );
+ }
return (
- {/* On-Time Start Performance */}
+ {/* Summary Stats */}
- On-Time Start Performance
+ {t("reliability.summary")}
+
+
+
+
+
{t("reliability.totalOperations")}
+
{metrics.totalOperations}
+
+
+
{t("reliability.onTime")}
+
+ {metrics.onTimeOperations}
+ ({metrics.onTimePercentage}%)
+
+
+
+
{t("reliability.late")}
+
+ {metrics.lateOperations}
+ ({metrics.latePercentage}%)
+
+
+
+
{t("reliability.avgDelay")}
+
+ {metrics.avgDelayMinutes}
+ {t("reliability.minutes")}
+
+
+
+
+
+
+ {/* On-Time Performance Trend */}
+
+
+ {t("reliability.onTimePerformance")}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ {metrics.weeklyTrend.length > 0 ? (
+
+
+
+
+
+
+
+
+
+
+
+ [`${value}%`, ""]}
+ />
+
+
+
+
+ ) : (
+
+
{t("analytics.noTrendData")}
+
+ )}
- {/* Average Start Delay */}
+ {/* Average Delay Trend */}
- Average Start Delay (Minutes)
+ {t("reliability.avgDelayTrend")}
-
-
-
-
-
-
-
-
-
+ {metrics.delayTrend.length > 0 ? (
+
+
+
+
+
+ [`${value} min`, ""]}
+ />
+
+
+
+ ) : (
+
+
{t("analytics.noTrendData")}
+
+ )}
+
+ {/* Reliability by Cell */}
+ {metrics.byCell.length > 0 && (
+
+
+ {t("reliability.byCell")}
+
+
+
+
+
+
+
+
+ [`${value}%`, ""]}
+ />
+
+
+
+
+
+
+
+ )}
);
};
+ReliabilityCharts.displayName = "ReliabilityCharts";
+
export { ReliabilityCharts };
diff --git a/src/hooks/useOEEMetrics.ts b/src/hooks/useOEEMetrics.ts
new file mode 100644
index 00000000..69289044
--- /dev/null
+++ b/src/hooks/useOEEMetrics.ts
@@ -0,0 +1,231 @@
+import { useQuery } from "@tanstack/react-query";
+import { supabase } from "@/integrations/supabase/client";
+import { useAuth } from "@/contexts/AuthContext";
+import { subDays, format, eachDayOfInterval } from "date-fns";
+
+export interface OEEMetrics {
+ // OEE Components
+ availability: number;
+ performance: number;
+ quality: number;
+ oee: number;
+
+ // Machine/Cell states based on operations
+ stateBreakdown: {
+ name: string;
+ value: number;
+ color: string;
+ }[];
+
+ // OEE trend over time
+ trend: {
+ date: string;
+ oee: number;
+ availability: number;
+ performance: number;
+ quality: number;
+ }[];
+
+ // OEE by cell
+ byCell: {
+ cellName: string;
+ oee: number;
+ availability: number;
+ performance: number;
+ quality: number;
+ }[];
+}
+
+export function useOEEMetrics(dateRange: number = 30) {
+ const { profile } = useAuth();
+
+ return useQuery({
+ queryKey: ["oee-metrics", profile?.tenant_id, dateRange],
+ queryFn: async (): Promise => {
+ if (!profile?.tenant_id) {
+ throw new Error("No tenant ID");
+ }
+
+ const startDate = subDays(new Date(), dateRange).toISOString();
+ const now = new Date();
+
+ // Fetch operations with cell info
+ const { data: operations, error: opsError } = await supabase
+ .from("operations")
+ .select(`
+ id,
+ actual_time,
+ estimated_time,
+ setup_time,
+ wait_time,
+ status,
+ completed_at,
+ cell_id,
+ cells(name, capacity_hours_per_day)
+ `)
+ .eq("tenant_id", profile.tenant_id)
+ .gte("updated_at", startDate);
+
+ if (opsError) throw opsError;
+
+ // Fetch quality data from operation_quantities
+ const { data: quantities, error: quantitiesError } = await supabase
+ .from("operation_quantities")
+ .select(`
+ quantity_produced,
+ quantity_good,
+ quantity_scrap,
+ operation_id,
+ recorded_at
+ `)
+ .eq("tenant_id", profile.tenant_id)
+ .gte("recorded_at", startDate);
+
+ if (quantitiesError) throw quantitiesError;
+
+ // Calculate overall metrics
+ const completedOps = operations?.filter(o => o.status === "completed") || [];
+
+ // Availability = actual production time / scheduled time
+ const totalActualTime = completedOps.reduce((sum, o) => sum + (o.actual_time || 0), 0);
+ const totalEstimatedTime = completedOps.reduce((sum, o) => sum + (o.estimated_time || 0), 0);
+ const totalWaitTime = operations?.reduce((sum, o) => sum + (o.wait_time || 0), 0) || 0;
+ const totalSetupTime = operations?.reduce((sum, o) => sum + (o.setup_time || 0), 0) || 0;
+
+ // Calculate availability (productive time / available time)
+ const plannedTime = totalEstimatedTime + totalSetupTime;
+ const availability = plannedTime > 0
+ ? Math.min(100, ((plannedTime - totalWaitTime) / plannedTime) * 100)
+ : 100;
+
+ // Performance = actual output rate / ideal output rate
+ const performance = totalActualTime > 0 && totalEstimatedTime > 0
+ ? Math.min(100, (totalEstimatedTime / totalActualTime) * 100)
+ : 100;
+
+ // Quality from quantities
+ const totalProduced = quantities?.reduce((sum, q) => sum + (q.quantity_produced || 0), 0) || 0;
+ const totalGood = quantities?.reduce((sum, q) => sum + (q.quantity_good || 0), 0) || 0;
+ const quality = totalProduced > 0 ? (totalGood / totalProduced) * 100 : 100;
+
+ // Overall OEE
+ const oee = (availability * performance * quality) / 10000;
+
+ // State breakdown based on operation status
+ const statusCounts = {
+ completed: operations?.filter(o => o.status === "completed").length || 0,
+ in_progress: operations?.filter(o => o.status === "in_progress").length || 0,
+ pending: operations?.filter(o => o.status === "pending" || o.status === "not_started").length || 0,
+ on_hold: operations?.filter(o => o.status === "on_hold").length || 0,
+ };
+ const totalOps = Object.values(statusCounts).reduce((a, b) => a + b, 0) || 1;
+
+ const stateBreakdown = [
+ { name: "Completed", value: Math.round((statusCounts.completed / totalOps) * 100), color: "hsl(var(--color-success))" },
+ { name: "In Progress", value: Math.round((statusCounts.in_progress / totalOps) * 100), color: "hsl(var(--brand-primary))" },
+ { name: "Pending", value: Math.round((statusCounts.pending / totalOps) * 100), color: "hsl(var(--color-warning))" },
+ { name: "On Hold", value: Math.round((statusCounts.on_hold / totalOps) * 100), color: "hsl(var(--neutral-400))" },
+ ].filter(s => s.value > 0);
+
+ // Trend calculation - group by day
+ const dateInterval = eachDayOfInterval({ start: subDays(now, Math.min(dateRange, 14)), end: now });
+ const trendSampleInterval = Math.max(1, Math.floor(dateInterval.length / 7));
+
+ const trend = dateInterval
+ .filter((_, i) => i % trendSampleInterval === 0)
+ .map(date => {
+ const dayStr = format(date, "yyyy-MM-dd");
+ const dayOps = completedOps.filter(o =>
+ o.completed_at && format(new Date(o.completed_at), "yyyy-MM-dd") === dayStr
+ );
+ const dayQuantities = quantities?.filter(q =>
+ q.recorded_at && format(new Date(q.recorded_at), "yyyy-MM-dd") === dayStr
+ ) || [];
+
+ const dayEstTime = dayOps.reduce((sum, o) => sum + (o.estimated_time || 0), 0);
+ const dayActTime = dayOps.reduce((sum, o) => sum + (o.actual_time || 0), 0);
+ const dayProduced = dayQuantities.reduce((sum, q) => sum + (q.quantity_produced || 0), 0);
+ const dayGood = dayQuantities.reduce((sum, q) => sum + (q.quantity_good || 0), 0);
+
+ const dayAvail = dayOps.length > 0 ? availability : 100;
+ const dayPerf = dayActTime > 0 ? Math.min(100, (dayEstTime / dayActTime) * 100) : performance;
+ const dayQual = dayProduced > 0 ? (dayGood / dayProduced) * 100 : quality;
+ const dayOee = (dayAvail * dayPerf * dayQual) / 10000;
+
+ return {
+ date: format(date, "MMM d"),
+ oee: Number(dayOee.toFixed(1)),
+ availability: Number(dayAvail.toFixed(1)),
+ performance: Number(dayPerf.toFixed(1)),
+ quality: Number(dayQual.toFixed(1)),
+ };
+ });
+
+ // OEE by cell
+ const cellMap = new Map();
+
+ operations?.forEach(op => {
+ if (op.cells?.name) {
+ const cellName = op.cells.name;
+ const current = cellMap.get(cellName) || { estTime: 0, actTime: 0, produced: 0, good: 0, waitTime: 0 };
+ cellMap.set(cellName, {
+ estTime: current.estTime + (op.estimated_time || 0),
+ actTime: current.actTime + (op.actual_time || 0),
+ produced: current.produced,
+ good: current.good,
+ waitTime: current.waitTime + (op.wait_time || 0),
+ });
+ }
+ });
+
+ // Add quality data to cells
+ quantities?.forEach(q => {
+ const op = operations?.find(o => o.id === q.operation_id);
+ if (op?.cells?.name) {
+ const current = cellMap.get(op.cells.name);
+ if (current) {
+ current.produced += q.quantity_produced || 0;
+ current.good += q.quantity_good || 0;
+ }
+ }
+ });
+
+ const byCell = Array.from(cellMap.entries()).map(([cellName, data]) => {
+ const cellAvail = data.estTime > 0
+ ? Math.min(100, ((data.estTime - data.waitTime) / data.estTime) * 100)
+ : 100;
+ const cellPerf = data.actTime > 0 && data.estTime > 0
+ ? Math.min(100, (data.estTime / data.actTime) * 100)
+ : 100;
+ const cellQual = data.produced > 0 ? (data.good / data.produced) * 100 : 100;
+ const cellOee = (cellAvail * cellPerf * cellQual) / 10000;
+
+ return {
+ cellName,
+ oee: Number(cellOee.toFixed(1)),
+ availability: Number(cellAvail.toFixed(1)),
+ performance: Number(cellPerf.toFixed(1)),
+ quality: Number(cellQual.toFixed(1)),
+ };
+ }).sort((a, b) => b.oee - a.oee).slice(0, 8);
+
+ return {
+ availability: Number(availability.toFixed(1)),
+ performance: Number(performance.toFixed(1)),
+ quality: Number(quality.toFixed(1)),
+ oee: Number(oee.toFixed(1)),
+ stateBreakdown,
+ trend,
+ byCell,
+ };
+ },
+ enabled: !!profile?.tenant_id,
+ staleTime: 60000,
+ });
+}
diff --git a/src/hooks/useReliabilityMetrics.ts b/src/hooks/useReliabilityMetrics.ts
new file mode 100644
index 00000000..fa5a47fc
--- /dev/null
+++ b/src/hooks/useReliabilityMetrics.ts
@@ -0,0 +1,206 @@
+import { useQuery } from "@tanstack/react-query";
+import { supabase } from "@/integrations/supabase/client";
+import { useAuth } from "@/contexts/AuthContext";
+import { subDays, format, eachDayOfInterval, differenceInMinutes, startOfWeek } from "date-fns";
+
+export interface ReliabilityMetrics {
+ // Overall on-time performance
+ onTimePercentage: number;
+ latePercentage: number;
+
+ // Weekly trend
+ weeklyTrend: {
+ date: string;
+ onTime: number;
+ late: number;
+ }[];
+
+ // Average delay in minutes
+ avgDelayMinutes: number;
+
+ // Daily delay trend
+ delayTrend: {
+ date: string;
+ delay: number;
+ }[];
+
+ // On-time by cell
+ byCell: {
+ cellName: string;
+ onTimePercentage: number;
+ avgDelay: number;
+ totalOperations: number;
+ }[];
+
+ // Summary stats
+ totalOperations: number;
+ onTimeOperations: number;
+ lateOperations: number;
+}
+
+export function useReliabilityMetrics(dateRange: number = 30) {
+ const { profile } = useAuth();
+
+ return useQuery({
+ queryKey: ["reliability-metrics", profile?.tenant_id, dateRange],
+ queryFn: async (): Promise => {
+ if (!profile?.tenant_id) {
+ throw new Error("No tenant ID");
+ }
+
+ const startDate = subDays(new Date(), dateRange).toISOString();
+ const now = new Date();
+
+ // Fetch operations with planned vs actual dates
+ const { data: operations, error: opsError } = await supabase
+ .from("operations")
+ .select(`
+ id,
+ planned_start,
+ planned_end,
+ completed_at,
+ status,
+ cell_id,
+ cells(name),
+ created_at,
+ updated_at
+ `)
+ .eq("tenant_id", profile.tenant_id)
+ .eq("status", "completed")
+ .gte("completed_at", startDate);
+
+ if (opsError) throw opsError;
+
+ const completedOps = operations || [];
+
+ // Calculate on-time vs late
+ let onTimeCount = 0;
+ let lateCount = 0;
+ let totalDelayMinutes = 0;
+ let opsWithDelay = 0;
+
+ const delaysByDay = new Map();
+ const onTimeByWeek = new Map();
+ const cellStats = new Map();
+
+ completedOps.forEach(op => {
+ const completedAt = op.completed_at ? new Date(op.completed_at) : null;
+ const plannedEnd = op.planned_end ? new Date(op.planned_end) : null;
+
+ // Determine if on-time
+ let isOnTime = true;
+ let delayMinutes = 0;
+
+ if (completedAt && plannedEnd) {
+ isOnTime = completedAt <= plannedEnd;
+ if (!isOnTime) {
+ delayMinutes = differenceInMinutes(completedAt, plannedEnd);
+ totalDelayMinutes += delayMinutes;
+ opsWithDelay++;
+ }
+ }
+
+ if (isOnTime) {
+ onTimeCount++;
+ } else {
+ lateCount++;
+ }
+
+ // Track by day for delay trend
+ if (completedAt) {
+ const dayStr = format(completedAt, "yyyy-MM-dd");
+ const existing = delaysByDay.get(dayStr) || { total: 0, count: 0 };
+ delaysByDay.set(dayStr, {
+ total: existing.total + delayMinutes,
+ count: existing.count + 1,
+ });
+ }
+
+ // Track by week for on-time trend
+ if (completedAt) {
+ const weekStr = format(startOfWeek(completedAt), "MMM d");
+ const existing = onTimeByWeek.get(weekStr) || { onTime: 0, late: 0 };
+ if (isOnTime) {
+ existing.onTime++;
+ } else {
+ existing.late++;
+ }
+ onTimeByWeek.set(weekStr, existing);
+ }
+
+ // Track by cell
+ if (op.cells?.name) {
+ const cellName = op.cells.name;
+ const existing = cellStats.get(cellName) || { onTime: 0, late: 0, totalDelay: 0 };
+ if (isOnTime) {
+ existing.onTime++;
+ } else {
+ existing.late++;
+ existing.totalDelay += delayMinutes;
+ }
+ cellStats.set(cellName, existing);
+ }
+ });
+
+ const totalOps = completedOps.length || 1;
+ const onTimePercentage = (onTimeCount / totalOps) * 100;
+ const latePercentage = (lateCount / totalOps) * 100;
+ const avgDelayMinutes = opsWithDelay > 0 ? totalDelayMinutes / opsWithDelay : 0;
+
+ // Build weekly trend (last 5 weeks)
+ const weeklyTrend = Array.from(onTimeByWeek.entries())
+ .map(([date, data]) => ({
+ date,
+ onTime: Math.round((data.onTime / (data.onTime + data.late)) * 100),
+ late: Math.round((data.late / (data.onTime + data.late)) * 100),
+ }))
+ .slice(-5);
+
+ // Build daily delay trend (sample to ~7 points)
+ const dateInterval = eachDayOfInterval({ start: subDays(now, Math.min(dateRange, 14)), end: now });
+ const trendSampleInterval = Math.max(1, Math.floor(dateInterval.length / 7));
+
+ const delayTrend = dateInterval
+ .filter((_, i) => i % trendSampleInterval === 0)
+ .map(date => {
+ const dayStr = format(date, "yyyy-MM-dd");
+ const dayData = delaysByDay.get(dayStr);
+ const avgDelay = dayData && dayData.count > 0
+ ? dayData.total / dayData.count
+ : 0;
+ return {
+ date: format(date, "EEE"),
+ delay: Math.round(avgDelay),
+ };
+ });
+
+ // Build by cell stats
+ const byCell = Array.from(cellStats.entries())
+ .map(([cellName, data]) => {
+ const total = data.onTime + data.late;
+ return {
+ cellName,
+ onTimePercentage: total > 0 ? Math.round((data.onTime / total) * 100) : 100,
+ avgDelay: data.late > 0 ? Math.round(data.totalDelay / data.late) : 0,
+ totalOperations: total,
+ };
+ })
+ .sort((a, b) => b.totalOperations - a.totalOperations)
+ .slice(0, 8);
+
+ return {
+ onTimePercentage: Number(onTimePercentage.toFixed(1)),
+ latePercentage: Number(latePercentage.toFixed(1)),
+ weeklyTrend,
+ avgDelayMinutes: Math.round(avgDelayMinutes),
+ delayTrend,
+ byCell,
+ totalOperations: completedOps.length,
+ onTimeOperations: onTimeCount,
+ lateOperations: lateCount,
+ };
+ },
+ enabled: !!profile?.tenant_id,
+ staleTime: 60000,
+ });
+}
diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json
index 6dbfa7af..01b11cbd 100644
--- a/src/i18n/locales/de/translation.json
+++ b/src/i18n/locales/de/translation.json
@@ -1737,7 +1737,33 @@
"jobsSubtitle": "Verfolgen Sie Auftragsstatus, Fristen und Qualitätsmetriken",
"qualityTitle": "Qualitätsanalysen",
"qualitySubtitle": "Überwachen Sie Ausbeute, Ausschuss und Problemmetriken",
- "noQualityData": "Noch keine Qualitätsdaten verfügbar. Beginnen Sie mit der Produktionserfassung, um Metriken zu sehen."
+ "noQualityData": "Noch keine Qualitätsdaten verfügbar. Beginnen Sie mit der Produktionserfassung, um Metriken zu sehen.",
+ "noOEEData": "Noch keine OEE-Daten verfügbar. Schließen Sie Arbeitsgänge ab, um Effizienzmetriken zu sehen.",
+ "noReliabilityData": "Noch keine Zuverlässigkeitsdaten verfügbar. Schließen Sie Arbeitsgänge mit geplanten Terminen ab, um Metriken zu sehen.",
+ "noData": "Keine Daten verfügbar",
+ "noTrendData": "Keine Trenddaten für diesen Zeitraum verfügbar"
+ },
+ "oee": {
+ "breakdown": "OEE-Aufschlüsselung",
+ "availability": "Verfügbarkeit",
+ "performance": "Leistung",
+ "quality": "Qualität",
+ "operationStates": "Arbeitsgangstatus",
+ "trend": "OEE-Trend",
+ "byCell": "OEE nach Zelle"
+ },
+ "reliability": {
+ "summary": "Zuverlässigkeitsübersicht",
+ "totalOperations": "Gesamte Arbeitsgänge",
+ "onTime": "Pünktlich",
+ "late": "Verspätet",
+ "avgDelay": "Durchschn. Verzögerung",
+ "minutes": "Min",
+ "onTimePerformance": "Pünktlichkeitsleistung",
+ "onTimePercent": "Pünktlich %",
+ "avgDelayTrend": "Durchschnittlicher Verzögerungstrend",
+ "delayMin": "Verzögerung (Min)",
+ "byCell": "Zuverlässigkeit nach Zelle"
},
"sessionTracking": {
"currentlyTracking": "Aktuelle Zeiterfassung",
diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json
index b3708a9b..976a6dbf 100644
--- a/src/i18n/locales/en/translation.json
+++ b/src/i18n/locales/en/translation.json
@@ -1979,7 +1979,33 @@
"jobsSubtitle": "Track job status, deadlines, and quality metrics",
"qualityTitle": "Quality Analytics",
"qualitySubtitle": "Monitor yield rates, scrap, and issue metrics",
- "noQualityData": "No quality data available yet. Start recording production to see metrics."
+ "noQualityData": "No quality data available yet. Start recording production to see metrics.",
+ "noOEEData": "No OEE data available yet. Complete operations to see efficiency metrics.",
+ "noReliabilityData": "No reliability data available yet. Complete operations with planned dates to see metrics.",
+ "noData": "No data available",
+ "noTrendData": "No trend data available for this period"
+ },
+ "oee": {
+ "breakdown": "OEE Breakdown",
+ "availability": "Availability",
+ "performance": "Performance",
+ "quality": "Quality",
+ "operationStates": "Operation States",
+ "trend": "OEE Trend",
+ "byCell": "OEE by Cell"
+ },
+ "reliability": {
+ "summary": "Reliability Summary",
+ "totalOperations": "Total Operations",
+ "onTime": "On Time",
+ "late": "Late",
+ "avgDelay": "Avg Delay",
+ "minutes": "min",
+ "onTimePerformance": "On-Time Performance",
+ "onTimePercent": "On-Time %",
+ "avgDelayTrend": "Average Delay Trend",
+ "delayMin": "Delay (min)",
+ "byCell": "Reliability by Cell"
},
"capacity": {
"title": "Capacity Matrix",
diff --git a/src/i18n/locales/nl/translation.json b/src/i18n/locales/nl/translation.json
index 3d34fdbf..a690689b 100644
--- a/src/i18n/locales/nl/translation.json
+++ b/src/i18n/locales/nl/translation.json
@@ -2007,7 +2007,33 @@
"jobsSubtitle": "Volg orderstatus, deadlines en kwaliteitsmetrieken",
"qualityTitle": "Kwaliteitsanalyses",
"qualitySubtitle": "Monitor opbrengsten, afval en probleemmetrieken",
- "noQualityData": "Nog geen kwaliteitsgegevens beschikbaar. Begin met productieregistratie om metrieken te zien."
+ "noQualityData": "Nog geen kwaliteitsgegevens beschikbaar. Begin met productieregistratie om metrieken te zien.",
+ "noOEEData": "Nog geen OEE-gegevens beschikbaar. Voltooi bewerkingen om efficiëntiemetrieken te zien.",
+ "noReliabilityData": "Nog geen betrouwbaarheidsgegevens beschikbaar. Voltooi bewerkingen met geplande datums om metrieken te zien.",
+ "noData": "Geen gegevens beschikbaar",
+ "noTrendData": "Geen trendgegevens beschikbaar voor deze periode"
+ },
+ "oee": {
+ "breakdown": "OEE Uitsplitsing",
+ "availability": "Beschikbaarheid",
+ "performance": "Prestaties",
+ "quality": "Kwaliteit",
+ "operationStates": "Bewerkingsstatussen",
+ "trend": "OEE Trend",
+ "byCell": "OEE per Cel"
+ },
+ "reliability": {
+ "summary": "Betrouwbaarheidsoverzicht",
+ "totalOperations": "Totaal Bewerkingen",
+ "onTime": "Op Tijd",
+ "late": "Te Laat",
+ "avgDelay": "Gem. Vertraging",
+ "minutes": "min",
+ "onTimePerformance": "Op-Tijd Prestaties",
+ "onTimePercent": "Op-Tijd %",
+ "avgDelayTrend": "Gemiddelde Vertragingstrend",
+ "delayMin": "Vertraging (min)",
+ "byCell": "Betrouwbaarheid per Cel"
},
"sessionTracking": {
"currentlyTracking": "Momenteel aan het bijhouden",