"use client"; import Link from "next/link"; import { inflowErrorMessage } from "@/lib/inflow-error"; import { useEffect, useMemo, useRef, useState } from "react"; import { AlertTriangle, ArrowDownToLine, CalendarDays, CheckCircle2, ClipboardList, Database, FileJson, FileSpreadsheet, GitBranch, GraduationCap, Info, PackageCheck, Play, RefreshCcw, RotateCcw, Search, SlidersHorizontal, Truck, Upload, } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { fetchWithCsrf } from "@/lib/fetch-with-csrf"; import { trackToolRunCompleted, trackToolRunStarted } from "@/lib/conversion-events"; import { useAuth } from "@/lib/auth-context"; import { useTenant } from "@/lib/tenant-context"; import { cn } from "@/lib/utils"; import { Tutorial } from "@/components/ui/tutorial"; import { INFLOW_LAB_CLIENT_TUTORIAL_BANNER_KEY, INFLOW_LAB_CLIENT_TUTORIAL_STORAGE_KEY, InflowLabTutorialBanner, buildInflowLabClientTutorialSteps, dismissInflowLabTutorialBanner, } from "./InflowLabTutorial"; type RunStage = "loaded" | "generating" | "delivering" | "validating" | "complete" | "exported"; type EventStatus = "posted" | "validated" | "queued" | "generated" | "failed"; type CteState = "complete" | "missing"; type DeliveryState = "posted" | "generated" | "failed"; type LotReadinessState = "ready" | "warning" | "blocked"; type TraceEvent = { id: number; cte: string; lotCode: string; product: string; location: string; timestamp: string; mode: "simulation" | "uploaded"; attempts: number; deliveryStatus: DeliveryState; }; type LineageNode = { cte: string; location: string; time: string; state: CteState; }; type OptionalOfflinePayload = { available?: boolean; offline?: boolean; detail?: string; }; type InflowHealth = OptionalOfflinePayload & { ok?: boolean; build?: { version?: string; commit_sha_short?: string }; }; type InflowStatus = OptionalOfflinePayload & { running?: boolean; config?: { source?: string; scenario?: string; delivery?: { mode?: string; tenant_id?: string | null }; }; stats?: { total_records?: number; unique_lots?: number; delivery?: { posted?: number; failed?: number; generated?: number; attempts?: number; last_attempt_at?: string | null; last_success_at?: string | null; }; }; }; type InflowRecord = { sequence_no: number; destination_mode: string; delivery_status: "generated" | "posted" | "failed"; delivery_attempts?: number; event: { cte_type: string; traceability_lot_code: string; product_description: string; location_name: string; timestamp: string; }; }; type InflowLineagePayload = { records?: InflowRecord[]; }; function optionalOfflineDetail(...payloads: OptionalOfflinePayload[]) { const offline = payloads.find((payload) => payload.offline || payload.available === false); return offline ? offline.detail || "Inflow Lab service unavailable" : null; } type SandboxEventEvaluation = { event_index: number; cte_type: string; traceability_lot_code: string; product_description: string; kde_errors: string[]; rules_failed: number; rules_warned: number; compliant: boolean; blocking_defects?: { rule_title: string; severity: string; result: string; why_failed: string | null; remediation: string | null; category: string; }[]; all_results?: { rule_title: string; severity: string; result: string; why_failed: string | null; remediation: string | null; category: string; }[]; }; type SandboxEvaluationResult = { total_events: number; compliant_events: number; non_compliant_events: number; total_kde_errors: number; total_rule_failures: number; submission_blocked: boolean; blocking_reasons: string[]; duplicate_warnings?: string[]; entity_warnings?: string[]; normalizations?: { field: string; original: string; normalized: string; action_type: string }[]; events: SandboxEventEvaluation[]; }; type FeederDiagnosis = { status: "clear" | "needs_work" | "blocked"; headline: string; impact: string; buckets: { id: string; label: string; count: number; tone: "danger" | "warning" | "success" | "info" }[]; }; type FeederRemediationStep = { title: string; detail: string; }; type FixQueueItem = { id: string; title: string; owner: string; status: "open" | "waiting" | "corrected" | "accepted"; severity: "blocked" | "warning" | "info"; impact: string; source: string; }; type WorkbenchScenario = { id: string; tenant_id?: string; name: string; outcome: string; records: string; csv: string; created_at?: string; built_in?: boolean; }; type ReadinessSummary = { score: number; label: string; components: { id: string; label: string; score: number; detail: string }[]; }; type CommitGateDecision = { mode: "simulation" | "preflight" | "staging" | "production_evidence"; allowed: boolean; export_eligible: boolean; reasons: string[]; next_state: string; }; type WorkbenchRunResponse = { run_id: string; tenant_id: string; source: string; readiness: ReadinessSummary; fix_queue: FixQueueItem[]; commit_gate: CommitGateDecision; saved_at: string; }; const INFLOW_API = "/api/inflow-lab"; const WORKBENCH_API = "/api/ingestion/api/v1/inflow-workbench"; const REQUIRED_CTES = ["harvesting", "cooling", "initial_packing", "shipping", "receiving"]; const SANDBOX_CTE_TYPES = [ "harvesting", "cooling", "initial_packing", "first_land_based_receiving", "shipping", "receiving", "transformation", ]; const FEEDER_SAMPLE_CSV = `cte_type,traceability_lot_code,product_description,quantity,unit_of_measure,location_name,timestamp,harvest_date,cooling_date,packing_date,ship_date,receive_date,ship_from_location,ship_to_location,receiving_location,immediate_previous_source,reference_document,tlc_source_reference,harvester_business_name,harvest_location_ref harvesting,TLC-FEED-001,Romaine Lettuce,120,cases,Example Grower Co,2026-04-26T15:20:00Z,2026-04-26,,,,,,,,,HARV-001,SRC-HARV-001,Example Grower Co,Example Grower Field 7 cooling,TLC-FEED-001,Romaine Lettuce,120,cases,Salinas Cooling Hub,2026-04-26T18:12:00Z,,2026-04-26,,,,,,,,COOL-001,SRC-COOL-001,,Example Grower Field 7 initial_packing,TLC-FEED-001,Romaine Lettuce,118,cases,Salinas Packhouse,2026-04-26T20:12:00Z,,,2026-04-26,,,,,,,PACK-001,SRC-PACK-001,Example Grower Co,Example Grower Field 7 shipping,TLC-FEED-001,Romaine Lettuce,118,cases,Salinas Packout Dock,2026-04-26T22:41:00Z,,,,2026-04-26,,Salinas Packhouse,Bay Area DC,,,BOL-001,SRC-PACK-001,,Example Grower Field 7 receiving,TLC-FEED-001,Romaine Lettuce,118,cases,Bay Area DC,2026-04-27T02:04:00Z,,,,,2026-04-27,,,Bay Area DC,Salinas Packout Dock,REC-001,SRC-PACK-001,,Example Grower Field 7`; const MISSING_DESTINATION_CSV = `cte_type,traceability_lot_code,product_description,quantity,unit_of_measure,location_name,timestamp,harvest_date,cooling_date,packing_date,ship_date,receive_date,ship_from_location,ship_to_location,receiving_location,immediate_previous_source,reference_document,tlc_source_reference,harvester_business_name,harvest_location_ref harvesting,TLC-FEED-002,Romaine Lettuce,104,cases,Example Grower Co,2026-04-26T15:20:00Z,2026-04-26,,,,,,,,,HARV-002,SRC-HARV-002,Example Grower Co,Example Grower Field 11 cooling,TLC-FEED-002,Romaine Lettuce,104,cases,Salinas Cooling Hub,2026-04-26T18:12:00Z,,2026-04-26,,,,,,,,COOL-002,SRC-COOL-002,,Example Grower Field 11 initial_packing,TLC-FEED-002,Romaine Lettuce,103,cases,Salinas Packhouse,2026-04-26T20:12:00Z,,,2026-04-26,,,,,,,PACK-002,SRC-PACK-002,Example Grower Co,Example Grower Field 11 shipping,TLC-FEED-002,Romaine Lettuce,103,cases,Salinas Packout Dock,2026-04-26T22:41:00Z,,,,2026-04-26,,Salinas Packhouse,,,,BOL-002,SRC-PACK-002,,Example Grower Field 11 receiving,TLC-FEED-002,Romaine Lettuce,103,cases,Bay Area DC,2026-04-27T02:04:00Z,,,,,2026-04-27,,,Bay Area DC,Salinas Packout Dock,REC-002,SRC-PACK-002,,Example Grower Field 11`; const BROKEN_LINEAGE_CSV = `cte_type,traceability_lot_code,product_description,quantity,unit_of_measure,location_name,timestamp,harvest_date,cooling_date,packing_date,ship_date,receive_date,ship_from_location,ship_to_location,receiving_location,immediate_previous_source,reference_document,tlc_source_reference,harvester_business_name,harvest_location_ref,transformation_date,input_traceability_lot_codes harvesting,TLC-FEED-003,Romaine Lettuce,220,cases,Desert Bloom Farm,2026-04-26T14:43:00Z,2026-04-26,,,,,,,,,HARV-003,SRC-HARV-003,Desert Bloom Farm,Desert Bloom Farm Block 3,, cooling,TLC-FEED-003,Romaine Lettuce,220,cases,Imperial Pre-Cool Facility,2026-04-26T17:51:00Z,,2026-04-26,,,,,,,,COOL-003,SRC-COOL-003,,Desert Bloom Farm Block 3,, transformation,TLC-FEED-003-CUT,Romaine Lettuce,218,cases,LA Fresh Cut Kitchen,2026-04-27T00:10:00Z,,,,,,,,,,TRANS-003,SRC-MISSING-PACK,,Desert Bloom Farm Block 3,2026-04-27,`; const FEEDER_SAVED_RUN_KEY = "regengine:inflow-lab:last-feeder-run"; const scenarioLibrary: WorkbenchScenario[] = [ { id: "complete-romaine-flow", name: "Complete romaine lettuce flow", outcome: "Export-ready full chain", records: "5 CTE records", csv: FEEDER_SAMPLE_CSV, }, { id: "missing-shipping-destination", name: "Missing shipping destination", outcome: "Blocked shipping KDE", records: "5 CTE records", csv: MISSING_DESTINATION_CSV, }, { id: "broken-lineage", name: "Transformation-style broken lineage", outcome: "Incomplete lot chain", records: "3 CTE records", csv: BROKEN_LINEAGE_CSV, }, ]; function servicePath(path: string) { return `${INFLOW_API}${path}`; } function safeHref(raw: string): string { try { const parsed = new URL(raw, "https://regengine.invalid"); if (parsed.protocol === "http:" || parsed.protocol === "https:") { return raw.startsWith("/") ? `${parsed.pathname}${parsed.search}${parsed.hash}` : parsed.toString(); } } catch { // fall through } return "#"; } async function inflowJson(path: string, init?: RequestInit): Promise { const headers = new Headers(init?.headers); if (!headers.has("content-type")) { headers.set("content-type", "application/json"); } // Mutating /api/inflow-lab calls are CSRF-protected by proxy.ts; match workbenchJson. const response = await fetchWithCsrf(servicePath(path), { cache: "no-store", ...init, headers, }); if (!response.ok) { const payload = await response.json().catch(() => ({})); throw new Error(inflowErrorMessage(response.status, payload)); } return response.json(); } async function workbenchJson(path: string, init?: RequestInit): Promise { const headers = new Headers(init?.headers); if (!headers.has("content-type")) { headers.set("content-type", "application/json"); } const response = await fetchWithCsrf(`${WORKBENCH_API}${path}`, { cache: "no-store", ...init, headers, }); if (!response.ok) { const payload = await response.json().catch(() => ({})); throw new Error(payload.error || payload.detail || `Inflow Workbench request failed: ${response.status}`); } return response.json(); } function formatServiceTime(value: string) { return new Date(value).toLocaleString(undefined, { month: "short", day: "numeric", year: "numeric", hour: "numeric", minute: "2-digit", }); } function toTraceEvent(record: InflowRecord): TraceEvent { return { id: record.sequence_no, cte: record.event.cte_type, lotCode: record.event.traceability_lot_code, product: record.event.product_description, location: record.event.location_name, timestamp: formatServiceTime(record.event.timestamp), mode: "simulation", attempts: record.delivery_attempts || 0, deliveryStatus: record.delivery_status, }; } function sandboxEventToTraceEvent(event: SandboxEventEvaluation): TraceEvent { return { id: event.event_index + 1, cte: event.cte_type, lotCode: event.traceability_lot_code || `row-${event.event_index + 1}`, product: event.product_description || "Uploaded traceability record", location: `Uploaded row ${event.event_index + 1}`, timestamp: formatServiceTime(new Date().toISOString()), mode: "uploaded", attempts: 1, deliveryStatus: event.compliant ? "posted" : "generated", }; } function lineageByLotFromEvents(traceEvents: TraceEvent[]) { return traceEvents.reduce>((current, event) => { const lotEvents = traceEvents.filter((candidate) => candidate.lotCode === event.lotCode); current[event.lotCode] = toLineage(lotEvents); return current; }, {}); } function formatCteLabel(cte: string) { if (cte === "receiving") return "DC receiving"; return cte.replaceAll("_", " "); } function toLineage(eventsForLot: TraceEvent[]): LineageNode[] { const byCte = new Map(eventsForLot.map((event) => [event.cte, event])); return REQUIRED_CTES.map((cte) => { const event = byCte.get(cte); return { cte: formatCteLabel(cte), location: event?.location || "Not captured", time: event?.timestamp || "Missing CTE", state: event ? "complete" : "missing", }; }); } function getLotReadiness(lineage: LineageNode[], lotEvents: TraceEvent[]) { const completeCount = lineage.filter((node) => node.state === "complete").length; const totalCount = lineage.length; const failedDeliveries = lotEvents.filter((event) => event.deliveryStatus === "failed").length; const generatedOnly = lotEvents.filter((event) => event.deliveryStatus === "generated").length; const missingCtes = lineage.filter((node) => node.state === "missing").map((node) => node.cte); const hasCompleteLineage = totalCount > 0 && completeCount === totalCount; if (failedDeliveries > 0 || lotEvents.length === 0) { return { state: "blocked" as LotReadinessState, label: "blocked", completeCount, totalCount, missingCtes, deliveryLabel: failedDeliveries > 0 ? "delivery failed" : "no events", exportReady: false, }; } if (!hasCompleteLineage || generatedOnly > 0) { return { state: "warning" as LotReadinessState, label: "exception", completeCount, totalCount, missingCtes, deliveryLabel: generatedOnly > 0 ? "generated only" : "posted", exportReady: false, }; } return { state: "ready" as LotReadinessState, label: "test complete", completeCount, totalCount, missingCtes, deliveryLabel: "posted", exportReady: true, }; } function summarizeFeederDiagnosis(result: SandboxEvaluationResult): FeederDiagnosis { const issueCount = result.total_kde_errors + result.total_rule_failures; const status: FeederDiagnosis["status"] = result.submission_blocked ? "blocked" : issueCount > 0 || result.non_compliant_events > 0 ? "needs_work" : "clear"; return { status, headline: status === "clear" ? "CSV check found no blocking fixes" : status === "blocked" ? "CSV check found blockers before mapping" : "CSV check found corrections before mapping", impact: status === "clear" ? "This test run can move into import mapping, but it still cannot create formal production records." : "Correct the highlighted rows and fields before treating this source as a monitored production feed.", buckets: [ { id: "events", label: "Rows checked", count: result.total_events, tone: "info" }, { id: "passed", label: "Rows passing", count: result.compliant_events, tone: "success" }, { id: "kde", label: "KDE fixes", count: result.total_kde_errors, tone: result.total_kde_errors ? "warning" : "success" }, { id: "rules", label: "Rule failures", count: result.total_rule_failures, tone: result.total_rule_failures ? "danger" : "success" }, ], }; } function buildFeederRemediationPlan(result: SandboxEvaluationResult): FeederRemediationStep[] { const steps: FeederRemediationStep[] = []; if (result.total_kde_errors > 0) { steps.push({ title: "Correct missing KDE fields", detail: "Fill required lot, location, quantity, timestamp, and handoff fields in the source CSV before mapping.", }); } if (result.total_rule_failures > 0) { steps.push({ title: "Resolve failed FSMA checks", detail: "Review the failed row-level rules, fix source data, and rerun the CSV check.", }); } if (result.duplicate_warnings?.length || result.entity_warnings?.length) { steps.push({ title: "Review duplicate and entity warnings", detail: "Confirm aliases, facility names, and duplicate rows before connecting a production feed.", }); } steps.push({ title: "Map only after diagnosis", detail: "Use the test run to configure import mapping; formal production records still require signed-in imports.", }); return steps; } function buildFixQueue(result: SandboxEvaluationResult | null, lotSummaries: { lotCode: string; product: string; missingCtes: string[]; readiness: ReturnType; }[]): FixQueueItem[] { const items: FixQueueItem[] = []; lotSummaries .filter((lot) => !lot.readiness.exportReady) .forEach((lot) => { items.push({ id: `lot-${lot.lotCode}`, title: lot.missingCtes.length ? `${lot.product} missing KDE evidence for ${lot.missingCtes.join(", ")}` : `${lot.product} needs delivery review before evidence handoff`, owner: lot.readiness.state === "blocked" ? "Integration owner" : "Operations analyst", status: lot.readiness.state === "blocked" ? "open" : "waiting", severity: lot.readiness.state === "blocked" ? "blocked" : "warning", impact: "Excluded from export-ready counts until lineage and delivery are complete.", source: lot.lotCode, }); }); result?.events .filter((event) => !event.compliant || event.kde_errors.length || event.rules_failed > 0) .slice(0, 5) .forEach((event) => { const firstDefect = event.kde_errors[0] || event.blocking_defects?.[0]?.rule_title || "failed rule evaluation"; items.push({ id: `row-${event.event_index}`, title: `Row ${event.event_index + 1} ${event.cte_type} needs ${firstDefect}`, owner: "Source data owner", status: result.submission_blocked ? "open" : "waiting", severity: result.submission_blocked ? "blocked" : "warning", impact: event.blocking_defects?.[0]?.remediation || "Correct the source record and rerun CSV validation.", source: event.traceability_lot_code || `row-${event.event_index + 1}`, }); }); result?.blocking_reasons.slice(0, 3).forEach((reason, index) => { items.push({ id: `blocking-${index}`, title: reason, owner: "Implementation", status: "open", severity: "blocked", impact: "Commit gate stays closed until this blocker is resolved.", source: "CSV evaluator", }); }); if (result?.duplicate_warnings?.length) { items.push({ id: "duplicate-risk", title: `${result.duplicate_warnings.length} duplicate or idempotency risks need review`, owner: "Integration owner", status: "waiting", severity: "warning", impact: "Duplicate records can weaken audit trust and supplier feed quality.", source: "CSV evaluator", }); } return items.slice(0, 8); } function getLotFixGuidance(lot: { missingCtes: string[]; readiness: ReturnType; }) { if (lot.readiness.state === "blocked") { return { reason: "Delivery failed or no traceability events were received.", owner: "Integration owner", next: "Review delivery status, then replay the source.", }; } const lowerMissing = lot.missingCtes.join(" ").toLowerCase(); if (lowerMissing.includes("shipping") || lowerMissing.includes("receiving")) { return { reason: `Missing handoff evidence for ${lot.missingCtes.join(", ")}.`, owner: "Shipping or receiving source", next: "Map the handoff fields or upload a corrected CSV.", }; } return { reason: lot.missingCtes.length ? `Missing source evidence for ${lot.missingCtes.join(", ")}.` : "Delivery posted, but the lot is not test complete.", owner: "Supplier data owner", next: "Open the fix queue and rerun validation after correction.", }; } const standaloneTabs = ["Control room", "Data feeder", "Fix queue", "Scenarios", "Suppliers", "Lots", "Lineage", "Test previews", "Event log", "Diagnostics"]; const dashboardTabs = ["Overview", "Data feeder", "Fix queue", "Lots", "Lineage", "Test previews", "Record log", "Diagnostics"]; const lotCodes = [ "00614141000012-20260426-000001", "00614141000012-20260426-000002", "00614141000012-20260426-000003", ]; const events: TraceEvent[] = [ { id: 15, cte: "receiving", lotCode: lotCodes[0], product: "Green Leaf Lettuce", location: "Bay Area DC", timestamp: "Apr 27, 2026, 2:04 AM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 14, cte: "shipping", lotCode: lotCodes[0], product: "Green Leaf Lettuce", location: "Salinas Packout Dock", timestamp: "Apr 26, 2026, 10:41 PM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 13, cte: "initial_packing", lotCode: lotCodes[0], product: "Green Leaf Lettuce", location: "Salinas Packhouse", timestamp: "Apr 26, 2026, 8:12 PM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 12, cte: "cooling", lotCode: lotCodes[0], product: "Green Leaf Lettuce", location: "Salinas Cooling Hub", timestamp: "Apr 26, 2026, 6:12 PM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 11, cte: "harvesting", lotCode: lotCodes[0], product: "Green Leaf Lettuce", location: "Example Grower Co", timestamp: "Apr 26, 2026, 3:20 PM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 10, cte: "receiving", lotCode: lotCodes[1], product: "Spring Mix", location: "Los Angeles DC", timestamp: "Apr 27, 2026, 1:24 AM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 9, cte: "shipping", lotCode: lotCodes[1], product: "Spring Mix", location: "Imperial Packout Dock", timestamp: "Apr 26, 2026, 10:03 PM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 8, cte: "initial_packing", lotCode: lotCodes[1], product: "Spring Mix", location: "Imperial Packhouse", timestamp: "Apr 26, 2026, 7:18 PM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 7, cte: "cooling", lotCode: lotCodes[1], product: "Spring Mix", location: "Imperial Pre-Cool Facility", timestamp: "Apr 26, 2026, 4:51 PM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 6, cte: "harvesting", lotCode: lotCodes[1], product: "Spring Mix", location: "Desert Bloom Farm", timestamp: "Apr 26, 2026, 3:43 PM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 5, cte: "cooling", lotCode: lotCodes[2], product: "Green Leaf Lettuce", location: "Salinas Cooling Hub", timestamp: "Apr 26, 2026, 5:26 PM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, { id: 4, cte: "harvesting", lotCode: lotCodes[2], product: "Green Leaf Lettuce", location: "Example Grower Co", timestamp: "Apr 26, 2026, 4:28 PM", mode: "simulation", attempts: 1, deliveryStatus: "posted", }, ]; const lineageByLot: Record = { [lotCodes[0]]: [ { cte: "Harvesting", location: "Example Grower Co", time: "Apr 26, 3:20 PM", state: "complete" }, { cte: "Cooling", location: "Salinas Cooling Hub", time: "Apr 26, 6:12 PM", state: "complete" }, { cte: "Initial packing", location: "Salinas Packhouse", time: "Apr 26, 8:12 PM", state: "complete" }, { cte: "Shipping", location: "Salinas Packout Dock", time: "Apr 26, 10:41 PM", state: "complete" }, { cte: "DC receiving", location: "Bay Area DC", time: "Apr 27, 2:04 AM", state: "complete" }, ], [lotCodes[1]]: [ { cte: "Harvesting", location: "Desert Bloom Farm", time: "Apr 26, 3:43 PM", state: "complete" }, { cte: "Cooling", location: "Imperial Pre-Cool Facility", time: "Apr 26, 4:51 PM", state: "complete" }, { cte: "Initial packing", location: "Imperial Packhouse", time: "Apr 26, 7:18 PM", state: "complete" }, { cte: "Shipping", location: "Imperial Packout Dock", time: "Apr 26, 10:03 PM", state: "complete" }, { cte: "DC receiving", location: "Los Angeles DC", time: "Apr 27, 1:24 AM", state: "complete" }, ], [lotCodes[2]]: [ { cte: "Harvesting", location: "Example Grower Co", time: "Apr 26, 4:28 PM", state: "complete" }, { cte: "Cooling", location: "Salinas Cooling Hub", time: "Apr 26, 5:26 PM", state: "complete" }, { cte: "Initial packing", location: "Not captured", time: "Missing KDEs", state: "missing" }, { cte: "Shipping", location: "Not captured", time: "Missing CTE", state: "missing" }, { cte: "DC receiving", location: "Not captured", time: "Missing CTE", state: "missing" }, ], }; const stageIndex: Record = { loaded: 0, generating: 1, delivering: 2, validating: 3, complete: 4, exported: 5, }; function getEventStatus(stage: RunStage): EventStatus { if (stage === "generating") return "queued"; if (stage === "validating") return "validated"; return "posted"; } function StatusPill({ status }: { status: EventStatus }) { const styles = { posted: "border-emerald-200 bg-emerald-50 text-emerald-700", validated: "border-blue-200 bg-blue-50 text-blue-700", queued: "border-amber-200 bg-amber-50 text-amber-700", generated: "border-amber-200 bg-amber-50 text-amber-700", failed: "border-amber-200 bg-amber-50 text-amber-800", }; return ( {status} ); } function LotReadinessPill({ state, label }: { state: LotReadinessState; label: string }) { const styles = { ready: "border-emerald-200 bg-emerald-50 text-emerald-700", warning: "border-amber-200 bg-amber-50 text-amber-700", blocked: "border-amber-200 bg-amber-50 text-amber-800", }; return ( {label} ); } function LineageStatusIcon({ state }: { state: CteState }) { if (state === "complete") return ; return ; } type InflowLabClientProps = { mode?: "standalone" | "dashboard"; /** When false, CSV evaluate stays disabled (experimental evaluator not in this deploy). */ csvEvaluateEnabled?: boolean; }; export function InflowLabClient({ mode = "standalone", csvEvaluateEnabled = true, }: InflowLabClientProps) { const { isAuthenticated, isHydrated } = useAuth(); const { tenantId: activeTenantId } = useTenant(); const isStandalone = mode === "standalone"; const primaryTab = isStandalone ? "Control room" : "Overview"; const tabs = isStandalone ? standaloneTabs : dashboardTabs; const [activeTab, setActiveTab] = useState(primaryTab); const [isTutorialOpen, setIsTutorialOpen] = useState(false); const [selectedLot, setSelectedLot] = useState(lotCodes[0]); const [traceInput, setTraceInput] = useState(lotCodes[0]); const [runStage, setRunStage] = useState("complete"); const [health, setHealth] = useState(null); const [serviceStatus, setServiceStatus] = useState(null); const [serviceEvents, setServiceEvents] = useState([]); const [serviceLineage, setServiceLineage] = useState>({}); const [serviceError, setServiceError] = useState(null); const [isBusy, setIsBusy] = useState(false); const [tenantId, setTenantId] = useState("mock-tenant"); const [scenarioPreset, setScenarioPreset] = useState("leafy_greens_supplier"); const [fixture, setFixture] = useState("leafy_greens_trace"); const [deliveryMode, setDeliveryMode] = useState("mock"); const [source, setSource] = useState("codex-simulator"); const [exportPreset, setExportPreset] = useState("all_records"); const [startDate, setStartDate] = useState("2026-04-26"); const [endDate, setEndDate] = useState("2026-04-27"); const [feederCsv, setFeederCsv] = useState(FEEDER_SAMPLE_CSV); const [feederResult, setFeederResult] = useState(null); const [feederError, setFeederError] = useState(null); const [feederSavedAt, setFeederSavedAt] = useState(null); const [isFeederEvaluating, setIsFeederEvaluating] = useState(false); const [activeScenarioId, setActiveScenarioId] = useState(scenarioLibrary[0].id); const [workbenchScenarios, setWorkbenchScenarios] = useState(scenarioLibrary); const [backendReadiness, setBackendReadiness] = useState(null); const [backendFixQueue, setBackendFixQueue] = useState([]); const [commitGateDecision, setCommitGateDecision] = useState(null); const [workbenchRunId, setWorkbenchRunId] = useState(null); const [workbenchError, setWorkbenchError] = useState(null); const feederFileInputRef = useRef(null); // Bumped whenever the CSV input changes so a late evaluate response cannot // overwrite the current feederCsv / saved-run pairing. const feederInputRevisionRef = useRef(0); useEffect(() => { if (!isHydrated || !isAuthenticated || !activeTenantId) { return; } setTenantId((currentTenantId) => currentTenantId === activeTenantId ? currentTenantId : activeTenantId ); }, [activeTenantId, isAuthenticated, isHydrated]); const refreshService = async () => { const [healthPayload, statusPayload, eventsPayload] = await Promise.all([ inflowJson("/api/healthz"), inflowJson("/api/simulate/status"), inflowJson("/api/events?limit=100"), ]); const nextEvents = (eventsPayload.events || []).map(toTraceEvent); setHealth(healthPayload); setServiceStatus(statusPayload); setServiceEvents(nextEvents); setServiceError(optionalOfflineDetail(healthPayload, statusPayload, eventsPayload)); const firstLot = nextEvents[0]?.lotCode; if (firstLot && (!traceInput || traceInput === lotCodes[0])) { setSelectedLot(firstLot); setTraceInput(firstLot); } }; const selectedEvents = useMemo( () => (serviceEvents.length ? serviceEvents : events).filter((event) => event.lotCode === selectedLot), [selectedLot, serviceEvents] ); const activeEvents = serviceEvents.length ? serviceEvents : events; const activeLineageByLot = serviceEvents.length ? serviceLineage : lineageByLot; const selectedLineage = activeLineageByLot[selectedLot] ?? toLineage(selectedEvents); const selectedReadiness = getLotReadiness(selectedLineage, selectedEvents); const selectedCompleteCount = selectedReadiness.completeCount; const selectedIsExportReady = selectedReadiness.exportReady; const hasGeneratedRecords = runStage !== "loaded"; const visibleEvents = hasGeneratedRecords ? activeEvents : []; const visibleEventStatus = serviceEvents.length ? "posted" : getEventStatus(runStage); const localDeliveryCounts = useMemo( () => activeEvents.reduce( (counts, event) => { counts.attempts += event.attempts; if (event.deliveryStatus === "posted") counts.posted += 1; if (event.deliveryStatus === "failed") counts.failed += 1; if (event.deliveryStatus === "generated") counts.generated += 1; return counts; }, { posted: 0, failed: 0, generated: 0, attempts: 0 } ), [activeEvents] ); const deliveredCount = serviceStatus?.stats?.delivery?.posted ?? localDeliveryCounts.posted; useEffect(() => { if (isStandalone) { document.body.dataset.inflowLab = "true"; } refreshService().catch((error) => setServiceError(error instanceof Error ? error.message : "Inflow Lab service unavailable")); return () => { if (isStandalone) { delete document.body.dataset.inflowLab; } }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [isStandalone]); useEffect(() => { if (!isHydrated) { return; } if (!isAuthenticated) { setWorkbenchScenarios(scenarioLibrary); return; } workbenchJson("/scenarios") .then((scenarios) => { if (scenarios.length) { setWorkbenchScenarios(scenarios); return; } setWorkbenchScenarios(scenarioLibrary); }) .catch(() => { setWorkbenchScenarios(scenarioLibrary); }); }, [isAuthenticated, isHydrated]); useEffect(() => { if (!serviceEvents.length || !selectedLot || serviceLineage[selectedLot]) return; inflowJson(`/api/lineage/${encodeURIComponent(selectedLot)}`) .then((payload) => { const lineageEvents = (payload.records || []).map(toTraceEvent); setServiceLineage((current) => ({ ...current, [selectedLot]: toLineage(lineageEvents) })); }) .catch((error) => setServiceError(error instanceof Error ? error.message : "Could not trace lot")); }, [selectedLot, serviceEvents.length, serviceLineage]); const runStatusLabel = runStage === "exported" ? "Mock export filters prepared" : runStage === "complete" ? "Mock run complete" : runStage === "loaded" ? "Mock scenario loaded" : "Mock run in progress"; const uniqueLots = useMemo( () => Array.from(new Set(activeEvents.map((event) => event.lotCode))).map((lotCode) => { const lotEvents = activeEvents.filter((event) => event.lotCode === lotCode); const lineage = activeLineageByLot[lotCode] ?? toLineage(lotEvents); const product = lotEvents[0]?.product ?? "Unknown product"; const readiness = getLotReadiness(lineage, lotEvents); return { lotCode, product, events: lotEvents.length, readiness, missingCtes: readiness.missingCtes, completeCount: readiness.completeCount, totalCount: readiness.totalCount, lastLocation: lotEvents[0]?.location ?? "No location", }; }), [activeEvents, activeLineageByLot] ); const exportReadyLots = uniqueLots.filter((lot) => lot.readiness.exportReady); const exceptionLots = uniqueLots.filter((lot) => !lot.readiness.exportReady); const warningLots = uniqueLots.filter((lot) => lot.readiness.state === "warning"); const blockedLots = uniqueLots.filter((lot) => lot.readiness.state === "blocked"); const loadScenario = async () => { setIsBusy(true); try { await inflowJson(`/api/demo-fixtures/${fixture}/load`, { method: "POST", body: JSON.stringify({ reset: true, source, delivery: { mode: deliveryMode, tenant_id: tenantId || null }, }), }); await refreshService(); setRunStage("complete"); } catch (error) { setServiceError(error instanceof Error ? error.message : "Could not load Inflow Lab fixture"); setRunStage("loaded"); } finally { setIsBusy(false); } setActiveTab(primaryTab); }; const runPipeline = async () => { trackToolRunStarted("inflow-lab"); setIsBusy(true); setRunStage("generating"); setActiveTab(primaryTab); try { await inflowJson("/api/simulate/start", { method: "POST", body: JSON.stringify({ config: { source, scenario: scenarioPreset, interval_seconds: 0.1, batch_size: 1, persist_path: "data/events.jsonl", delivery: { mode: deliveryMode, tenant_id: tenantId || null }, }, }), }); setRunStage("delivering"); await new Promise((resolve) => window.setTimeout(resolve, 700)); await inflowJson("/api/simulate/stop", { method: "POST" }); await refreshService(); setRunStage("complete"); trackToolRunCompleted("inflow-lab", "success"); } catch (error) { setServiceError(error instanceof Error ? error.message : "Could not run Inflow Lab pipeline"); setRunStage("loaded"); trackToolRunCompleted("inflow-lab", "error"); } finally { setIsBusy(false); } }; const resetServiceState = async () => { setIsBusy(true); try { await inflowJson("/api/simulate/reset", { method: "POST", body: JSON.stringify({ source, scenario: scenarioPreset, delivery: { mode: deliveryMode, tenant_id: tenantId || null }, }), }); setServiceEvents([]); setServiceLineage({}); await refreshService(); setRunStage("loaded"); } catch (error) { setServiceError(error instanceof Error ? error.message : "Could not reset Inflow Lab state"); } finally { setIsBusy(false); } }; const evaluateFeederData = async () => { if (!csvEvaluateEnabled) { setFeederError("CSV evaluation needs experimental features enabled on this deployment."); return; } if (!feederCsv.trim()) { setFeederError("Paste CSV text or upload a CSV file before evaluating."); return; } trackToolRunStarted("inflow-lab"); setIsFeederEvaluating(true); setFeederError(null); const evaluationRevision = feederInputRevisionRef.current; try { const response = await fetchWithCsrf("/api/ingestion/api/v1/sandbox/evaluate", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ csv: feederCsv }), }); if (!response.ok) { const payload = await response.json().catch(() => ({})); const detail = typeof payload.detail === "string" ? payload.detail : `CSV evaluation failed: ${response.status}`; throw new Error(detail); } const result = (await response.json()) as SandboxEvaluationResult; if (evaluationRevision !== feederInputRevisionRef.current) { // CSV changed while this request was in flight — drop the stale result. return; } const evaluatedEvents = result.events.map(sandboxEventToTraceEvent); setFeederResult(result); setFeederSavedAt(null); setWorkbenchRunId(null); setWorkbenchError(null); setServiceEvents(evaluatedEvents); setServiceLineage(lineageByLotFromEvents(evaluatedEvents)); setServiceStatus(null); setRunStage("complete"); setSource("sandbox-data-feeder"); const firstLot = evaluatedEvents[0]?.lotCode; if (firstLot) { setSelectedLot(firstLot); setTraceInput(firstLot); } try { const [readiness, gate] = await Promise.all([ workbenchJson("/readiness/preview", { method: "POST", body: JSON.stringify(result), }), workbenchJson("/commit-gate", { method: "POST", body: JSON.stringify({ mode: "preflight", tenant_id: tenantId || "default", result, authenticated: false, persisted: false, provenance_attached: false, }), }), ]); setBackendReadiness(typeof readiness.score === "number" ? readiness : null); setCommitGateDecision(Array.isArray(gate.reasons) ? gate : null); setBackendFixQueue([]); } catch (workbenchIssue) { setBackendReadiness(null); setCommitGateDecision(null); setWorkbenchError( workbenchIssue instanceof Error ? `Workbench preview unavailable: ${workbenchIssue.message}` : "Workbench preview unavailable" ); } trackToolRunCompleted("inflow-lab", "success"); } catch (error) { setFeederError(error instanceof Error ? error.message : "Could not evaluate pasted data"); trackToolRunCompleted("inflow-lab", "error"); } finally { setIsFeederEvaluating(false); } }; const handleFeederFile = (file: File | null) => { if (!file) return; const reader = new FileReader(); reader.onload = () => { feederInputRevisionRef.current += 1; setFeederCsv(String(reader.result || "")); setFeederResult(null); setFeederSavedAt(null); setFeederError(null); setBackendReadiness(null); setBackendFixQueue([]); setCommitGateDecision(null); setWorkbenchRunId(null); }; reader.onerror = () => setFeederError("Could not read the selected CSV file."); reader.readAsText(file); }; const loadFeederScenario = (scenarioId: string) => { const scenario = workbenchScenarios.find((item) => item.id === scenarioId) || scenarioLibrary.find((item) => item.id === scenarioId); if (!scenario) return; setActiveScenarioId(scenario.id); feederInputRevisionRef.current += 1; setFeederCsv(scenario.csv); setFeederResult(null); setFeederSavedAt(null); setFeederError(null); setBackendReadiness(null); setBackendFixQueue([]); setCommitGateDecision(null); setWorkbenchRunId(null); setActiveTab("Data feeder"); }; const saveFeederRun = async () => { if (!feederResult) return; const savedAt = new Date().toISOString(); window.localStorage.setItem( FEEDER_SAVED_RUN_KEY, JSON.stringify({ saved_at: savedAt, csv: feederCsv, result: feederResult, source: "inflow-lab-data-feeder", }) ); setFeederSavedAt(savedAt); setWorkbenchError(null); try { const run = await workbenchJson("/runs", { method: "POST", body: JSON.stringify({ tenant_id: tenantId || "default", source: "inflow-lab-data-feeder", csv: feederCsv, result: feederResult, }), }); if (run.run_id) { setWorkbenchRunId(run.run_id); } if (run.readiness && typeof run.readiness.score === "number") { setBackendReadiness(run.readiness); } if (Array.isArray(run.fix_queue)) { setBackendFixQueue(run.fix_queue); } if (run.commit_gate && Array.isArray(run.commit_gate.reasons)) { setCommitGateDecision(run.commit_gate); } if (run.saved_at) { setFeederSavedAt(run.saved_at); } } catch (error) { setWorkbenchError( error instanceof Error ? `Saved locally; backend workbench save unavailable: ${error.message}` : "Saved locally; backend workbench save unavailable" ); } }; const traceLot = async () => { if (traceInput) { setSelectedLot(traceInput); setActiveTab("Lineage"); } try { const payload = await inflowJson(`/api/lineage/${encodeURIComponent(traceInput)}`); const lineageEvents = (payload.records || []).map(toTraceEvent); setServiceLineage((current) => ({ ...current, [traceInput]: toLineage(lineageEvents) })); setServiceError(null); } catch (error) { setServiceError(error instanceof Error ? error.message : "Could not trace lot"); } }; const traceLatestLot = () => { const latestLot = visibleEvents[0]?.lotCode || lotCodes[0]; setSelectedLot(latestLot); setTraceInput(latestLot); setActiveTab("Lineage"); }; const prepareExport = () => { setRunStage("exported"); setActiveTab("Test previews"); }; const startMachineDemo = async () => { if (visibleEvents.length === 0) { await loadScenario(); } await runPipeline(); setActiveTab(isStandalone ? "Event log" : "Record log"); }; // Rebuild steps each render (avoids react-hooks/refs on a demo callback). // Tutorial measures against currentStep + step.target, not step object // identity, so a fresh steps array no longer re-fires scrollIntoView. const tutorialSteps = buildInflowLabClientTutorialSteps({ runDemo: () => { void startMachineDemo(); }, isStandalone, }); const startTutorial = () => { // Reset to the primary tab so every step's anchor is where the copy // says it is, regardless of where the user wandered beforehand. setActiveTab(primaryTab); setIsTutorialOpen(true); }; const csvExportHref = safeHref( servicePath( `/api/regengine/export/fda-request?preset=${encodeURIComponent(exportPreset)}&traceability_lot_code=${encodeURIComponent(traceInput)}&start_date=${encodeURIComponent(startDate)}&end_date=${encodeURIComponent(endDate)}`, ), ); const epcisExportHref = safeHref( servicePath( `/api/regengine/export/epcis?traceability_lot_code=${encodeURIComponent(traceInput)}&start_date=${encodeURIComponent(startDate)}&end_date=${encodeURIComponent(endDate)}`, ), ); const engineConnected = Boolean(health?.ok && !serviceError); const connectionLabel = engineConnected ? "Connection ready" : serviceError ? "Connection needs attention" : "Checking connection"; const connectionTone = engineConnected ? "border-emerald-200 bg-emerald-50 text-emerald-700" : "border-amber-200 bg-amber-50 text-amber-800"; const postedCount = serviceStatus?.stats?.delivery?.posted ?? deliveredCount; const failedCount = serviceStatus?.stats?.delivery?.failed ?? localDeliveryCounts.failed; const generatedOnlyCount = serviceStatus?.stats?.delivery?.generated ?? localDeliveryCounts.generated; const attemptCount = serviceStatus?.stats?.delivery?.attempts ?? localDeliveryCounts.attempts; const totalLotCount = uniqueLots.length; const exceptionCount = exceptionLots.length + failedCount; const feederDefectCount = feederResult ? feederResult.total_kde_errors + feederResult.total_rule_failures : 0; const validationOutcome = feederResult ? feederDefectCount ? `${feederDefectCount} validation issue${feederDefectCount === 1 ? "" : "s"} found` : "No CSV validation issues" : exceptionCount ? `${exceptionCount} exception${exceptionCount === 1 ? "" : "s"} to review` : "No blocking exceptions"; const exportReadyCount = exportReadyLots.length; const exportReadinessLabel = totalLotCount > 0 ? `${exportReadyCount} of ${totalLotCount} lots test complete` : "No test records loaded"; const lastReceivedLabel = serviceStatus?.stats?.delivery?.last_success_at ? formatServiceTime(serviceStatus.stats.delivery.last_success_at) : visibleEvents[0]?.timestamp || "No inbound records yet"; const feederCteCoverage = useMemo(() => { const covered = new Set((feederResult?.events || []).map((event) => event.cte_type).filter(Boolean)); return { covered: SANDBOX_CTE_TYPES.filter((cte) => covered.has(cte)), missing: SANDBOX_CTE_TYPES.filter((cte) => !covered.has(cte)), }; }, [feederResult]); const feederDiagnosis = useMemo(() => (feederResult ? summarizeFeederDiagnosis(feederResult) : null), [feederResult]); const feederRemediationPlan = useMemo(() => (feederResult ? buildFeederRemediationPlan(feederResult) : []), [feederResult]); const readinessScore = useMemo(() => { if (backendReadiness) return backendReadiness.score; if (totalLotCount === 0) return 0; const lotCoverage = exportReadyLots.length / totalLotCount; const selectedCoverage = selectedLineage.length ? selectedCompleteCount / selectedLineage.length : 0; const deliveryTotal = Math.max(1, postedCount + failedCount + generatedOnlyCount); const deliveryQuality = postedCount / deliveryTotal; const feederQuality = feederResult ? feederResult.total_events > 0 ? feederResult.compliant_events / feederResult.total_events : 0 : exceptionCount ? 0.72 : 1; return Math.max( 0, Math.min( 100, Math.round(lotCoverage * 36 + selectedCoverage * 18 + deliveryQuality * 22 + feederQuality * 18 + (engineConnected ? 6 : 0)) ) ); }, [ engineConnected, backendReadiness, exceptionCount, exportReadyLots.length, failedCount, feederResult, generatedOnlyCount, postedCount, selectedCompleteCount, selectedLineage.length, totalLotCount, ]); const fixQueue = useMemo( () => (backendFixQueue.length ? backendFixQueue : buildFixQueue(feederResult, uniqueLots)), [backendFixQueue, feederResult, uniqueLots] ); const unresolvedFixCount = fixQueue.filter((item) => item.status === "open" || item.status === "waiting").length; const supplierReadiness = [ { name: "Example Grower Co", status: exportReadyCount > 0 ? "Passing validation" : "Fixture received", score: Math.max(58, readinessScore - 3), blocker: exceptionLots.length ? "Waiting on missing CTE handoffs" : "No open blockers", }, { name: "FreshPack Central", status: feederResult ? (feederResult.submission_blocked ? "Fixture received" : "Mapping configured") : "Invited", score: feederResult ? Math.max(35, Math.round((feederResult.compliant_events / Math.max(1, feederResult.total_events)) * 100)) : 44, blocker: feederResult?.submission_blocked ? "CSV blockers need correction" : "Mapping profile not promoted", }, { name: "Bay Area DC", status: selectedIsExportReady ? "Export ready" : "Production feed active", score: Math.min(98, readinessScore + 5), blocker: selectedIsExportReady ? "No open blockers" : "Selected lot needs complete lineage", }, ]; const supplierAverageScore = Math.round( supplierReadiness.reduce((total, supplier) => total + supplier.score, 0) / Math.max(1, supplierReadiness.length) ); const activeDataBoundary = feederResult ? "Uploaded CSV check run" : serviceEvents.length ? "Mock feed data" : "Bundled mock fixture"; const boundaryStateDetail = feederResult ? "You are viewing uploaded CSV data checked in this browser session." : serviceEvents.length ? "You are viewing mock feed data; no uploaded CSV check run has been evaluated yet." : "You are viewing bundled fixture data that is safe for exploration and walkthroughs."; const boundarySteps = [ { label: "CSV check", value: feederResult ? "Uploaded CSV was checked" : "No uploaded CSV run yet", tone: "border-blue-200 bg-blue-50 text-blue-900", }, { label: "Mock Inflow Lab", value: "Test records only; not production ingestion", tone: "border-amber-200 bg-amber-50 text-amber-950", }, { label: "Authenticated feed", value: "Starts after signed-in import mapping", tone: "border-slate-200 bg-white text-slate-900", }, { label: "Production evidence", value: "Formal production records come from signed-in imports", tone: "border-emerald-200 bg-emerald-50 text-emerald-950", }, ]; const machineDemoSteps = [ { label: "Load test records", detail: visibleEvents.length > 0 ? `${visibleEvents.length} test records are loaded` : "Seed the mock leafy-greens feed", state: visibleEvents.length > 0 ? "complete" : "active", }, { label: "Validate records", detail: runStage === "generating" || runStage === "delivering" ? "Inbound records are moving" : validationOutcome, state: runStage === "loaded" ? "pending" : runStage === "generating" || runStage === "delivering" ? "active" : "complete", }, { label: "Review exceptions", detail: exceptionCount ? `${exceptionCount} exception${exceptionCount === 1 ? "" : "s"} to review` : "No exceptions in this test run", state: postedCount > 0 ? "complete" : "pending", }, { label: "Trace selected lot", detail: selectedReadiness.exportReady ? "Selected lot has a complete test chain" : "Selected lot shows the missing handoffs", state: selectedLot ? "complete" : "pending", }, { label: "Preview test handoff", detail: "Test filters only; formal production records stay in production exports", state: runStage === "exported" ? "complete" : "pending", }, ]; return (
{isStandalone ? (

RegEngine Inflow Lab

{runStatusLabel}

Diagnose CSV data, run a mock FSMA 204 feed, and keep test records separate from signed-in production imports.

Tenant {tenantId}
Delivery {deliveryMode === "mock" ? "Mock only" : "Live adapter disabled"}
Fixture {fixture === "leafy_greens_trace" ? "Leafy greens trace" : "Fresh-cut transformation"}
Source {source}
) : (

Inflow Lab

{connectionLabel} Mock environment

Test supplier traceability data before production import. This lab uses mock or uploaded test records only; formal production records start after signed-in import.

Last inbound record {lastReceivedLabel}
Readiness score {readinessScore}/100
Validation outcome {validationOutcome}
Test completeness {exportReadinessLabel}
)}
Boundary active {activeDataBoundary}

{boundaryStateDetail} The lab checks Critical Tracking Event (CTE) handoffs and required Key Data Element (KDE) fields, but it cannot create formal production records. Your operation remains responsible for compliance.

{boundarySteps.map((step) => (

{step.label}

{step.value}

))}
{serviceError && (
Inflow Lab service connection: {serviceError}
)}
{isStandalone ? "Operational workspace" : "Inbound data readiness"}

{isStandalone ? "Review mock-generated records, trace lots, and preview test filters without mixing simulator data into production imports." : "Validate FSMA 204 records, resolve exceptions, prove traceability, and keep formal production records separate from fixture data."}

setTraceInput(event.target.value)} className={cn("min-w-0 border-slate-300 text-xs sm:w-[290px]", isStandalone ? "h-10" : "h-9")} aria-label="Traceability lot code" />
{tabs.map((tab) => ( ))}
{activeTab === "Overview" && (

Connection status

{connectionLabel}

Test submissions are routed through the Inflow Lab gateway and remain outside production ingestion.

Traceability Readiness Score

{readinessScore}/100

Blends KDE completeness, CTE lifecycle coverage, delivery quality, CSV check pass rate, and connection health.

{exceptionCount ? ( ) : ( )}

Validation outcome

{validationOutcome}

Ready: {exportReadyCount}. Warning: {warningLots.length}. Blocked: {blockedLots.length}. Records need CTE coverage, KDE completeness, lineage, and posted delivery before export.

Test completeness

{exportReadinessLabel}

Test previews use the selected lot and date window. Formal production records come later from signed-in imports.

Lot readiness

Select a lot to inspect the traceability path and required FSMA 204 events.

{uniqueLots.map((lot) => ( ))}

Exceptions

Partial lots remain visible for remediation instead of being hidden from the mock preview.

{exceptionLots.length === 0 && failedCount === 0 ? (
No blocking exceptions found in the current fixture data.
) : ( <> {exceptionLots.map((lot) => { const guidance = getLotFixGuidance(lot); return (

{lot.product}

{lot.lotCode}

{guidance.reason}

Likely owner: {guidance.owner} Next: {guidance.next}
); })} {failedCount > 0 && (
{failedCount} inbound delivery {failedCount === 1 ? "attempt needs" : "attempts need"} review.
)} )}

Test handoff preview

Preview test CSV or EPCIS JSON-LD filters. Formal production records are generated only from imported production data.

)} {activeTab === "Data feeder" && (

Paste or upload inbound CSV

Uses the public stateless CSV evaluator. Data is parsed, normalized, and checked against FSMA 204 rules without being stored or promoted to production ingestion.

handleFeederFile(event.target.files?.[0] ?? null)} />