import { BookOpenIcon } from "@heroicons/react/20/solid"; import { type MetaFunction } from "@remix-run/react"; import { type LoaderFunctionArgs } from "@remix-run/server-runtime"; import { Suspense, useMemo } from "react"; import { TypedAwait, typeddefer, useTypedLoaderData } from "remix-typedjson"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; import { LinkButton } from "~/components/primitives/Buttons"; import { Card } from "~/components/primitives/charts/Card"; import type { ChartConfig } from "~/components/primitives/charts/Chart"; import { Chart } from "~/components/primitives/charts/ChartCompound"; import { Header1, Header3 } from "~/components/primitives/Headers"; import { NavBar, PageTitle } from "~/components/primitives/PageHeader"; import { Paragraph } from "~/components/primitives/Paragraph"; import { Spinner } from "~/components/primitives/Spinner"; 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 { tasksDashboardPresenter, type DailyRunPoint, } from "~/presenters/v3/TasksDashboardPresenter.server"; import { requireUserId } from "~/services/session.server"; import { docsPath, EnvironmentParamSchema, v3EnvironmentPath, v3RunsPath, } from "~/utils/pathBuilder"; export const meta: MetaFunction = () => { return [{ title: "Tasks | Trigger.dev" }]; }; export const loader = async ({ request, params }: LoaderFunctionArgs) => { const userId = await requireUserId(request); const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params); const project = await findProjectBySlug(organizationSlug, projectParam, userId); if (!project) { throw new Response(undefined, { status: 404, statusText: "Project not found" }); } const environment = await findEnvironmentBySlug(project.id, envParam, userId); if (!environment) { throw new Response(undefined, { status: 404, statusText: "Environment not found" }); } const result = await tasksDashboardPresenter.call({ organizationId: project.organizationId, projectId: project.id, environmentId: environment.id, environmentType: environment.type, }); return typeddefer(result); }; const isoDateFormatter = new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric", timeZone: "utc", }); function formatDay(value: string) { // value is a YYYY-MM-DD date string const d = new Date(value + "T00:00:00Z"); return isoDateFormatter.format(d); } const STANDARD_EXAMPLE = `import { task } from "@trigger.dev/sdk"; export const helloWorld = task({ id: "hello-world", run: async (payload: { name: string }) => { return { greeting: \`Hello, \${payload.name}!\` }; }, }); `; const SCHEDULED_EXAMPLE = `import { schedules } from "@trigger.dev/sdk"; export const dailyReport = schedules.task({ id: "daily-report", cron: "0 9 * * *", run: async (payload) => { // Runs every day at 9am UTC return { ranAt: payload.timestamp }; }, }); `; const AGENT_EXAMPLE = `import { agent } from "@trigger.dev/sdk/ai"; export const supportAgent = agent({ id: "support-agent", model: "anthropic/claude-sonnet-4-6", instructions: "You are a helpful customer support agent.", }); `; export default function TasksDashboardPage() { const { counts, series } = useTypedLoaderData(); const organization = useOrganization(); const project = useProject(); const environment = useEnvironment(); return (
Tasks overview
}> {(s) => ( )} }> {(s) => ( )} }> {(s) => ( )}
); } function PanelSkeleton({ title }: { title: string }) { return ( {title} ); } type TaskTypePanelProps = { title: string; count: number; description: string; example: string; data: DailyRunPoint[]; seriesColor: string; listingPath: string; docsHref: string; emptyTitle: string; emptyDescription: string; emptyCta: string; exampleCode: string; }; function TaskTypePanel(props: TaskTypePanelProps) { const { title, count, description, example, data, seriesColor, listingPath, docsHref, emptyTitle, emptyDescription, emptyCta, exampleCode, } = props; const hasData = count > 0; const chartConfig = useMemo( () => ({ count: { label: "Runs", color: seriesColor, }, }), [seriesColor] ); return ( {title} {count.toLocaleString()}
formatDay(value as string)} fillContainer > formatDay(label)} xAxisProps={{ tickFormatter: (value) => formatDay(value) }} />
{hasData ? ( <> {description}
View all Read docs
) : ( )}
); } function EmptyContent({ title, description, example, listingPath, docsHref, cta, exampleCode, }: { title: string; description: string; example: string; listingPath: string; docsHref: string; cta: string; exampleCode: string; }) { const copyExample = () => { if (typeof navigator !== "undefined" && navigator.clipboard) { navigator.clipboard.writeText(exampleCode).catch(() => { /* swallow */ }); } }; return ( <> {title} {description}
{cta} Read docs
); }