From 888370cddc0e66cf0389e9464d0d393a1dbd2475 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Tue, 7 Apr 2026 14:48:19 -0400 Subject: [PATCH] fix(web): use local workflow response types The workflow routes were importing types that do not exist in the generated OpenAPI client. Define the workflow endpoint response shapes locally so the web app typechecks against the actual server responses. --- apps/fabro-web/app/lib/workflow-api.ts | 32 +++++++++++++++++++ apps/fabro-web/app/routes/run-overview.tsx | 5 +-- apps/fabro-web/app/routes/workflow-detail.tsx | 3 +- apps/fabro-web/app/routes/workflows.tsx | 4 +-- 4 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 apps/fabro-web/app/lib/workflow-api.ts diff --git a/apps/fabro-web/app/lib/workflow-api.ts b/apps/fabro-web/app/lib/workflow-api.ts new file mode 100644 index 000000000..abad9388a --- /dev/null +++ b/apps/fabro-web/app/lib/workflow-api.ts @@ -0,0 +1,32 @@ +import type { PaginationMeta, RunSettings } from "@qltysh/fabro-api-client"; + +export interface WorkflowScheduleSummary { + expression: string; + next_run?: string | null; +} + +export interface WorkflowLastRunSummary { + ran_at?: string | null; +} + +export interface WorkflowListItem { + name: string; + slug: string; + filename: string; + last_run?: WorkflowLastRunSummary | null; + schedule?: WorkflowScheduleSummary | null; +} + +export interface PaginatedWorkflowListResponse { + data: WorkflowListItem[]; + pagination?: PaginationMeta; +} + +export interface WorkflowDetailResponse { + name: string; + slug: string; + description: string; + filename: string; + settings: RunSettings; + graph: string; +} diff --git a/apps/fabro-web/app/routes/run-overview.tsx b/apps/fabro-web/app/routes/run-overview.tsx index 67b22c965..dc01cbeef 100644 --- a/apps/fabro-web/app/routes/run-overview.tsx +++ b/apps/fabro-web/app/routes/run-overview.tsx @@ -7,7 +7,8 @@ import { useTheme } from "../lib/theme"; import { getGraphTheme } from "../lib/graph-theme"; import { apiJson } from "../api"; import { formatDurationSecs } from "../lib/format"; -import type { PaginatedRunStageList, PaginatedRunList, WorkflowDetail } from "@qltysh/fabro-api-client"; +import type { PaginatedRunStageList, PaginatedRunList } from "@qltysh/fabro-api-client"; +import type { WorkflowDetailResponse } from "../lib/workflow-api"; export const handle = { wide: true }; @@ -35,7 +36,7 @@ export async function loader({ request, params }: any) { let graphDot: string | null = null; if (run) { try { - const workflow = await apiJson(`/workflows/${run.workflow}`, { request }); + const workflow = await apiJson(`/workflows/${run.workflow}`, { request }); graphDot = workflow.graph; } catch { // workflow not found — leave graphDot null diff --git a/apps/fabro-web/app/routes/workflow-detail.tsx b/apps/fabro-web/app/routes/workflow-detail.tsx index bbcabc831..735a7ca23 100644 --- a/apps/fabro-web/app/routes/workflow-detail.tsx +++ b/apps/fabro-web/app/routes/workflow-detail.tsx @@ -1,7 +1,8 @@ import { ChevronRightIcon } from "@heroicons/react/20/solid"; import { Link, Outlet, useLocation, useParams } from "react-router"; import { apiJson } from "../api"; -import type { WorkflowDetail as ApiWorkflowDetail, RunSettings } from "@qltysh/fabro-api-client"; +import type { RunSettings } from "@qltysh/fabro-api-client"; +import type { WorkflowDetailResponse as ApiWorkflowDetail } from "../lib/workflow-api"; export interface WorkflowEntry { name: string; diff --git a/apps/fabro-web/app/routes/workflows.tsx b/apps/fabro-web/app/routes/workflows.tsx index c0a0d25a0..df18f577d 100644 --- a/apps/fabro-web/app/routes/workflows.tsx +++ b/apps/fabro-web/app/routes/workflows.tsx @@ -15,7 +15,7 @@ import { import { Link } from "react-router"; import { apiJson } from "../api"; import { timeAgo, timeUntil } from "../lib/time"; -import type { PaginatedWorkflowList } from "@qltysh/fabro-api-client"; +import type { PaginatedWorkflowListResponse } from "../lib/workflow-api"; export function meta({}: any) { return [{ title: "Workflows — Fabro" }]; @@ -105,7 +105,7 @@ interface WorkflowData { } export async function loader({ request }: any) { - const { data: apiWorkflows } = await apiJson("/workflows", { request }); + const { data: apiWorkflows } = await apiJson("/workflows", { request }); const workflows: WorkflowData[] = apiWorkflows.map((w) => ({ name: w.name, slug: w.slug,