"use client"; import { useState, useEffect, useCallback } from "react"; import { useTranslations, useFormatter } from "next-intl"; import { Card } from "@/components/ui/card"; import { StatusBadge } from "@/components/ui/status-badge"; import { formatDateTime, formatRelative } from "@/lib/format"; interface RequestSummary { id: string; instanceName?: string | null; agentName: string; packages: string[]; status: string; adminNotes?: string; tenantName?: string; createdAt?: string; updatedAt?: string; } interface TenantSummary { name: string; displayName: string; phase: string; conditions: Array<{ type: string; status: string; reason?: string; message?: string; lastTransitionTime?: string; }>; } interface SingleRequestState { request: RequestSummary; tenant: TenantSummary | null; } /** * ProvisioningStatus * * Polls /api/onboarding?id= every 5s until the request reaches * a terminal state. Slice 3: takes a `requestId` prop so multiple of these * can render on the same dashboard for different in-flight requests. * * The pre-Slice-3 version polled /api/onboarding with no params and * assumed one-request-per-org — that endpoint shape is gone now. */ export function ProvisioningStatus({ requestId }: { requestId: string }) { const t = useTranslations("onboarding"); const f = useFormatter(); const [data, setData] = useState(null); const [error, setError] = useState(""); const poll = useCallback(async () => { try { const res = await fetch( `/api/onboarding?id=${encodeURIComponent(requestId)}` ); if (!res.ok) throw new Error("Failed to fetch status"); const json = await res.json(); setData(json); } catch (err: any) { setError(err.message); } }, [requestId]); useEffect(() => { poll(); const status = data?.request?.status; const phase = data?.tenant?.phase; const terminal = status === "rejected" || status === "active" || phase === "Ready" || phase === "Running"; if (terminal) return; const interval = setInterval(poll, 5000); return () => clearInterval(interval); }, [poll, data?.request?.status, data?.tenant?.phase]); if (error) { return (
{error}
); } if (!data) { return (
{t("loading")}
); } const status = data.request.status; const label = data.request.instanceName || data.request.tenantName || data.request.agentName; // Pending admin approval if (status === "pending") { return (

{t("pendingTitle")}

{label && (

{label}

)}

{t("pendingDescription")}

{data.request.createdAt && (

{t("submittedAt")}{" "} {formatRelative(data.request.createdAt, f)} {" "} ({formatDateTime(data.request.createdAt, f)})

)}
); } // Rejected if (status === "rejected") { return (

{t("rejectedTitle")}

{label && (

{label}

)}

{t("rejectedDescription")}

{data.request.adminNotes && (

{data.request.adminNotes}

)}
); } // Provisioning in progress (status approved/provisioning, optionally with tenant phase < Ready) if ( status === "approved" || status === "provisioning" || (status === "active" && data.tenant && data.tenant.phase !== "Ready") ) { const phase = data.tenant?.phase ?? "Pending"; const conditions = data.tenant?.conditions ?? []; return (

{t("provisioningTitle")}

{label && (

{label}

)}

{t("provisioningDescription")}

{t("phase")}
{conditions.map((c, i) => (
{c.type} {c.reason || c.status}
))}
); } // Active / Ready if (status === "active") { return (

{t("readyTitle")}

{label && (

{label}

)}

{t("readyDescription")}

); } return null; }