This commit is contained in:
Marcelo
2026-04-26 16:31:04 +00:00
parent 66c89f9bf4
commit 7e0fe5c2e1
28 changed files with 5310 additions and 2741 deletions

View File

@@ -3,6 +3,7 @@
import Link from "next/link";
import { useEffect, useState } from "react";
import { useI18n } from "@/lib/i18n/useI18n";
import { RECAP_HEARTBEAT_STALE_MS } from "@/lib/recap/recapUiConstants";
import type { RecapSummaryMachine, RecapTimelineResponse } from "@/lib/recap/types";
import RecapMiniTimeline from "@/components/recap/RecapMiniTimeline";
@@ -42,7 +43,8 @@ export default function RecapMachineCard({ machine, rangeStart, rangeEnd }: Prop
const timelineStart = timeline?.range.start ?? rangeStart;
const timelineEnd = timeline?.range.end ?? rangeEnd;
const hasTimelineData = timeline?.hasData ?? timelineSegments.length > 0;
const staleHeartbeat = machine.lastSeenMs == null ? true : nowMs - machine.lastSeenMs > 5 * 60 * 1000;
const staleHeartbeat =
machine.lastSeenMs == null ? true : nowMs - machine.lastSeenMs > RECAP_HEARTBEAT_STALE_MS;
const lastSeenLabel =
machine.lastActivityMin == null

View File

@@ -1,6 +1,7 @@
"use client";
import { useI18n } from "@/lib/i18n/useI18n";
import { formatRecapProgressPercent } from "@/lib/recap/progressDisplay";
import type { RecapSkuRow } from "@/lib/recap/types";
type Props = {
@@ -8,7 +9,7 @@ type Props = {
};
export default function RecapProductionBySku({ rows }: Props) {
const { t } = useI18n();
const { t, locale } = useI18n();
return (
<div className="rounded-2xl border border-white/10 bg-black/40 p-4">
@@ -30,7 +31,8 @@ export default function RecapProductionBySku({ rows }: Props) {
</thead>
<tbody>
{rows.slice(0, 10).map((row) => {
const progress = row.progressPct == null ? "--" : `${Math.round(row.progressPct)}%`;
const progress =
row.progressPct == null ? "—" : formatRecapProgressPercent(row.progressPct, locale);
return (
<tr key={`${row.sku}:${row.machineName}`} className="border-b border-white/5">
<td className="py-2 pr-3">{row.sku}</td>

View File

@@ -1,6 +1,7 @@
"use client";
import { useI18n } from "@/lib/i18n/useI18n";
import { formatRecapProgressPercent, progressBarWidthPercent } from "@/lib/recap/progressDisplay";
import type { RecapMachine } from "@/lib/recap/types";
type Props = {
@@ -22,10 +23,13 @@ export default function RecapWorkOrderStatus({ workOrders }: Props) {
<div className="mt-2 rounded-xl border border-white/10 bg-black/30 p-3 text-sm text-zinc-200">
<div className="font-medium text-white">{workOrders.active.id}</div>
<div className="text-zinc-400">SKU: {workOrders.active.sku || "--"}</div>
<div className="mt-1 text-xs text-zinc-300">
{t("recap.production.progress")}: {formatRecapProgressPercent(workOrders.active.progressPct, locale)}
</div>
<div className="mt-2 h-2 rounded-full bg-white/10">
<div
className="h-2 rounded-full bg-emerald-400"
style={{ width: `${Math.max(0, Math.min(100, workOrders.active.progressPct ?? 0))}%` }}
className="h-2 rounded-full bg-emerald-400 transition-[width]"
style={{ width: `${progressBarWidthPercent(workOrders.active.progressPct)}%` }}
/>
</div>
<div className="mt-2 text-xs text-zinc-400">

View File

@@ -1,6 +1,7 @@
"use client";
import { useI18n } from "@/lib/i18n/useI18n";
import { formatRecapProgressPercent, progressBarWidthPercent } from "@/lib/recap/progressDisplay";
import type { RecapWorkOrders as RecapWorkOrdersType } from "@/lib/recap/types";
type Props = {
@@ -41,10 +42,14 @@ export default function RecapWorkOrders({ workOrders }: Props) {
<div className="mt-2 rounded-lg border border-white/10 bg-black/20 p-3 text-sm text-zinc-200">
<div className="font-medium text-white">{workOrders.active.id}</div>
<div className="text-zinc-400">{t("recap.workOrders.sku")}: {workOrders.active.sku || "--"}</div>
<div className="mt-1 text-xs text-zinc-300">
{t("recap.production.progress")}:{" "}
{formatRecapProgressPercent(workOrders.active.progressPct, locale)}
</div>
<div className="mt-2 h-2 rounded-full bg-white/10">
<div
className="h-2 rounded-full bg-emerald-400"
style={{ width: `${Math.max(0, Math.min(100, workOrders.active.progressPct ?? 0))}%` }}
className="h-2 rounded-full bg-emerald-400 transition-[width]"
style={{ width: `${progressBarWidthPercent(workOrders.active.progressPct)}%` }}
/>
</div>
<div className="mt-2 text-xs text-zinc-400">