Browse Source

refactor: 移除云端工作流相关的弹窗组件和国际化文案

移除了CanvasWorkflowPickerModal组件以及对应的国际化多语言配置,同时删除了Header中唤起该弹窗的相关代码逻辑
feature/group
huangmin 1 month ago
parent
commit
ce84aff529
  1. 172
      src/components/CanvasWorkflowPickerModal.tsx
  2. 13
      src/components/Header.tsx
  3. 26
      src/i18n/index.tsx

172
src/components/CanvasWorkflowPickerModal.tsx

@ -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>
);
}

13
src/components/Header.tsx

@ -2,7 +2,6 @@
import { useState, useMemo, useCallback, useEffect, type ReactNode } from "react";
import Link from "next/link";
import { Button } from "antd";
import { useWorkflowStore } from "@/store/workflowStore";
import { useCanvasWorkflowStore } from "@/store/canvasWorkflowStore";
import { useShallow } from "zustand/shallow";
@ -11,7 +10,6 @@ import { CostIndicator } from "./CostIndicator";
import { KeyboardShortcutsDialog } from "./KeyboardShortcutsDialog";
import { WorkflowBrowserModal } from "./WorkflowBrowserModal";
import { WorkflowSaveModal } from "./WorkflowSaveModal";
import { CanvasWorkflowPickerModal } from "./CanvasWorkflowPickerModal";
import { LANGUAGE_OPTIONS, useI18n } from "@/i18n";
import { isBrowserFileSystemPath } from "@/utils/browserFileSystem";
import { HeaderUserMenu } from "@/components/auth/HeaderUserMenu";
@ -126,7 +124,6 @@ export function Header() {
const [projectModalMode, setProjectModalMode] = useState<"new" | "settings">("new");
const [showWorkflowBrowser, setShowWorkflowBrowser] = useState(false);
const [showWorkflowSaveModal, setShowWorkflowSaveModal] = useState(false);
const [showCanvasWorkflowPicker, setShowCanvasWorkflowPicker] = useState(false);
const [workflowNameDraft, setWorkflowNameDraft] = useState(workflowName || "");
const currentCanvasWorkflowId = useCanvasWorkflowStore((state) => state.currentCanvasWorkflowId);
const currentCanvasWorkflowDescription = useCanvasWorkflowStore((state) => state.currentCanvasWorkflowDescription);
@ -279,12 +276,6 @@ export function Header() {
onClose={() => setShowWorkflowSaveModal(false)}
/>
)}
{showCanvasWorkflowPicker && (
<CanvasWorkflowPickerModal
isOpen={showCanvasWorkflowPicker}
onClose={() => setShowCanvasWorkflowPicker(false)}
/>
)}
<header className="h-14 bg-neutral-950/95 border-b border-neutral-900 flex items-center justify-between px-5 shrink-0">
<div className="flex items-center gap-2">
<Link href="/" className="flex items-center gap-2 transition-opacity hover:opacity-80">
@ -469,9 +460,6 @@ export function Header() {
</div>
<div className="flex items-center gap-2 text-xs">
<Button size="small" onClick={() => setShowCanvasWorkflowPicker(true)}>
{t("canvasWorkflow.projects")}
</Button>
{/* Temporarily hidden until these actions have usable web flows. */}
{SHOW_UNUSED_HEADER_ACTIONS && (
<>
@ -534,7 +522,6 @@ export function Header() {
</button>
)}
<CommentsNavigationIcon />
<span className="text-neutral-500">·</span>
<label className="sr-only" htmlFor="language-select">
{t("language.label")}
</label>

26
src/i18n/index.tsx

@ -141,19 +141,6 @@ const en = {
"workflowSave.updateSuccess": "Workflow updated",
"workflowSave.failed": "Failed to save workflow",
"workflowSave.nameRequired": "Workflow name is required",
"canvasWorkflow.templates": "Templates",
"canvasWorkflow.projects": "Projects",
"canvasWorkflow.open": "Open",
"canvasWorkflow.empty": "No workflows",
"canvasWorkflow.noDescription": "No description",
"canvasWorkflow.listFailed": "Failed to load workflows",
"canvasWorkflow.deleteSuccess": "Workflow deleted",
"canvasWorkflow.deleteFailed": "Failed to delete workflow",
"canvasWorkflow.deleteConfirmTitle": "Delete workflow?",
"canvasWorkflow.deleteConfirmDesc": "This only deletes the cloud workflow record.",
"canvasWorkflow.columnName": "Workflow",
"canvasWorkflow.columnUpdatedAt": "Updated",
"canvasWorkflow.columnActions": "Actions",
"auth.loginNow": "Login",
"auth.loggedInUser": "Logged-in user",
"auth.points": "Points",
@ -1102,19 +1089,6 @@ const zhCN: Dictionary = {
"workflowSave.updateSuccess": "工作流已更新",
"workflowSave.failed": "保存工作流失败",
"workflowSave.nameRequired": "工作流名称必填",
"canvasWorkflow.templates": "模板",
"canvasWorkflow.projects": "项目",
"canvasWorkflow.open": "打开",
"canvasWorkflow.empty": "暂无工作流",
"canvasWorkflow.noDescription": "暂无描述",
"canvasWorkflow.listFailed": "加载工作流列表失败",
"canvasWorkflow.deleteSuccess": "工作流已删除",
"canvasWorkflow.deleteFailed": "删除工作流失败",
"canvasWorkflow.deleteConfirmTitle": "删除工作流?",
"canvasWorkflow.deleteConfirmDesc": "只会删除云端工作流记录。",
"canvasWorkflow.columnName": "工作流",
"canvasWorkflow.columnUpdatedAt": "更新时间",
"canvasWorkflow.columnActions": "操作",
"quickstart.description": "一个面向生成式 AI 流水线的节点式工作流编辑器。连接节点即可构建生成与转换图片、视频、音频和 3D 资产的流程。",
"quickstart.docs": "文档",
"quickstart.waitlist": "NB Pro 候补名单",

Loading…
Cancel
Save