import { BoltIcon, BoltSlashIcon, BookOpenIcon, PencilSquareIcon, TrashIcon, } from "@heroicons/react/20/solid"; import { DialogDescription } from "@radix-ui/react-dialog"; import { type FetcherWithComponents, Form, useLocation } from "@remix-run/react"; import { type ReactNode } from "react"; import { InlineCode } from "~/components/code/InlineCode"; import { EnvironmentCombo } from "~/components/environments/EnvironmentLabel"; import { Button, LinkButton } from "~/components/primitives/Buttons"; import { DateTime } from "~/components/primitives/DateTime"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTrigger, } from "~/components/primitives/Dialog"; import { Header2, Header3 } from "~/components/primitives/Headers"; import { InfoPanel } from "~/components/primitives/InfoPanel"; import { Paragraph } from "~/components/primitives/Paragraph"; import * as Property from "~/components/primitives/PropertyTable"; import { Table, TableBlankRow, TableBody, TableCell, TableHeader, TableHeaderCell, TableRow, } from "~/components/primitives/Table"; import { EnabledStatus } from "~/components/runs/v3/EnabledStatus"; import { ScheduleTypeCombo } from "~/components/runs/v3/ScheduleType"; import { TaskRunsTable } from "~/components/runs/v3/TaskRunsTable"; import { useEnvironment } from "~/hooks/useEnvironment"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { cn } from "~/utils/cn"; import { v3EditSchedulePath } from "~/utils/pathBuilder"; type RunRow = React.ComponentProps["runs"][number]; type EnvironmentRow = React.ComponentProps["environment"] & { id: string; }; export type ScheduleInspectorData = { id: string; friendlyId: string; type: "DECLARATIVE" | "IMPERATIVE"; taskIdentifier: string; cron: string; cronDescription: string; timezone: string; externalId: string | null; deduplicationKey: string | null; userProvidedDeduplicationKey: boolean; active: boolean; environments: EnvironmentRow[]; runs: RunRow[]; nextRuns: Date[]; }; type Props = { schedule: ScheduleInspectorData; /** * Right-aligned slot in the header (e.g. a back link or close button). */ headerActions?: ReactNode; /** * URL the action `Form`s post to. Defaults to the current page (Form's * default behavior) — pass the schedule detail route when the inspector * is rendered somewhere else (e.g. in a sheet on a different page). */ actionPath?: string; /** When set, Edit calls back instead of navigating to the standalone edit page. */ onEdit?: () => void; /** Submits enable/disable via this fetcher with `_format=json` so the host stays put. */ activeToggleFetcher?: FetcherWithComponents; /** Submits delete via this fetcher with `_format=json` so the host stays put. */ deleteFetcher?: FetcherWithComponents; }; export function ScheduleInspector({ schedule, headerActions, actionPath, onEdit, activeToggleFetcher, deleteFetcher, }: Props) { const location = useLocation(); const organization = useOrganization(); const project = useProject(); const environment = useEnvironment(); const isUtc = schedule.timezone === "UTC"; const isImperative = schedule.type === "IMPERATIVE"; return (
{schedule.friendlyId} {headerActions}
Schedule ID {schedule.friendlyId} Task ID {schedule.taskIdentifier} Type CRON
{schedule.cron} {schedule.cronDescription}
Timezone {schedule.timezone} Environment
{schedule.environments.map((env) => ( ))}
{isImperative && ( <> External ID {schedule.externalId ? schedule.externalId : "–"} Deduplication key {schedule.userProvidedDeduplicationKey ? schedule.deduplicationKey : "–"} Status )}
Last 5 runs
Next 5 runs {!isUtc && {schedule.timezone}} UTC {schedule.active ? ( schedule.nextRuns.length ? ( schedule.nextRuns.map((run, index) => ( {!isUtc && ( )} )) ) : ( ) ) : ( )}
{!isImperative && (
Schedules docs } panelClassName="max-w-full" > You can only edit a declarative schedule by updating your schedules.task and then running the CLI dev and deploy commands.
)}
{isImperative && (
{(() => { const ToggleForm = activeToggleFetcher?.Form ?? Form; const isSubmitting = activeToggleFetcher?.state === "submitting"; return ( {activeToggleFetcher ? : null} ); })()} Delete schedule Are you sure you want to delete this schedule? This can't be reversed. {(() => { const DeleteForm = deleteFetcher?.Form ?? Form; const isSubmitting = deleteFetcher?.state === "submitting"; return ( {deleteFetcher ? : null} ); })()}
{onEdit ? ( ) : ( Edit schedule… )}
)}
); } function PlaceholderText({ title }: { title: string }) { return (
{title}
); }