import { useMemo } from "react"; import { AlertTriangle, CheckCircle2, CloudUpload, ExternalLink, FileJson, History, Loader2, RefreshCcw, ShieldAlert, } from "lucide-react"; import type { CloudUpstreamActivationDecision, CloudUpstreamActivationEntityType, CloudUpstreamConflict, CloudUpstreamConnection, CloudUpstreamPreview, CloudUpstreamRun, CloudUpstreamStep, CloudUpstreamSummaryCount, CloudUpstreamWarning, } from "@paperclipai/shared"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { useLocation } from "@/lib/router"; type FixtureStateKey = | "settings-pane" | "connect-wizard" | "schema-mismatch" | "preview" | "preview-clean" | "progress" | "retry" | "finish"; const STEPS: Array<{ key: CloudUpstreamStep; label: string }> = [ { key: "connect", label: "Connect" }, { key: "scan", label: "Scan" }, { key: "preview", label: "Preview" }, { key: "push", label: "Push" }, { key: "verify", label: "Verify" }, { key: "activate", label: "Activate" }, ]; const ACTIVATION_CATEGORIES: Array<{ key: CloudUpstreamActivationEntityType; label: string; singular: string; detail: string; }> = [ { key: "agents", label: "Agents", singular: "agent", detail: "Keep paused until cloud secrets and adapter credentials are verified.", }, { key: "routines", label: "Routines", singular: "routine", detail: "Review schedules before enabling triggers.", }, { key: "monitors", label: "Monitors", singular: "monitor", detail: "Activate after the target instance has been smoke tested.", }, ]; const FIXTURE_LABELS: Record = { "settings-pane": "1 · Settings → Cloud upstream pane (enabled)", "connect-wizard": "2 · Connect wizard — remote URL entry + PKCE launch", "schema-mismatch": "3 · Connect wizard — schema-mismatch hard block", preview: "4 · Preview — conflicts, warnings, planned actions", "preview-clean": "5 · Preview — clean run with no conflicts", progress: "6 · Durable progress — mid-run from run events", retry: "7 · Retry without duplicating ledger entries", finish: "8 · Finish / activation checklist with run report", }; const PARSE_ORDER: FixtureStateKey[] = [ "settings-pane", "connect-wizard", "schema-mismatch", "preview", "preview-clean", "progress", "retry", "finish", ]; export function CloudUpstreamUxLab() { const location = useLocation(); const { state, showChrome } = useMemo(() => { const params = new URLSearchParams(location.search); const raw = (params.get("state") ?? "settings-pane") as FixtureStateKey; return { state: PARSE_ORDER.includes(raw) ? raw : "settings-pane", showChrome: params.get("chrome") === "on", }; }, [location.search]); const fixture = useMemo(() => buildFixture(state), [state]); return (
{showChrome ? : null}
); } function FixtureNav({ active }: { active: FixtureStateKey }) { return (
UX lab · cloud upstream
{PARSE_ORDER.map((key) => ( {FIXTURE_LABELS[key]} ))}
); } interface Fixture { selectedCompanyName: string; connection: CloudUpstreamConnection | null; preview: CloudUpstreamPreview | null; latestRun: CloudUpstreamRun | null; history: CloudUpstreamRun[]; notice: string | null; actionError: string | null; } function CloudUpstreamRender({ fixture }: { fixture: Fixture }) { const { connection, preview, latestRun, history, notice, actionError, selectedCompanyName } = fixture; const activeStep: CloudUpstreamStep = latestRun?.activeStep ?? (preview ? "preview" : connection?.tokenStatus === "connected" ? "scan" : "connect"); return (

Cloud upstream

Push {selectedCompanyName} into a Paperclip Cloud stack. Automations stay paused until activation.

{connection?.target.origin ? ( ) : null}
{notice ? (
{notice}
) : null} {actionError ? (
{actionError}
) : null}
Connection
{connection ? (
{connection.target.stackDisplayName ?? connection.target.stackSlug ?? connection.target.stackId}
{connection.target.product} · {connection.target.origin} · token {connection.tokenStatus}
Schema {connection.target.schemaMajor}. Max chunk {formatBytes(connection.target.maxChunkBytes)}.
) : (
)}
{preview ? (
Preview
) : null} {latestRun ? (
Progress and finish
{latestRun.status === "failed" || latestRun.status === "cancelled" ? ( ) : latestRun.status === "succeeded" ? ( ) : null}
{latestRun.status}
Run {latestRun.id.slice(0, 8)} · {latestRun.completedAt ? `completed ${formatDate(latestRun.completedAt)}` : latestRun.status === "running" ? "in progress" : "in progress"}
{latestRun.progressPercent}%
{latestRun.events.map((event) => (
{formatDate(event.at)} {event.phase} {event.message}
))}
{latestRun.status === "succeeded" ? : null}
) : null} {history.length ? (
History
{history.map((run) => (
Run {run.id.slice(0, 8)} · {run.status} {formatDate(run.createdAt)}
))}
) : null}
); } function Stepper({ activeStep }: { activeStep: CloudUpstreamStep }) { const activeIndex = STEPS.findIndex((step) => step.key === activeStep); return (
{STEPS.map((step, index) => { const complete = index < activeIndex; const active = index === activeIndex; return (
{complete ? ( ) : ( )} {step.label}
); })}
); } function SummaryGrid({ summary }: { summary: CloudUpstreamSummaryCount[] }) { return (
{summary.map((item) => (
{item.count}
{item.label}
))}
); } function WarningsPanel({ warnings }: { warnings: CloudUpstreamWarning[] }) { return (
Warnings
{warnings.map((warning) => (
{warning.title}
{warning.detail}
))}
); } function ConflictTable({ conflicts }: { conflicts: CloudUpstreamConflict[] }) { return (
Conflicts
{conflicts.length === 0 ? (
No target conflicts detected for this preview.
) : (
{conflicts.map((conflict) => (
{conflict.entityType} {conflict.sourceLabel} {conflict.targetLabel} {conflict.plannedAction}
))}
)}
); } function ActivationChecklist({ run }: { run: CloudUpstreamRun }) { const rows = buildActivationRows(run); return (
Activation checklist
{rows.map((row) => { const activated = row.status === "activated"; return (
{row.label}
{row.statusLabel}
{row.count === 0 ? `0 imported ${row.pluralLabel} in this run.` : row.detail}
); })}
); } function buildActivationRows(run: CloudUpstreamRun) { const decisions = decisionsFromReport(run.report); return ACTIVATION_CATEGORIES.map((category) => { const decision = decisions[category.key]; const count = summaryCount(run.summary, category.key); const status = decision?.status === "activated" ? "activated" : "paused"; const pluralLabel = `${category.singular}${count === 1 ? "" : "s"}`; return { ...category, count, pluralLabel, status, detail: `${count} imported ${pluralLabel} are paused by default. ${category.detail}`, statusLabel: status === "activated" ? `${count} activated` : count === 0 ? "0 imported" : `${count} paused`, }; }); } function decisionsFromReport(report: Record): Partial> { const value = optionalRecord(report.activationChecklist); const decisions: Partial> = {}; for (const key of ["agents", "routines", "monitors"] as const) { const item = optionalRecord(value[key]); if (!item) continue; decisions[key] = { entityType: key, count: typeof item.count === "number" ? item.count : 0, status: item.status === "activated" ? "activated" : "paused", activatedAt: typeof item.activatedAt === "string" ? item.activatedAt : null, }; } return decisions; } function summaryCount(summary: CloudUpstreamSummaryCount[], key: CloudUpstreamActivationEntityType): number { return summary.find((item) => item.key === key)?.count ?? 0; } function optionalRecord(value: unknown): Record { return value && typeof value === "object" && !Array.isArray(value) ? value as Record : {}; } function formatDate(value: string) { return new Date(value).toLocaleString(undefined, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", timeZone: "UTC", }); } function formatBytes(value: number) { if (value >= 1024 * 1024) return `${Math.round(value / (1024 * 1024))} MiB`; if (value >= 1024) return `${Math.round(value / 1024)} KiB`; return `${value} B`; } const STACK_TARGET = { stackId: "stk_2vKqz9D8mNFqQ7Rp", stackSlug: "paperclip-prod", stackDisplayName: "Paperclip Prod", companyId: "co_4hT2yX", primaryHost: "paperclip.paperclip.app", origin: "https://paperclip.paperclip.app", product: "paperclip-cloud", schemaMajor: 7, maxChunkBytes: 5 * 1024 * 1024, }; const STACK_TARGET_SCHEMA_BEHIND = { ...STACK_TARGET, schemaMajor: 5, }; function connectedConnection(target = STACK_TARGET): CloudUpstreamConnection { return { id: "cu_conn_8d3f1b6a", companyId: "co_4hT2yX", remoteUrl: "https://paperclip.paperclip.app/PC521D/dashboard", target, tokenStatus: "connected", scopes: ["upstream.push", "upstream.preview"], authorizedGlobalUserId: "user_9pXqYzAbCdEf", expiresAt: "2026-08-18T19:00:00.000Z", createdAt: "2026-05-18T18:45:00.000Z", updatedAt: "2026-05-18T19:02:18.000Z", lastRunId: null, }; } const PREVIEW_SUMMARY: CloudUpstreamSummaryCount[] = [ { key: "users", label: "Users", count: 14 }, { key: "agents", label: "Agents", count: 6 }, { key: "routines", label: "Routines", count: 4 }, { key: "monitors", label: "Monitors", count: 2 }, ]; const PREVIEW_WARNINGS_NORMAL: CloudUpstreamWarning[] = [ { code: "imported_automations_paused", severity: "warning", title: "Automations stay paused", detail: "Imported agents, routines, and monitors require explicit activation after the push.", }, { code: "unmatched_users_import_as_historical_authors", severity: "warning", title: "Unmatched users become historical authors", detail: "Invite now remains a secondary action after the transfer is complete.", }, { code: "secret_values_redacted", severity: "warning", title: "Secret values are not transferred", detail: "The push carries secret requirements only. Configure cloud secrets before activating automations.", }, ]; const PREVIEW_WARNINGS_SCHEMA: CloudUpstreamWarning[] = [ { code: "schema_mismatch", severity: "blocker", title: "Cloud stack upgrade required", detail: "This local build uses upstream schema 7, but the cloud stack reports schema 5.", }, ...PREVIEW_WARNINGS_NORMAL, ]; const PREVIEW_CONFLICTS: CloudUpstreamConflict[] = [ { id: "conflict_user_serena", entityType: "user", sourceLabel: "serena@magicmachine.co (unmatched)", targetLabel: "→ historical author Serena R.", plannedAction: "create", reason: "Target stack has no matching identity. Will arrive as historical author; invite available after push.", }, { id: "conflict_user_dotta", entityType: "user", sourceLabel: "dotta@magicmachine.co", targetLabel: "↦ dotta@magicmachine.co (cloud)", plannedAction: "update", reason: "Existing cloud identity matches local user; will be merged.", }, { id: "conflict_agent_qa", entityType: "agent", sourceLabel: "QA · qa-bot", targetLabel: "↦ QA · qa-bot (cloud)", plannedAction: "update", reason: "Mapped to existing cloud agent. Imported run history will be appended.", }, { id: "conflict_routine_nightly_reports", entityType: "routine", sourceLabel: "Nightly status report", targetLabel: "(new in cloud)", plannedAction: "create", reason: "Routine does not exist in the target stack and will be created in paused state.", }, ]; function basePreview(): CloudUpstreamPreview { return { connectionId: "cu_conn_8d3f1b6a", sourceCompanyId: "co_local_pc521d", target: STACK_TARGET, schemaCompatible: true, summary: PREVIEW_SUMMARY, warnings: PREVIEW_WARNINGS_NORMAL, conflicts: PREVIEW_CONFLICTS, generatedAt: "2026-05-18T19:03:14.000Z", }; } function schemaMismatchPreview(): CloudUpstreamPreview { return { ...basePreview(), target: STACK_TARGET_SCHEMA_BEHIND, schemaCompatible: false, summary: [], conflicts: [], warnings: PREVIEW_WARNINGS_SCHEMA, }; } function cleanPreview(): CloudUpstreamPreview { return { ...basePreview(), conflicts: [], warnings: PREVIEW_WARNINGS_NORMAL.slice(0, 1), }; } const PROGRESS_EVENTS = [ { id: "evt_01", at: "2026-05-18T19:10:02.000Z", phase: "scan" as CloudUpstreamStep, type: "completed" as const, message: "Scanned 14 users, 6 agents, 4 routines, 2 monitors." }, { id: "evt_02", at: "2026-05-18T19:10:11.000Z", phase: "preview" as CloudUpstreamStep, type: "completed" as const, message: "Preview generated with 4 conflicts and 3 warnings." }, { id: "evt_03", at: "2026-05-18T19:10:31.000Z", phase: "push" as CloudUpstreamStep, type: "created" as const, message: "users · 8 created, 6 mapped to existing identities." }, { id: "evt_04", at: "2026-05-18T19:10:48.000Z", phase: "push" as CloudUpstreamStep, type: "updated" as const, message: "agents · 4 created paused, 2 updated paused." }, { id: "evt_05", at: "2026-05-18T19:10:58.000Z", phase: "push" as CloudUpstreamStep, type: "updated" as const, message: "routines · 3 created paused, 1 updated." }, { id: "evt_06", at: "2026-05-18T19:11:09.000Z", phase: "push" as CloudUpstreamStep, type: "created" as const, message: "monitors · 2 created paused." }, { id: "evt_07", at: "2026-05-18T19:11:18.000Z", phase: "verify" as CloudUpstreamStep, type: "updated" as const, message: "Verifying transferred ledger checksums…" }, ]; function runningRun(): CloudUpstreamRun { return { id: "run_3kQ8mNpW9bX2zL4Y", connectionId: "cu_conn_8d3f1b6a", companyId: "co_local_pc521d", status: "running", activeStep: "push", progressPercent: 62, dryRun: false, summary: PREVIEW_SUMMARY, warnings: PREVIEW_WARNINGS_NORMAL, conflicts: PREVIEW_CONFLICTS, events: PROGRESS_EVENTS, targetUrl: "https://paperclip.paperclip.app/PC521D/dashboard", report: {}, retryOfRunId: null, createdAt: "2026-05-18T19:10:01.000Z", updatedAt: "2026-05-18T19:11:18.000Z", completedAt: null, }; } function failedRun(): CloudUpstreamRun { return { id: "run_5fXqR2bT7aD8zP1K", connectionId: "cu_conn_8d3f1b6a", companyId: "co_local_pc521d", status: "failed", activeStep: "push", progressPercent: 78, dryRun: false, summary: PREVIEW_SUMMARY, warnings: PREVIEW_WARNINGS_NORMAL, conflicts: PREVIEW_CONFLICTS, events: [ ...PROGRESS_EVENTS, { id: "evt_08", at: "2026-05-18T19:11:30.000Z", phase: "push", type: "failed", message: "Apply rejected: cloud rejected chunk 4 of 6 (HTTP 502). Ledger entries from chunks 1–3 retained; chunk 4 not committed.", }, ], targetUrl: "https://paperclip.paperclip.app/PC521D/dashboard", report: { ledgerCheckpoint: "chunk-3" }, retryOfRunId: null, createdAt: "2026-05-18T19:10:01.000Z", updatedAt: "2026-05-18T19:11:30.000Z", completedAt: null, }; } function succeededRun(): CloudUpstreamRun { return { id: "run_7aBcD9eFgH2iJ3kL", connectionId: "cu_conn_8d3f1b6a", companyId: "co_local_pc521d", status: "succeeded", activeStep: "activate", progressPercent: 100, dryRun: false, summary: PREVIEW_SUMMARY, warnings: PREVIEW_WARNINGS_NORMAL, conflicts: PREVIEW_CONFLICTS, events: [ ...PROGRESS_EVENTS, { id: "evt_08", at: "2026-05-18T19:11:25.000Z", phase: "verify", type: "completed", message: "Ledger checksums match. Push committed.", }, { id: "evt_09", at: "2026-05-18T19:11:31.000Z", phase: "activate", type: "completed", message: "Activation checklist pending operator approval — automations remain paused.", }, ], targetUrl: "https://paperclip.paperclip.app/PC521D/dashboard", report: { activationChecklist: { agents: { count: 6, status: "paused", activatedAt: null }, routines: { count: 4, status: "paused", activatedAt: null }, monitors: { count: 2, status: "paused", activatedAt: null }, }, }, retryOfRunId: null, createdAt: "2026-05-18T19:10:01.000Z", updatedAt: "2026-05-18T19:11:31.000Z", completedAt: "2026-05-18T19:11:31.000Z", }; } function buildFixture(state: FixtureStateKey): Fixture { switch (state) { case "settings-pane": return { selectedCompanyName: "Paperclip · PC521D", connection: connectedConnection(), preview: null, latestRun: null, history: [], notice: "Cloud upstream connection approved.", actionError: null, }; case "connect-wizard": return { selectedCompanyName: "Paperclip · PC521D", connection: null, preview: null, latestRun: null, history: [], notice: null, actionError: null, }; case "schema-mismatch": return { selectedCompanyName: "Paperclip · PC521D", connection: connectedConnection(STACK_TARGET_SCHEMA_BEHIND), preview: schemaMismatchPreview(), latestRun: null, history: [], notice: null, actionError: "Cloud stack is on schema 5 but this local build pushes schema 7. Upgrade the cloud stack to continue.", }; case "preview": return { selectedCompanyName: "Paperclip · PC521D", connection: connectedConnection(), preview: basePreview(), latestRun: null, history: [], notice: null, actionError: null, }; case "preview-clean": return { selectedCompanyName: "Paperclip · PC521D", connection: connectedConnection(), preview: cleanPreview(), latestRun: null, history: [], notice: "Preview completed. No target conflicts detected.", actionError: null, }; case "progress": return { selectedCompanyName: "Paperclip · PC521D", connection: connectedConnection(), preview: null, latestRun: runningRun(), history: [], notice: null, actionError: null, }; case "retry": return { selectedCompanyName: "Paperclip · PC521D", connection: connectedConnection(), preview: null, latestRun: failedRun(), history: [ { ...failedRun(), id: "run_9pYqXwVtSrQ" }, ], notice: null, actionError: "Push run failed. Review the events. Retry resumes from ledger checkpoint chunk-3 — chunks 1–3 will not be re-applied.", }; case "finish": return { selectedCompanyName: "Paperclip · PC521D", connection: connectedConnection(), preview: null, latestRun: succeededRun(), history: [ { ...succeededRun(), id: "run_aZcXvBnMqWeR" }, ], notice: "Push run completed. Review activation before unpausing automations.", actionError: null, }; } }