3 changed files with 0 additions and 211 deletions
@ -1,172 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import { useCallback, useEffect, useMemo, useState } from "react"; |
|||
import { App, Button, Card, Empty, Modal, Pagination, Popconfirm, Space, Spin, Typography } from "antd"; |
|||
import { useRouter, useSearchParams } from "next/navigation"; |
|||
import { useI18n } from "@/i18n"; |
|||
import { |
|||
deleteCanvasWorkflow, |
|||
listCanvasWorkflows, |
|||
type CanvasWorkflowListItem, |
|||
type CanvasWorkflowPageInfo, |
|||
} from "@/lib/canvasWorkflowApi"; |
|||
import { useCanvasWorkflowStore } from "@/store/canvasWorkflowStore"; |
|||
|
|||
interface CanvasWorkflowPickerModalProps { |
|||
isOpen: boolean; |
|||
onClose: () => void; |
|||
} |
|||
|
|||
const DEFAULT_PAGE_INFO: CanvasWorkflowPageInfo = { |
|||
page: 1, |
|||
pageSize: 10, |
|||
pageCount: 1, |
|||
total: 0, |
|||
}; |
|||
|
|||
function workflowDisplayTitle(workflow: CanvasWorkflowListItem): string { |
|||
return workflow.title?.trim() || workflow.code?.trim() || `#${workflow.id}`; |
|||
} |
|||
|
|||
function formatWorkflowTime(value?: string): string { |
|||
if (!value) return "-"; |
|||
const date = new Date(value); |
|||
if (Number.isNaN(date.getTime())) return value; |
|||
return date.toLocaleString(); |
|||
} |
|||
|
|||
function buildCanvasWorkflowUrl(searchParams: URLSearchParams): string { |
|||
const query = searchParams.toString(); |
|||
return query ? `/canvas?${query}` : "/canvas"; |
|||
} |
|||
|
|||
export function CanvasWorkflowPickerModal({ isOpen, onClose }: CanvasWorkflowPickerModalProps) { |
|||
const { t } = useI18n(); |
|||
const { message } = App.useApp(); |
|||
const router = useRouter(); |
|||
const searchParams = useSearchParams(); |
|||
const currentCanvasWorkflowId = useCanvasWorkflowStore((state) => state.currentCanvasWorkflowId); |
|||
const clearCurrentCanvasWorkflow = useCanvasWorkflowStore((state) => state.clearCurrentCanvasWorkflow); |
|||
const [workflows, setWorkflows] = useState<CanvasWorkflowListItem[]>([]); |
|||
const [pageInfo, setPageInfo] = useState<CanvasWorkflowPageInfo>(DEFAULT_PAGE_INFO); |
|||
const [isLoading, setIsLoading] = useState(false); |
|||
|
|||
const loadWorkflows = useCallback(async (page = pageInfo.page, pageSize = pageInfo.pageSize) => { |
|||
setIsLoading(true); |
|||
try { |
|||
const data = await listCanvasWorkflows({ page, pageSize }); |
|||
setWorkflows(data.list); |
|||
setPageInfo(data.pageInfo); |
|||
} catch (error) { |
|||
const errorMessage = error instanceof Error ? error.message : t("canvasWorkflow.listFailed"); |
|||
void message.error(errorMessage); |
|||
} finally { |
|||
setIsLoading(false); |
|||
} |
|||
}, [message, pageInfo.page, pageInfo.pageSize, t]); |
|||
|
|||
useEffect(() => { |
|||
if (isOpen) { |
|||
void loadWorkflows(1, pageInfo.pageSize); |
|||
} |
|||
}, [isOpen]); |
|||
|
|||
const openWorkflow = (workflow: CanvasWorkflowListItem) => { |
|||
const nextParams = new URLSearchParams(searchParams.toString()); |
|||
nextParams.set("workflowId", String(workflow.id)); |
|||
router.push(buildCanvasWorkflowUrl(nextParams)); |
|||
onClose(); |
|||
}; |
|||
|
|||
const handleDelete = async (workflow: CanvasWorkflowListItem) => { |
|||
try { |
|||
await deleteCanvasWorkflow(workflow.id); |
|||
void message.success(t("canvasWorkflow.deleteSuccess")); |
|||
if (currentCanvasWorkflowId === workflow.id) { |
|||
const nextParams = new URLSearchParams(searchParams.toString()); |
|||
nextParams.delete("workflowId"); |
|||
router.replace(buildCanvasWorkflowUrl(nextParams)); |
|||
clearCurrentCanvasWorkflow(); |
|||
} |
|||
await loadWorkflows(pageInfo.page, pageInfo.pageSize); |
|||
} catch (error) { |
|||
const errorMessage = error instanceof Error ? error.message : t("canvasWorkflow.deleteFailed"); |
|||
void message.error(errorMessage); |
|||
} |
|||
}; |
|||
|
|||
const cards = useMemo(() => workflows.map((workflow) => ( |
|||
<Card |
|||
key={workflow.id} |
|||
size="small" |
|||
className="h-full" |
|||
title={ |
|||
<Typography.Text strong ellipsis> |
|||
{workflowDisplayTitle(workflow)} |
|||
</Typography.Text> |
|||
} |
|||
extra={<Typography.Text type="secondary">#{workflow.id}</Typography.Text>} |
|||
actions={[ |
|||
<Button key="open" type="link" size="small" onClick={() => openWorkflow(workflow)}> |
|||
{t("canvasWorkflow.open")} |
|||
</Button>, |
|||
<Popconfirm |
|||
key="delete" |
|||
title={t("canvasWorkflow.deleteConfirmTitle")} |
|||
description={t("canvasWorkflow.deleteConfirmDesc")} |
|||
okText={t("common.remove")} |
|||
cancelText={t("common.cancel")} |
|||
onConfirm={() => handleDelete(workflow)} |
|||
> |
|||
<Button type="link" danger size="small"> |
|||
{t("common.remove")} |
|||
</Button> |
|||
</Popconfirm>, |
|||
]} |
|||
> |
|||
<Space direction="vertical" size={8} className="w-full"> |
|||
<Typography.Paragraph |
|||
type="secondary" |
|||
ellipsis={{ rows: 2 }} |
|||
className="!mb-0 min-h-11" |
|||
> |
|||
{workflow.desp?.trim() || t("canvasWorkflow.noDescription")} |
|||
</Typography.Paragraph> |
|||
<Typography.Text type="secondary" className="text-xs"> |
|||
{t("canvasWorkflow.columnUpdatedAt")}: {formatWorkflowTime(workflow.updateTime)} |
|||
</Typography.Text> |
|||
</Space> |
|||
</Card> |
|||
)), [workflows, t, searchParams, currentCanvasWorkflowId, pageInfo.page, pageInfo.pageSize]); |
|||
|
|||
return ( |
|||
<Modal |
|||
title={t("canvasWorkflow.projects")} |
|||
open={isOpen} |
|||
onCancel={onClose} |
|||
footer={null} |
|||
width={760} |
|||
destroyOnHidden |
|||
> |
|||
<Spin spinning={isLoading}> |
|||
{workflows.length > 0 ? ( |
|||
<div className="grid grid-cols-1 gap-3 md:grid-cols-2"> |
|||
{cards} |
|||
</div> |
|||
) : ( |
|||
<Empty description={t("canvasWorkflow.empty")} /> |
|||
)} |
|||
<div className="mt-4 flex justify-end"> |
|||
<Pagination |
|||
current={pageInfo.page} |
|||
pageSize={pageInfo.pageSize} |
|||
total={pageInfo.total} |
|||
showSizeChanger |
|||
pageSizeOptions={["10", "20", "50"]} |
|||
onChange={(page, pageSize) => void loadWorkflows(page, pageSize)} |
|||
/> |
|||
</div> |
|||
</Spin> |
|||
</Modal> |
|||
); |
|||
} |
|||
Loading…
Reference in new issue