import { useEffect, useMemo, useState } from 'react'; import { FolderKanban, Loader2, Pencil, Plus, Trash2, } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { IMAGE_WORKSPACE_CREATE_PROJECT_EVENT } from '@/lib/image-workspace'; import { cn } from '@/lib/utils'; import { useImageWorkspaceStore } from '@/stores/image-workspace'; import type { DesignWorkspace, DesignWorkspaceSummary } from '../../../shared/image-workspace'; import { DesignAssetThumbnail } from './DesignAssetThumbnail'; import { hasActiveCreationPlan } from './plan-lifecycle'; type ProjectDialogState = | { mode: 'create'; project: null } | { mode: 'rename'; project: DesignWorkspaceSummary }; function formatUpdatedAt(value: string): string { const timestamp = Date.parse(value); if (!Number.isFinite(timestamp)) return ''; const date = new Date(timestamp); const now = new Date(); if (date.toDateString() === now.toDateString()) { return `今天 ${date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`; } return date.toLocaleDateString([], { month: 'numeric', day: 'numeric' }); } function activeProjectStatus(workspace: DesignWorkspace | null): { label: string; tone: 'active' | 'success' | 'neutral'; } { const activeTask = workspace?.tasks.find((task) => task.status === 'queued' || task.status === 'running'); if (activeTask) return { label: '制作中', tone: 'active' }; if (workspace?.form.activeQuotes.some((quote) => quote.status === 'offered')) { return { label: '待确认', tone: 'active' }; } if (workspace && hasActiveCreationPlan(workspace)) { return { label: '刚刚更新', tone: 'neutral' }; } const completed = workspace?.tasks.some((task) => task.status === 'succeeded'); if (completed) return { label: '已完成', tone: 'success' }; return { label: '刚刚更新', tone: 'neutral' }; } export function DesignWorksRail({ workspace, onProjectSelected, }: { workspace: DesignWorkspace | null; onProjectSelected?: () => void; }) { const bootstrap = useImageWorkspaceStore((state) => state.bootstrap); const activeWorkspaceId = useImageWorkspaceStore((state) => state.activeWorkspaceId); const deletingWorkspaceId = useImageWorkspaceStore((state) => state.deletingWorkspaceId); const createProject = useImageWorkspaceStore((state) => state.createProject); const renameProject = useImageWorkspaceStore((state) => state.renameProject); const deleteProject = useImageWorkspaceStore((state) => state.deleteProject); const selectProject = useImageWorkspaceStore((state) => state.selectProject); const [dialog, setDialog] = useState(null); const [projectName, setProjectName] = useState(''); const [dialogError, setDialogError] = useState(null); const [saving, setSaving] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const [deleteConfirmation, setDeleteConfirmation] = useState(''); const [deleteError, setDeleteError] = useState(null); const projects = useMemo( () => [...(bootstrap?.workspaces ?? [])].sort( (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt), ), [bootstrap?.workspaces], ); const currentPreview = useMemo(() => { if (!workspace) return null; return [...workspace.assets].sort( (left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt), )[0] ?? null; }, [workspace]); const currentStatus = activeProjectStatus(workspace); const openCreate = () => { setProjectName(''); setDialogError(null); setDialog({ mode: 'create', project: null }); }; useEffect(() => { const handleCreateRequest = () => { setProjectName(''); setDialogError(null); setDialog({ mode: 'create', project: null }); }; window.addEventListener(IMAGE_WORKSPACE_CREATE_PROJECT_EVENT, handleCreateRequest); return () => window.removeEventListener(IMAGE_WORKSPACE_CREATE_PROJECT_EVENT, handleCreateRequest); }, []); const openRename = (project: DesignWorkspaceSummary) => { setProjectName(project.title); setDialogError(null); setDialog({ mode: 'rename', project }); }; const saveProject = async () => { const title = projectName.trim(); if (!title) { setDialogError('请输入作品名称'); return; } setSaving(true); setDialogError(null); try { if (dialog?.mode === 'rename') { await renameProject(dialog.project.workspaceId, title); toast.success('作品名称已更新'); } else { await createProject(title); toast.success('新作品已创建'); } setDialog(null); onProjectSelected?.(); } catch (error) { setDialogError(error instanceof Error ? error.message : '保存作品失败'); } finally { setSaving(false); } }; const confirmDelete = async () => { if (!deleteTarget) return; if (deleteConfirmation !== deleteTarget.title) { setDeleteError('请输入完整作品名称'); return; } setDeleteError(null); try { await deleteProject(deleteTarget.workspaceId); setDeleteTarget(null); setDeleteConfirmation(''); toast.success('作品已删除'); } catch (error) { setDeleteError(error instanceof Error ? error.message : '删除作品失败'); } }; return ( ); }