import { ExclamationTriangleIcon } from "@heroicons/react/20/solid"; import { type LoaderFunctionArgs } from "@remix-run/server-runtime"; import { tryCatch } from "@trigger.dev/core"; import { motion } from "framer-motion"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; import { ExitIcon } from "~/assets/icons/ExitIcon"; import { RunsIcon } from "~/assets/icons/RunsIcon"; import { LinkButton } from "~/components/primitives/Buttons"; import { CopyableText } from "~/components/primitives/CopyableText"; import { DateTime } from "~/components/primitives/DateTime"; import { Header2, Header3 } from "~/components/primitives/Headers"; import { Paragraph } from "~/components/primitives/Paragraph"; import * as Property from "~/components/primitives/PropertyTable"; import { BatchStatusCombo, descriptionForBatchStatus } from "~/components/runs/v3/BatchStatus"; import { useAutoRevalidate } from "~/hooks/useAutoRevalidate"; import { useEnvironment } from "~/hooks/useEnvironment"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { findProjectBySlug } from "~/models/project.server"; import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server"; import { BatchPresenter } from "~/presenters/v3/BatchPresenter.server"; import { requireUserId } from "~/services/session.server"; import { cn } from "~/utils/cn"; import { formatNumber } from "~/utils/numberFormatter"; import { EnvironmentParamSchema, v3BatchesPath, v3BatchRunsPath } from "~/utils/pathBuilder"; const BatchParamSchema = EnvironmentParamSchema.extend({ batchParam: z.string(), }); export const loader = async ({ request, params }: LoaderFunctionArgs) => { const userId = await requireUserId(request); const { organizationSlug, projectParam, envParam, batchParam } = BatchParamSchema.parse(params); const project = await findProjectBySlug(organizationSlug, projectParam, userId); if (!project) { throw new Response("Not Found", { status: 404 }); } const environment = await findEnvironmentBySlug(project.id, envParam, userId); if (!environment) { throw new Response("Not Found", { status: 404 }); } try { const presenter = new BatchPresenter(); const [error, data] = await tryCatch( presenter.call({ environmentId: environment.id, batchId: batchParam, userId, }) ); if (error) { throw new Error(error.message); } return typedjson({ batch: data }); } catch (error) { console.error(error); throw new Response(undefined, { status: 400, statusText: "Something went wrong, if this problem persists please contact support.", }); } }; export default function Page() { const { batch } = useTypedLoaderData(); const organization = useOrganization(); const project = useProject(); const environment = useEnvironment(); // Auto-reload when batch is still in progress useAutoRevalidate({ interval: 1000, onFocus: true, disabled: batch.hasFinished, }); const showProgressMeter = batch.isV2 && (batch.status === "PROCESSING" || batch.status === "PARTIAL_FAILED"); return (
{/* Header */}
{batch.friendlyId}
{/* Status bar */}
{descriptionForBatchStatus(batch.status)}
{/* Scrollable content */}
{/* Progress meter for v2 batches */} {showProgressMeter && (
)} {/* Properties */}
ID Status Version {batch.isV2 ? "v2 (Run Engine)" : "v1 (Legacy)"} Total runs {formatNumber(batch.runCount)} {batch.isV2 && ( <> Successfully created {formatNumber(batch.successfulRunCount)} {batch.failedRunCount > 0 && ( Failed to create {formatNumber(batch.failedRunCount)} )} )} {batch.idempotencyKey && ( Idempotency key )} Created {batch.processingStartedAt && ( Processing started )} {batch.processingCompletedAt && ( Processing completed )} Finished {batch.finishedAt ? : "–"}
{/* Errors section */} {batch.errors.length > 0 && (
Run creation errors ({batch.errors.length})
{batch.errors.map((error) => (
Item #{error.index} {error.taskIdentifier}
{error.errorCode && ( {error.errorCode} )}
{error.error}
))}
)}
{/* Footer */}
View runs
); } type BatchProgressMeterProps = { successCount: number; failureCount: number; totalCount: number; }; function BatchProgressMeter({ successCount, failureCount, totalCount }: BatchProgressMeterProps) { const processedCount = successCount + failureCount; const successPercentage = totalCount === 0 ? 0 : (successCount / totalCount) * 100; const failurePercentage = totalCount === 0 ? 0 : (failureCount / totalCount) * 100; return (
Run creation progress {formatNumber(processedCount)}/{formatNumber(totalCount)}
{formatNumber(successCount)} created
{failureCount > 0 && (
{formatNumber(failureCount)} failed
)}
); }