feat: wire PrepareSessionModal entry point in Flow Library

Add "Prepare session" button (clipboard icon) to grid, list, and table
views for procedural/maintenance flows. Clicking fetches tree intake
fields and account members, then opens PrepareSessionModal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-03-10 08:22:21 -04:00
parent 9387902497
commit 2d40687fd9
4 changed files with 94 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
import { Link } from 'react-router-dom'
import { Pencil, Globe, Lock, Trash2, GitBranch, FileText, Wrench, Star, Download } from 'lucide-react'
import { Pencil, Globe, Lock, Trash2, GitBranch, FileText, Wrench, Star, Download, ClipboardList } from 'lucide-react'
import type { TreeListItem } from '@/types'
import { TagBadges } from '@/components/common/TagBadges'
import { StaggerList } from '@/components/common/StaggerList'
@@ -10,6 +10,7 @@ import { getTreeEditorPath } from '@/lib/routing'
interface TreeGridViewProps {
trees: TreeListItem[]
onStartSession: (treeId: string, treeType?: string) => void
onPrepareSession?: (tree: TreeListItem) => void
onTagClick: (tag: string) => void
onFolderCreated: (parentId?: string | null) => void
onDeleteTree: (tree: TreeListItem) => void
@@ -23,6 +24,7 @@ interface TreeGridViewProps {
export function TreeGridView({
trees,
onStartSession,
onPrepareSession,
onTagClick,
onDeleteTree,
onForkTree,
@@ -169,6 +171,20 @@ export function TreeGridView({
<Trash2 className="h-4 w-4" />
</button>
)}
{onPrepareSession && tree.tree_type !== 'troubleshooting' && (
<button
type="button"
onClick={() => onPrepareSession(tree)}
className={cn(
'rounded-md border border-border p-2 text-muted-foreground',
'hover:bg-cyan-500/10 hover:text-cyan-400 hover:border-cyan-500/30'
)}
title="Prepare session for engineer"
aria-label="Prepare session"
>
<ClipboardList className="h-4 w-4" />
</button>
)}
<button
type="button"
onClick={() => onStartSession(tree.id, tree.tree_type)}

View File

@@ -1,5 +1,5 @@
import { Link } from 'react-router-dom'
import { Pencil, Globe, Lock, GitBranch, FileText, Trash2, Wrench, Star, Download } from 'lucide-react'
import { Pencil, Globe, Lock, GitBranch, FileText, Trash2, Wrench, Star, Download, ClipboardList } from 'lucide-react'
import type { TreeListItem } from '@/types'
import { TagBadges } from '@/components/common/TagBadges'
import { cn } from '@/lib/utils'
@@ -9,6 +9,7 @@ import { getTreeEditorPath } from '@/lib/routing'
interface TreeListViewProps {
trees: TreeListItem[]
onStartSession: (treeId: string, treeType?: string) => void
onPrepareSession?: (tree: TreeListItem) => void
onTagClick: (tag: string) => void
onFolderCreated: (parentId?: string | null) => void
onDeleteTree: (tree: TreeListItem) => void
@@ -22,6 +23,7 @@ interface TreeListViewProps {
export function TreeListView({
trees,
onStartSession,
onPrepareSession,
onTagClick,
onDeleteTree,
onForkTree,
@@ -172,6 +174,20 @@ export function TreeListView({
</button>
</>
)}
{onPrepareSession && tree.tree_type !== 'troubleshooting' && (
<button
type="button"
onClick={() => onPrepareSession(tree)}
className={cn(
'rounded-md border border-border p-1.5 text-muted-foreground',
'hover:bg-cyan-500/10 hover:text-cyan-400 hover:border-cyan-500/30'
)}
title="Prepare session for engineer"
aria-label="Prepare session"
>
<ClipboardList className="h-4 w-4" />
</button>
)}
<button
type="button"
onClick={() => onStartSession(tree.id, tree.tree_type)}

View File

@@ -1,6 +1,6 @@
import { useState } from 'react'
import { Link } from 'react-router-dom'
import { Pencil, Globe, Lock, ChevronUp, ChevronDown, GitBranch, FileText, Trash2, Wrench, Star, Download } from 'lucide-react'
import { Pencil, Globe, Lock, ChevronUp, ChevronDown, GitBranch, FileText, Trash2, Wrench, Star, Download, ClipboardList } from 'lucide-react'
import type { TreeListItem } from '@/types'
import { TagBadges } from '@/components/common/TagBadges'
import { cn } from '@/lib/utils'
@@ -10,6 +10,7 @@ import { getTreeEditorPath } from '@/lib/routing'
interface TreeTableViewProps {
trees: TreeListItem[]
onStartSession: (treeId: string, treeType?: string) => void
onPrepareSession?: (tree: TreeListItem) => void
onTagClick: (tag: string) => void
onFolderCreated: (parentId?: string | null) => void
onDeleteTree: (tree: TreeListItem) => void
@@ -26,6 +27,7 @@ type SortColumn = 'name' | 'category' | 'version' | 'usage' | 'updated'
export function TreeTableView({
trees,
onStartSession,
onPrepareSession,
onTagClick,
onDeleteTree,
onSortChange,
@@ -283,6 +285,20 @@ export function TreeTableView({
</button>
</>
)}
{onPrepareSession && tree.tree_type !== 'troubleshooting' && (
<button
type="button"
onClick={() => onPrepareSession(tree)}
className={cn(
'rounded-md border border-border p-1.5 text-muted-foreground',
'hover:bg-cyan-500/10 hover:text-cyan-400 hover:border-cyan-500/30'
)}
title="Prepare session for engineer"
aria-label="Prepare session"
>
<ClipboardList className="h-3.5 w-3.5" />
</button>
)}
<button
type="button"
onClick={() => onStartSession(tree.id, tree.tree_type)}

View File

@@ -7,11 +7,13 @@ import { treesApi } from '@/api/trees'
import { categoriesApi } from '@/api/categories'
import { foldersApi } from '@/api/folders'
import { sessionsApi } from '@/api/sessions'
import type { TreeListItem, CategoryListItem, FolderListItem, Session } from '@/types'
import type { TreeListItem, CategoryListItem, FolderListItem, Session, IntakeFormField } from '@/types'
import { FolderEditModal } from '@/components/library/FolderEditModal'
import { ForkModal } from '@/components/library/ForkModal'
import { ExportFlowModal } from '@/components/library/ExportFlowModal'
import { ImportFlowModal } from '@/components/library/ImportFlowModal'
import { PrepareSessionModal } from '@/components/procedural/PrepareSessionModal'
import { accountsApi } from '@/api/accounts'
import { ConfirmDialog } from '@/components/common/ConfirmDialog'
import { TreeGridView } from '@/components/library/TreeGridView'
import { TreeListView } from '@/components/library/TreeListView'
@@ -83,6 +85,13 @@ export function TreeLibraryPage() {
const [showImportModal, setShowImportModal] = useState(false)
const [exportTarget, setExportTarget] = useState<TreeListItem | null>(null)
// Prepare session modal state
const [prepareTarget, setPrepareTarget] = useState<{
tree: TreeListItem
intakeFields: IntakeFormField[]
teamMembers: { id: string; name: string; email: string }[]
} | null>(null)
// AI builder state
const { aiEnabled } = useCachedQuota()
@@ -268,6 +277,24 @@ export function TreeLibraryPage() {
if (tree) setExportTarget(tree)
}
const handlePrepareSession = async (tree: TreeListItem) => {
try {
const [treeDetail, members] = await Promise.all([
treesApi.get(tree.id),
accountsApi.getMembers().catch(() => []),
])
setPrepareTarget({
tree,
intakeFields: treeDetail.intake_form || [],
teamMembers: members
.filter(m => m.is_active)
.map(m => ({ id: m.id, name: m.name, email: m.email })),
})
} catch {
toast.error('Failed to load flow details')
}
}
const hasActiveFilters =
selectedCategoryId || selectedTags.length > 0 || searchQuery || selectedFolderId
@@ -498,6 +525,7 @@ export function TreeLibraryPage() {
<TreeGridView
trees={trees}
onStartSession={handleStartSession}
onPrepareSession={handlePrepareSession}
onTagClick={handleTagClick}
onFolderCreated={handleCreateFolder}
onDeleteTree={(tree) => {
@@ -515,6 +543,7 @@ export function TreeLibraryPage() {
<TreeListView
trees={trees}
onStartSession={handleStartSession}
onPrepareSession={handlePrepareSession}
onTagClick={handleTagClick}
onFolderCreated={handleCreateFolder}
onDeleteTree={(tree) => {
@@ -532,6 +561,7 @@ export function TreeLibraryPage() {
<TreeTableView
trees={trees}
onStartSession={handleStartSession}
onPrepareSession={handlePrepareSession}
onTagClick={handleTagClick}
onFolderCreated={handleCreateFolder}
onDeleteTree={(tree) => {
@@ -604,6 +634,18 @@ export function TreeLibraryPage() {
onClose={() => { setShowImportModal(false); loadTrees() }}
/>
)}
{prepareTarget && (
<PrepareSessionModal
isOpen
onClose={() => setPrepareTarget(null)}
treeId={prepareTarget.tree.id}
treeName={prepareTarget.tree.name}
intakeFields={prepareTarget.intakeFields}
teamMembers={prepareTarget.teamMembers}
onPrepared={() => setPrepareTarget(null)}
/>
)}
</div>
</div>
)