feat: add flow export/import frontend + backend tests

Frontend:
- ExportFlowModal with JSON/XML format selection + download
- ImportFlowModal with drag-drop file picker + preview step
- rfflowParser for client-side JSON/XML .rfflow parsing
- Export buttons on editor toolbar and library action menus
- Import button on library page next to Create New
- Provenance display for imported flows in editor
- flowTransfer API client + types

Backend:
- Fix regex->pattern deprecation in export endpoint
- 12 integration tests covering export, import, round-trip,
  access control, tag/category creation, version validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-03-06 00:18:10 -05:00
parent ee9895de5d
commit 39677a3841
15 changed files with 1099 additions and 6 deletions

View File

@@ -1,5 +1,5 @@
import { Link } from 'react-router-dom'
import { Pencil, Globe, Lock, Trash2, GitBranch, FileText, Wrench, Star } from 'lucide-react'
import { Pencil, Globe, Lock, Trash2, GitBranch, FileText, Wrench, Star, Download } from 'lucide-react'
import type { TreeListItem } from '@/types'
import { TagBadges } from '@/components/common/TagBadges'
import { cn } from '@/lib/utils'
@@ -13,6 +13,7 @@ interface TreeGridViewProps {
onFolderCreated: (parentId?: string | null) => void
onDeleteTree: (tree: TreeListItem) => void
onForkTree?: (treeId: string) => void
onExportTree?: (treeId: string) => void
pinnedTreeIds?: Set<string>
onTogglePin?: (treeId: string) => void
pinLoadingTreeIds?: Set<string>
@@ -24,6 +25,7 @@ export function TreeGridView({
onTagClick,
onDeleteTree,
onForkTree,
onExportTree,
pinnedTreeIds,
onTogglePin,
pinLoadingTreeIds,
@@ -111,6 +113,20 @@ export function TreeGridView({
v{tree.version} · {tree.usage_count} uses
</span>
<div className="flex items-center gap-2">
{onExportTree && (
<button
type="button"
onClick={() => onExportTree(tree.id)}
className={cn(
'rounded-md border border-border p-2 text-muted-foreground',
'hover:bg-accent hover:text-foreground'
)}
title="Export flow"
aria-label="Export flow"
>
<Download className="h-4 w-4" />
</button>
)}
{onForkTree && (
<button
type="button"