diff --git a/ui/src/components/workflow/WorkflowRunDetail.tsx b/ui/src/components/workflow/WorkflowRunDetail.tsx
index fff54849a..36ee20e2b 100644
--- a/ui/src/components/workflow/WorkflowRunDetail.tsx
+++ b/ui/src/components/workflow/WorkflowRunDetail.tsx
@@ -1,8 +1,9 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
-import { IconBrowserShare, IconCheck, IconChevronRight, IconDownload, IconSettings2 } from "@tabler/icons-react";
+import { EditorState, FlowLayoutDefault } from "@flowgram.ai/fixed-layout-editor";
+import { IconBrowserShare, IconCheck, IconDots, IconDownload, IconSettings2, IconTransferOut } from "@tabler/icons-react";
import { useRequest } from "ahooks";
-import { App, Button, Collapse, Divider, Dropdown, Empty, Skeleton, Spin, Table, type TableProps, Tooltip, Typography } from "antd";
+import { Alert, App, Button, Card, Divider, Dropdown, Empty, Skeleton, Spin, Table, type TableProps, Tooltip, Typography, theme } from "antd";
import dayjs from "dayjs";
import { ClientResponseError } from "pocketbase";
@@ -17,6 +18,9 @@ import { listByWorkflowRunId as listLogsByWorkflowRunId } from "@/repository/wor
import { mergeCls } from "@/utils/css";
import { getErrMsg } from "@/utils/error";
+import WorkflowDesigner from "./designer/Designer";
+import WorkflowToolbar from "./designer/Toolbar";
+import WorkflowGraphExportModal from "./WorkflowGraphExportModal";
import WorkflowStatus from "./WorkflowStatus";
export interface WorkflowRunDetailProps {
@@ -25,21 +29,127 @@ export interface WorkflowRunDetailProps {
data: WorkflowRunModel;
}
-const WorkflowRunDetail = ({ data, ...props }: WorkflowRunDetailProps) => {
- return (
-
-
-
-
+const WorkflowRunDetail = ({ className, style, data }: WorkflowRunDetailProps) => {
+ const { t } = useTranslation();
-
-
-
+ return (
+
+
+ {data.endedAt
+ ? t("workflow_run.base.description_with_time_cost", {
+ trigger: t(`workflow_run.base.trigger.${data.trigger}`),
+ startedAt: dayjs(data.startedAt).format("YYYY-MM-DD HH:mm:ss"),
+ timeCost: dayjs(data.endedAt).diff(dayjs(data.startedAt), "second") + "s",
+ })
+ : t("workflow_run.base.description", {
+ trigger: t(`workflow_run.base.trigger.${data.trigger}`),
+ startedAt: dayjs(data.startedAt).format("YYYY-MM-DD HH:mm:ss"),
+ })}
+
+ }
+ showIcon
+ type={
+ {
+ [WORKFLOW_RUN_STATUSES.SUCCEEDED]: "success" as const,
+ [WORKFLOW_RUN_STATUSES.FAILED]: "error" as const,
+ [WORKFLOW_RUN_STATUSES.CANCELED]: "warning" as const,
+ }[data.status] ?? ("info" as const)
+ }
+ />
+
+
+ {t("workflow_run.process")}
+
+
+
+
+ {t("workflow_run.logs")}
+
+
+
+
+
+ {t("workflow_run.artifacts")}
+
+
);
};
+const WorkflowRunProcess = ({ runData }: { runData: WorkflowRunModel }) => {
+ const { t } = useTranslation();
+
+ const { token: themeToken } = theme.useToken();
+
+ const { modalProps: graphExportModalProps, ...graphExportModal } = WorkflowGraphExportModal.useModal();
+
+ const handleExportClick = () => {
+ graphExportModal.open({ data: runData.graph! });
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+ ,
+ onClick: handleExportClick,
+ },
+ ],
+ }}
+ trigger={["click"]}
+ >
+ } size="small" />
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: string }) => {
const { t } = useTranslation();
@@ -131,7 +241,7 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin
.map((group) => {
const escape = (str: string) => str.replaceAll("\r", "\\r").replaceAll("\n", "\\n");
return (
- group.name +
+ `#${group.id} ${group.name}` +
NEWLINE +
group.records
.map((record) => {
@@ -156,49 +266,48 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin
};
return (
- <>
- {t("workflow_run.logs")}
-
-
-
-
-
-
- ,
- onClick: () => setShowTimestamp(!showTimestamp),
- },
- {
- key: "show-whitespace",
- label: t("workflow_run.logs.menu.show_whitespaces"),
- icon: ,
- onClick: () => setShowWhitespace(!showWhitespace),
- },
- {
- type: "divider",
- },
- {
- key: "download-logs",
- label: t("workflow_run.logs.menu.download_logs"),
- icon: ,
- onClick: handleDownloadClick,
- },
- ],
- }}
- trigger={["click"]}
- >
- } ghost={browserTheme === "light"} />
-
-
+
+
+
+
+
+ ,
+ onClick: () => setShowTimestamp(!showTimestamp),
+ },
+ {
+ key: "show-whitespace",
+ label: t("workflow_run.logs.menu.show_whitespaces"),
+ icon: ,
+ onClick: () => setShowWhitespace(!showWhitespace),
+ },
+ {
+ type: "divider",
+ },
+ {
+ key: "download-logs",
+ label: t("workflow_run.logs.menu.download_logs"),
+ icon: ,
+ onClick: handleDownloadClick,
+ },
+ ],
+ }}
+ trigger={["click"]}
+ >
+ } ghost={browserTheme === "light"} />
+
+
+
-
+
+
0}
fallback={
@@ -207,32 +316,20 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin
}
>
-
-
group.id)}
- expandIcon={({ isActive }) => }
- items={listData.map((group) => {
- return {
- key: group.id,
- classNames: {
- header: "text-sm text-stone-200",
- body: "text-stone-200",
- },
- style: { color: "inherit", border: "none" },
- styles: {
- header: { color: "inherit" },
- },
- label: group.name,
- children: {group.records.map((record) => renderRecord(record))}
,
- };
- })}
- />
-
+ {listData.map((group) => {
+ return (
+
+
+ {`#${group.id}\u00A0`}
+ {group.name}
+
+
{group.records.map((record) => renderRecord(record))}
+
+ );
+ })}
- >
+
);
};
@@ -286,7 +383,7 @@ const WorkflowRunArtifacts = ({ runId }: { runId: string }) => {
},
];
const [tableData, setTableData] = useState
([]);
- const { loading: tableLoading } = useRequest(
+ const { loading } = useRequest(
() => {
return listCertificatesByWorkflowRunId(runId);
},
@@ -309,20 +406,17 @@ const WorkflowRunArtifacts = ({ runId }: { runId: string }) => {
);
return (
- <>
- {t("workflow_run.artifacts")}
-
- columns={tableColumns}
- dataSource={tableData}
- loading={tableLoading}
- locale={{
- emptyText: ,
- }}
- pagination={false}
- rowKey={(record) => record.id}
- size="small"
- />
- >
+
+ columns={tableColumns}
+ dataSource={tableData}
+ loading={loading}
+ locale={{
+ emptyText: ,
+ }}
+ pagination={false}
+ rowKey={(record) => record.id}
+ size="small"
+ />
);
};
diff --git a/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx b/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx
index a667323da..77d6f3a48 100644
--- a/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx
+++ b/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx
@@ -1,6 +1,6 @@
import { startTransition, useCallback, useState } from "react";
import { IconX } from "@tabler/icons-react";
-import { useControllableValue } from "ahooks";
+import { useControllableValue, useGetState } from "ahooks";
import { Button, Drawer, Flex } from "antd";
import Show from "@/components/Show";
@@ -64,7 +64,8 @@ const WorkflowRunDetailDrawer = ({ afterClose, data, loading, trigger, ...props
const useDrawer = () => {
type DataType = WorkflowRunDetailDrawerProps["data"];
- const [data, setData] = useState();
+ const [data, setData, getData] = useGetState();
+ const [loading, setLoading] = useState();
const [open, setOpen] = useState(false);
const onOpenChange = useCallback((open: boolean) => {
@@ -77,17 +78,34 @@ const useDrawer = () => {
startTransition(() => {
if (!open) {
setData(void 0);
+ setLoading(void 0);
}
});
},
data,
+ loading,
open,
onOpenChange,
},
- open: (data: NonNullable) => {
+ open: ({ data, loading }: { data: NonNullable; loading?: boolean }) => {
setData(data);
+ setLoading(loading);
setOpen(true);
+
+ return {
+ safeUpdate: ({ data, loading }: { data?: NonNullable; loading?: boolean }) => {
+ if (data != null) {
+ if (data.id !== getData()?.id) return; // 确保数据不脏读
+
+ setData(data);
+ }
+
+ if (loading != null) {
+ setLoading(loading);
+ }
+ },
+ };
},
close: () => {
setOpen(false);
diff --git a/ui/src/components/workflow/designer/Designer.tsx b/ui/src/components/workflow/designer/Designer.tsx
index 8acb7e65b..a93b1710b 100644
--- a/ui/src/components/workflow/designer/Designer.tsx
+++ b/ui/src/components/workflow/designer/Designer.tsx
@@ -26,6 +26,8 @@ export interface DesignerProps {
className?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
+ defaultEditorState?: string;
+ defaultLayout?: string;
initialData?: FlowDocumentJSON;
readonly?: boolean;
onDocumentChange?: (ctx: FixedLayoutPluginContext) => void;
@@ -39,7 +41,7 @@ export interface DesignerInstance extends FixedLayoutPluginContext {
}
const Designer = forwardRef(
- ({ className, style, children, initialData, readonly, onDocumentChange, onNodeChange, onNodeClick }, ref) => {
+ ({ className, style, children, defaultEditorState, defaultLayout, initialData, readonly, onDocumentChange, onNodeChange, onNodeClick }, ref) => {
const { token: themeToken } = theme.useToken();
const rendered = useRef(false);
@@ -47,6 +49,8 @@ const Designer = forwardRef(
const flowgramEditorRef = useRef(null);
const flowgramEditorProps = useMemo(
() => ({
+ defaultLayout: defaultLayout,
+
initialData: initialData,
constants: {
@@ -121,9 +125,13 @@ const Designer = forwardRef(
],
onInit: (ctx) => {
- const maybeMobile = ["android", "ios", "iphone", "ipad", "micromessenger"].some((s) => navigator.userAgent.includes(s));
- if (maybeMobile) {
- ctx.playground.editorState.changeState(EditorState.STATE_MOUSE_FRIENDLY_SELECT.id);
+ if (defaultEditorState != null) {
+ ctx.playground.editorState.changeState(defaultEditorState);
+ } else {
+ const maybeMobile = ["android", "ios", "iphone", "ipad", "micromessenger"].some((s) => navigator.userAgent.includes(s));
+ if (maybeMobile) {
+ ctx.playground.editorState.changeState(EditorState.STATE_MOUSE_FRIENDLY_SELECT.id);
+ }
}
},
@@ -136,7 +144,7 @@ const Designer = forwardRef(
}, 1);
},
}),
- [themeToken, initialData, readonly, onDocumentChange]
+ [defaultEditorState, defaultLayout, initialData, readonly, onDocumentChange, themeToken]
);
useEffect(() => {
diff --git a/ui/src/components/workflow/designer/Toolbar.tsx b/ui/src/components/workflow/designer/Toolbar.tsx
index 8759b0c86..0edbe95d9 100644
--- a/ui/src/components/workflow/designer/Toolbar.tsx
+++ b/ui/src/components/workflow/designer/Toolbar.tsx
@@ -1,9 +1,10 @@
-import { useCallback, useEffect, useState } from "react";
+import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { EditorState, FlowLayoutDefault, useClientContext, usePlaygroundTools, useRefresh } from "@flowgram.ai/fixed-layout-editor";
import { IconHandStop, IconLayoutCards, IconMatrix, IconMaximize, IconMinus, IconPlus } from "@tabler/icons-react";
-import { Button, Dropdown, Tooltip } from "antd";
+import { Button, type ButtonProps, Dropdown, Tooltip } from "antd";
+import Show from "@/components/Show";
import { mergeCls } from "@/utils/css";
import Minimap from "./Minimap";
@@ -11,9 +12,26 @@ import Minimap from "./Minimap";
export interface ToolbarProps {
className?: string;
style?: React.CSSProperties;
+ size?: ButtonProps["size"];
+ showLayout?: boolean;
+ showMinimap?: boolean;
+ showMouseState?: boolean;
+ showZoom?: boolean;
+ showZoomFit?: boolean;
+ showZoomLevel?: boolean;
}
-const Toolbar = ({ className, style }: ToolbarProps) => {
+const Toolbar = ({
+ className,
+ style,
+ size,
+ showLayout = true,
+ showMinimap = true,
+ showMouseState = true,
+ showZoom = true,
+ showZoomFit = true,
+ showZoomLevel = true,
+}: ToolbarProps) => {
const { t } = useTranslation();
const ctx = useClientContext();
@@ -29,6 +47,12 @@ const Toolbar = ({ className, style }: ToolbarProps) => {
return () => d.dispose();
}, [playground]);
+ const buttonIconSize = useMemo(() => {
+ if (size === "large") return "1.5em";
+ if (size === "small") return "1em";
+ return "1.25em";
+ }, [size]);
+
const [isMinimapVisible, setIsMinimapVisible] = useState(() => window.screen.availWidth >= 1024);
const [isMouseFriendly, setIsMouseFriendly] = useState(() => playground.editorState.is(EditorState.STATE_MOUSE_FRIENDLY_SELECT.id));
@@ -58,55 +82,80 @@ const Toolbar = ({ className, style }: ToolbarProps) => {
return (
-
- }
- type={isMouseFriendly ? "primary" : "default"}
- onClick={handleToggleMouseFriendly}
- />
-
+
+
+ }
+ size={size}
+ type={isMouseFriendly ? "primary" : "default"}
+ onClick={handleToggleMouseFriendly}
+ />
+
+
-
- } onClick={() => tools.zoomout()} />
-
- ({
- key: `${zoom}%`,
- label: `${zoom}%`,
- onClick: () => tools.updateZoom(zoom / 100),
- })),
- {
- type: "divider",
- },
- {
- key: "auto",
- label: t("workflow.detail.design.toolbar.auto_fit"),
- onClick: () => tools.fitView(),
- },
- ],
- }}
- trigger={["click"]}
- >
-
-
-
- } onClick={() => tools.zoomin()} />
-
-
- } onClick={() => tools.fitView()} />
-
+
+
+ } size={size} onClick={() => tools.zoomout()} />
+
+
+ ({
+ key: `${zoom}%`,
+ label: `${zoom}%`,
+ onClick: () => tools.updateZoom(zoom / 100),
+ })),
+ {
+ type: "divider",
+ },
+ {
+ key: "auto",
+ label: t("workflow.detail.design.toolbar.auto_fit"),
+ onClick: () => tools.fitView(),
+ },
+ ],
+ }}
+ trigger={["click"]}
+ >
+
+
+
+
+ } size={size} onClick={() => tools.zoomin()} />
+
+
+
+ } size={size} onClick={() => tools.fitView()} />
+
+
+
-
- } onClick={handleToggleLayout} />
-
+
+
+ }
+ size={size}
+ onClick={handleToggleLayout}
+ />
+
+
-
- } ghost={isMinimapVisible} type={isMinimapVisible ? "primary" : "default"} onClick={handleToggleMinimap} />
-
- {isMinimapVisible && }
+
+
+ }
+ ghost={isMinimapVisible}
+ type={isMinimapVisible ? "primary" : "default"}
+ size={size}
+ onClick={handleToggleMinimap}
+ />
+
+ {isMinimapVisible && }
+
);
diff --git a/ui/src/domain/workflowRun.ts b/ui/src/domain/workflowRun.ts
index 855e5e312..167d5f117 100644
--- a/ui/src/domain/workflowRun.ts
+++ b/ui/src/domain/workflowRun.ts
@@ -1,4 +1,4 @@
-import { type WorkflowModel } from "./workflow";
+import { type WorkflowGraph, type WorkflowModel } from "./workflow";
export interface WorkflowRunModel extends BaseModel {
workflowRef: string;
@@ -6,6 +6,7 @@ export interface WorkflowRunModel extends BaseModel {
trigger: string;
startedAt: ISO8601String;
endedAt: ISO8601String;
+ graph?: WorkflowGraph;
error?: string;
expand?: {
workflowRef?: Pick;
diff --git a/ui/src/i18n/locales/en/nls.workflow.runs.json b/ui/src/i18n/locales/en/nls.workflow.runs.json
index 525a68079..18aa1e3a3 100644
--- a/ui/src/i18n/locales/en/nls.workflow.runs.json
+++ b/ui/src/i18n/locales/en/nls.workflow.runs.json
@@ -26,6 +26,14 @@
"workflow_run.props.started_at": "Started at",
"workflow_run.props.ended_at": "Ended at",
+ "workflow_run.base.description": "Triggered {{trigger}} at {{startedAt}}",
+ "workflow_run.base.description_with_time_cost": "Triggered {{trigger}} at {{startedAt}}. Time cost: {{timeCost}}.",
+ "workflow_run.base.trigger.scheduled": "scheduledly",
+ "workflow_run.base.trigger.manual": "manually",
+
+ "workflow_run.process": "Process",
+ "workflow_run.process.menu.export": "Export",
+
"workflow_run.logs": "Logs",
"workflow_run.logs.menu.show_timestamps": "Show timestamps",
"workflow_run.logs.menu.show_whitespaces": "Show whitespaces",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.runs.json b/ui/src/i18n/locales/zh/nls.workflow.runs.json
index f38749b43..42076c397 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.runs.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.runs.json
@@ -26,6 +26,14 @@
"workflow_run.props.started_at": "开始时间",
"workflow_run.props.ended_at": "完成时间",
+ "workflow_run.base.description": "{{trigger}}触发于 {{startedAt}}",
+ "workflow_run.base.description_with_time_cost": "{{trigger}}触发于 {{startedAt}},总计用时 {{timeCost}}。",
+ "workflow_run.base.trigger.scheduled": "定时",
+ "workflow_run.base.trigger.manual": "手动",
+
+ "workflow_run.process": "流程",
+ "workflow_run.process.menu.export": "导出",
+
"workflow_run.logs": "日志",
"workflow_run.logs.menu.show_timestamps": "显示日期时间",
"workflow_run.logs.menu.show_whitespaces": "显示转义换行符",
diff --git a/ui/src/pages/ConsoleLayout.tsx b/ui/src/pages/ConsoleLayout.tsx
index c2964657a..7d7f635f7 100644
--- a/ui/src/pages/ConsoleLayout.tsx
+++ b/ui/src/pages/ConsoleLayout.tsx
@@ -40,10 +40,10 @@ const ConsoleLayout = () => {
};
const handleDocumentClick = () => {
- if (i18n.language.startsWith("en")) {
- window.open(APP_DOCUMENT_URL + "/en/", "_blank");
- } else {
+ if (i18n.language.startsWith("zh")) {
window.open(APP_DOCUMENT_URL, "_blank");
+ } else {
+ window.open(APP_DOCUMENT_URL + "/en/", "_blank");
}
};
diff --git a/ui/src/pages/dashboard/Dashboard.tsx b/ui/src/pages/dashboard/Dashboard.tsx
index 23f537c2b..7e322e992 100644
--- a/ui/src/pages/dashboard/Dashboard.tsx
+++ b/ui/src/pages/dashboard/Dashboard.tsx
@@ -26,13 +26,11 @@ import WorkflowStatus from "@/components/workflow/WorkflowStatus";
import { type Statistics } from "@/domain/statistics";
import { type WorkflowRunModel } from "@/domain/workflowRun";
import { useBrowserTheme } from "@/hooks";
-import { list as listWorkflowRuns } from "@/repository/workflowRun";
+import { get as getWorkflowRun, list as listWorkflowRuns } from "@/repository/workflowRun";
import { mergeCls } from "@/utils/css";
import { getErrMsg } from "@/utils/error";
const Dashboard = () => {
- const navigate = useNavigate();
-
const { t } = useTranslation();
return (
@@ -48,17 +46,7 @@ const Dashboard = () => {
{t("dashboard.shortcut")}
-
- } shape="round" size="large" type="primary" onClick={() => navigate("/workflows/new")}>
- {t("dashboard.shortcut.create_workflow")}
-
- } shape="round" size="large" onClick={() => navigate("/settings/account")}>
- {t("dashboard.shortcut.change_account")}
-
- } shape="round" size="large" onClick={() => navigate("/settings/ssl-provider")}>
- {t("dashboard.shortcut.configure_ca")}
-
-
+
@@ -224,6 +212,46 @@ const StatisticCards = ({ className, style }: { className?: string; style?: Reac
);
};
+const Shortcuts = ({ className, style }: { className?: string; style?: React.CSSProperties }) => {
+ const navigate = useNavigate();
+
+ const { t } = useTranslation();
+
+ return (
+
+
+ }
+ shape="round"
+ size="large"
+ onClick={() => navigate("/workflows/new")}
+ >
+ {t("dashboard.shortcut.create_workflow")}
+
+ }
+ shape="round"
+ size="large"
+ onClick={() => navigate("/settings/account")}
+ >
+ {t("dashboard.shortcut.change_account")}
+
+ }
+ shape="round"
+ size="large"
+ onClick={() => navigate("/settings/ssl-provider")}
+ >
+ {t("dashboard.shortcut.configure_ca")}
+
+
+
+ );
+};
+
const WorkflowRunHistoryTable = ({ className, style }: { className?: string; style?: React.CSSProperties }) => {
const navigate = useNavigate();
@@ -332,7 +360,10 @@ const WorkflowRunHistoryTable = ({ className, style }: { className?: string; sty
const { drawerProps: detailDrawerProps, ...detailDrawer } = WorkflowRunDetailDrawer.useDrawer();
const handleRecordDetailClick = (workflowRun: WorkflowRunModel) => {
- detailDrawer.open(workflowRun);
+ const drawer = detailDrawer.open({ data: workflowRun, loading: true });
+ getWorkflowRun(workflowRun.id).then((data) => {
+ drawer.safeUpdate({ data, loading: false });
+ });
};
return (
diff --git a/ui/src/pages/workflows/WorkflowDetailRuns.tsx b/ui/src/pages/workflows/WorkflowDetailRuns.tsx
index 7659d35bd..8aafb8761 100644
--- a/ui/src/pages/workflows/WorkflowDetailRuns.tsx
+++ b/ui/src/pages/workflows/WorkflowDetailRuns.tsx
@@ -16,6 +16,7 @@ import { WORKFLOW_TRIGGERS } from "@/domain/workflow";
import { WORKFLOW_RUN_STATUSES, type WorkflowRunModel } from "@/domain/workflowRun";
import { useAppSettings, useZustandShallowSelector } from "@/hooks";
import {
+ get as getWorkflowRun,
list as listWorkflowRuns,
remove as removeWorkflowRun,
subscribe as subscribeWorkflowRun,
@@ -260,7 +261,10 @@ const WorkflowDetailRuns = () => {
const { drawerProps: detailDrawerProps, ...detailDrawer } = WorkflowRunDetailDrawer.useDrawer();
const handleRecordDetailClick = (workflowRun: WorkflowRunModel) => {
- detailDrawer.open(workflowRun);
+ const drawer = detailDrawer.open({ data: workflowRun, loading: true });
+ getWorkflowRun(workflowRun.id).then((data) => {
+ drawer.safeUpdate({ data, loading: false });
+ });
};
const handleRecordCancelClick = (workflowRun: WorkflowRunModel) => {
diff --git a/ui/src/repository/workflow.ts b/ui/src/repository/workflow.ts
index 38f8f5f02..e9ee43d92 100644
--- a/ui/src/repository/workflow.ts
+++ b/ui/src/repository/workflow.ts
@@ -29,7 +29,7 @@ export const list = async (request: ListRequest) => {
const perPage = request.perPage || 10;
return await pb.collection(COLLECTION_NAME_WORKFLOW).getList
(page, perPage, {
- expand: request.expand ? "lastRunRef" : void 0,
+ expand: request.expand ? ["lastRunRef"].join(",") : void 0,
fields: [
"id",
"name",
diff --git a/ui/src/repository/workflowRun.ts b/ui/src/repository/workflowRun.ts
index 21e8c179a..72ef9bf16 100644
--- a/ui/src/repository/workflowRun.ts
+++ b/ui/src/repository/workflowRun.ts
@@ -22,7 +22,7 @@ export const list = async (request: ListRequest) => {
const page = request.page || 1;
const perPage = request.perPage || 10;
return await pb.collection(COLLECTION_NAME_WORKFLOW_RUN).getList(page, perPage, {
- expand: request.expand ? "workflowRef" : void 0,
+ expand: request.expand ? ["workflowRef"].join(",") : void 0,
fields: [
"id",
"status",
@@ -43,6 +43,15 @@ export const list = async (request: ListRequest) => {
});
};
+export const get = async (id: string) => {
+ return await getPocketBase()
+ .collection(COLLECTION_NAME_WORKFLOW_RUN)
+ .getOne(id, {
+ expand: ["workflowRef"].join(","),
+ requestKey: null,
+ });
+};
+
export const remove = async (record: MaybeModelRecordWithId | MaybeModelRecordWithId[]) => {
const pb = getPocketBase();