mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
fix(ui): unsynchronized running status after WorkflowDetail subpage changed
This commit is contained in:
@@ -3,6 +3,7 @@ import { Immer, produce } from "immer";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import i18n from "@/i18n";
|
||||
import { type WorkflowRunModel } from "./workflowRun";
|
||||
|
||||
export interface WorkflowModel extends BaseModel {
|
||||
name: string;
|
||||
@@ -16,6 +17,9 @@ export interface WorkflowModel extends BaseModel {
|
||||
lastRunRef?: string;
|
||||
lastRunStatus?: string;
|
||||
lastRunTime?: string;
|
||||
expand?: {
|
||||
lastRunRef?: WorkflowRunModel;
|
||||
};
|
||||
}
|
||||
|
||||
export const WORKFLOW_TRIGGERS = Object.freeze({
|
||||
|
||||
@@ -16,7 +16,7 @@ const Settings = () => {
|
||||
["ssl-provider", "settings.sslprovider.tab", <IconPlugConnected size="1em" />],
|
||||
["persistence", "settings.persistence.tab", <IconDatabaseCog size="1em" />],
|
||||
] satisfies [string, string, React.ReactElement][];
|
||||
const [menuKey, setMenuValue] = useState(menus[0][0]);
|
||||
const [menuKey, setMenuValue] = useState<string>();
|
||||
useEffect(() => {
|
||||
const subpath = location.pathname.split("/")[2];
|
||||
if (!subpath) {
|
||||
|
||||
@@ -36,10 +36,10 @@ const WorkflowDetail = () => {
|
||||
["design", "workflow.detail.design.tab", <IconRobot size="1em" />],
|
||||
["runs", "workflow.detail.runs.tab", <IconHistory size="1em" />],
|
||||
] satisfies [string, string, React.ReactElement][];
|
||||
const [tabValue, setTabValue] = useState(tabs[0][0]);
|
||||
const [tabValue, setTabValue] = useState<string>();
|
||||
useEffect(() => {
|
||||
const subpath = location.pathname.split("/")[3];
|
||||
if (!tabs.map((tab) => tab[0]).includes(subpath)) {
|
||||
if (!subpath) {
|
||||
navigate(`/workflows/${workflowId}/${tabs[0][0]}`, { replace: true });
|
||||
return;
|
||||
}
|
||||
@@ -50,6 +50,8 @@ const WorkflowDetail = () => {
|
||||
const handleTabChange = (value: string) => {
|
||||
setTabValue(value);
|
||||
navigate(`/workflows/${workflowId}/${value}`);
|
||||
|
||||
workflowState.init(workflow.id); // reload state
|
||||
};
|
||||
|
||||
const handleEnableClick = async () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IconArrowBackUp, IconDots, IconPlayerPlay } from "@tabler/icons-react";
|
||||
import { Alert, App, Button, Card, Dropdown, Space } from "antd";
|
||||
@@ -10,7 +10,7 @@ import WorkflowElementsContainer from "@/components/workflow/WorkflowElementsCon
|
||||
import { isAllNodesValidated } from "@/domain/workflow";
|
||||
import { WORKFLOW_RUN_STATUSES } from "@/domain/workflowRun";
|
||||
import { useZustandShallowSelector } from "@/hooks";
|
||||
import { subscribe as subscribeWorkflow, unsubscribe as unsubscribeWorkflow } from "@/repository/workflow";
|
||||
import { subscribe as subscribeWorkflow } from "@/repository/workflow";
|
||||
import { useWorkflowStore } from "@/stores/workflow";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
|
||||
@@ -19,30 +19,32 @@ const WorkflowDetailDesign = () => {
|
||||
|
||||
const { message, modal, notification } = App.useApp();
|
||||
|
||||
const { workflow, ...workflowState } = useWorkflowStore(useZustandShallowSelector(["workflow", "publish", "rollback"]));
|
||||
const { workflow, ...workflowState } = useWorkflowStore(useZustandShallowSelector(["workflow", "init", "publish", "rollback"]));
|
||||
|
||||
const [isPendingOrRunning, setIsPendingOrRunning] = useState(false);
|
||||
const lastRunStatus = useMemo(() => workflow.lastRunStatus, [workflow]);
|
||||
|
||||
const [allowRollback, setAllowRollback] = useState(false);
|
||||
const [allowPublish, setAllowPublish] = useState(false);
|
||||
const [allowRun, setAllowRun] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsPendingOrRunning(lastRunStatus == WORKFLOW_RUN_STATUSES.PENDING || lastRunStatus == WORKFLOW_RUN_STATUSES.RUNNING);
|
||||
}, [lastRunStatus]);
|
||||
const pending = workflow.lastRunStatus === WORKFLOW_RUN_STATUSES.PENDING || workflow.lastRunStatus === WORKFLOW_RUN_STATUSES.RUNNING;
|
||||
setIsPendingOrRunning(pending);
|
||||
}, [workflow]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPendingOrRunning) {
|
||||
let unsubscribeFn: Awaited<ReturnType<typeof subscribeWorkflow>> | undefined = undefined;
|
||||
subscribeWorkflow(workflow.id, (cb) => {
|
||||
if (cb.record.lastRunStatus !== WORKFLOW_RUN_STATUSES.PENDING && cb.record.lastRunStatus !== WORKFLOW_RUN_STATUSES.RUNNING) {
|
||||
setIsPendingOrRunning(false);
|
||||
unsubscribeWorkflow(workflow.id);
|
||||
unsubscribeFn?.();
|
||||
}
|
||||
}).then((res) => {
|
||||
unsubscribeFn = res;
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribeWorkflow(workflow.id);
|
||||
unsubscribeFn?.();
|
||||
};
|
||||
}
|
||||
}, [workflow.id, isPendingOrRunning]);
|
||||
|
||||
@@ -108,8 +108,8 @@ const WorkflowList = () => {
|
||||
} else {
|
||||
return (
|
||||
<Flex gap="small">
|
||||
<WorkflowStatusIcon color={true} size="1.25em" status={lastRunStatus!} />
|
||||
<Typography.Text>{lastRunTime ? dayjs(record.lastRunTime!).format("YYYY-MM-DD HH:mm:ss") : ""}</Typography.Text>
|
||||
<WorkflowStatusIcon color={true} size="1.25em" status={lastRunStatus} />
|
||||
<Typography.Text>{lastRunTime ? dayjs(lastRunTime).format("YYYY-MM-DD HH:mm:ss") : ""}</Typography.Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -231,6 +231,7 @@ const WorkflowList = () => {
|
||||
sort: sort,
|
||||
page: page,
|
||||
perPage: pageSize,
|
||||
expand: true,
|
||||
});
|
||||
},
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ export type ListRequest = {
|
||||
sort?: string;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
expand?: boolean;
|
||||
};
|
||||
|
||||
export const list = async (request: ListRequest) => {
|
||||
@@ -28,6 +29,7 @@ export const list = async (request: ListRequest) => {
|
||||
const perPage = request.perPage || 10;
|
||||
|
||||
return await pb.collection(COLLECTION_NAME_WORKFLOW).getList<WorkflowModel>(page, perPage, {
|
||||
expand: request.expand ? "lastRunRef" : undefined,
|
||||
filter: filters.join(" && "),
|
||||
sort: sort,
|
||||
requestKey: null,
|
||||
@@ -36,6 +38,7 @@ export const list = async (request: ListRequest) => {
|
||||
|
||||
export const get = async (id: string) => {
|
||||
return await getPocketBase().collection(COLLECTION_NAME_WORKFLOW).getOne<WorkflowModel>(id, {
|
||||
expand: "lastRunRef",
|
||||
requestKey: null,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -22,15 +22,27 @@ 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<WorkflowRunModel>(page, perPage, {
|
||||
expand: request.expand ? "workflowRef" : undefined,
|
||||
filter: filters.join(" && "),
|
||||
sort: "-created",
|
||||
requestKey: null,
|
||||
expand: request.expand ? "workflowRef" : undefined,
|
||||
});
|
||||
};
|
||||
|
||||
export const remove = async (record: MaybeModelRecordWithId<WorkflowRunModel>) => {
|
||||
return await getPocketBase().collection(COLLECTION_NAME_WORKFLOW_RUN).delete(record.id);
|
||||
export const remove = async (record: MaybeModelRecordWithId<WorkflowRunModel> | MaybeModelRecordWithId<WorkflowRunModel>[]) => {
|
||||
const pb = getPocketBase();
|
||||
|
||||
if (Array.isArray(record)) {
|
||||
const batch = pb.createBatch();
|
||||
for (const item of record) {
|
||||
batch.collection(COLLECTION_NAME_WORKFLOW_RUN).delete(item.id);
|
||||
}
|
||||
const res = await batch.send();
|
||||
return res.every((e) => e.status === 200);
|
||||
} else {
|
||||
await pb.collection(COLLECTION_NAME_WORKFLOW_RUN).delete(record.id!);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
export const subscribe = async (id: string, cb: (e: RecordSubscription<WorkflowRunModel>) => void) => {
|
||||
|
||||
Reference in New Issue
Block a user