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:
@@ -1,5 +1,5 @@
|
|||||||
import { Link } from 'react-router-dom'
|
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 type { TreeListItem } from '@/types'
|
||||||
import { TagBadges } from '@/components/common/TagBadges'
|
import { TagBadges } from '@/components/common/TagBadges'
|
||||||
import { StaggerList } from '@/components/common/StaggerList'
|
import { StaggerList } from '@/components/common/StaggerList'
|
||||||
@@ -10,6 +10,7 @@ import { getTreeEditorPath } from '@/lib/routing'
|
|||||||
interface TreeGridViewProps {
|
interface TreeGridViewProps {
|
||||||
trees: TreeListItem[]
|
trees: TreeListItem[]
|
||||||
onStartSession: (treeId: string, treeType?: string) => void
|
onStartSession: (treeId: string, treeType?: string) => void
|
||||||
|
onPrepareSession?: (tree: TreeListItem) => void
|
||||||
onTagClick: (tag: string) => void
|
onTagClick: (tag: string) => void
|
||||||
onFolderCreated: (parentId?: string | null) => void
|
onFolderCreated: (parentId?: string | null) => void
|
||||||
onDeleteTree: (tree: TreeListItem) => void
|
onDeleteTree: (tree: TreeListItem) => void
|
||||||
@@ -23,6 +24,7 @@ interface TreeGridViewProps {
|
|||||||
export function TreeGridView({
|
export function TreeGridView({
|
||||||
trees,
|
trees,
|
||||||
onStartSession,
|
onStartSession,
|
||||||
|
onPrepareSession,
|
||||||
onTagClick,
|
onTagClick,
|
||||||
onDeleteTree,
|
onDeleteTree,
|
||||||
onForkTree,
|
onForkTree,
|
||||||
@@ -169,6 +171,20 @@ export function TreeGridView({
|
|||||||
<Trash2 className="h-4 w-4" />
|
<Trash2 className="h-4 w-4" />
|
||||||
</button>
|
</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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onStartSession(tree.id, tree.tree_type)}
|
onClick={() => onStartSession(tree.id, tree.tree_type)}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Link } from 'react-router-dom'
|
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 type { TreeListItem } from '@/types'
|
||||||
import { TagBadges } from '@/components/common/TagBadges'
|
import { TagBadges } from '@/components/common/TagBadges'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
@@ -9,6 +9,7 @@ import { getTreeEditorPath } from '@/lib/routing'
|
|||||||
interface TreeListViewProps {
|
interface TreeListViewProps {
|
||||||
trees: TreeListItem[]
|
trees: TreeListItem[]
|
||||||
onStartSession: (treeId: string, treeType?: string) => void
|
onStartSession: (treeId: string, treeType?: string) => void
|
||||||
|
onPrepareSession?: (tree: TreeListItem) => void
|
||||||
onTagClick: (tag: string) => void
|
onTagClick: (tag: string) => void
|
||||||
onFolderCreated: (parentId?: string | null) => void
|
onFolderCreated: (parentId?: string | null) => void
|
||||||
onDeleteTree: (tree: TreeListItem) => void
|
onDeleteTree: (tree: TreeListItem) => void
|
||||||
@@ -22,6 +23,7 @@ interface TreeListViewProps {
|
|||||||
export function TreeListView({
|
export function TreeListView({
|
||||||
trees,
|
trees,
|
||||||
onStartSession,
|
onStartSession,
|
||||||
|
onPrepareSession,
|
||||||
onTagClick,
|
onTagClick,
|
||||||
onDeleteTree,
|
onDeleteTree,
|
||||||
onForkTree,
|
onForkTree,
|
||||||
@@ -172,6 +174,20 @@ export function TreeListView({
|
|||||||
</button>
|
</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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onStartSession(tree.id, tree.tree_type)}
|
onClick={() => onStartSession(tree.id, tree.tree_type)}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
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 type { TreeListItem } from '@/types'
|
||||||
import { TagBadges } from '@/components/common/TagBadges'
|
import { TagBadges } from '@/components/common/TagBadges'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
@@ -10,6 +10,7 @@ import { getTreeEditorPath } from '@/lib/routing'
|
|||||||
interface TreeTableViewProps {
|
interface TreeTableViewProps {
|
||||||
trees: TreeListItem[]
|
trees: TreeListItem[]
|
||||||
onStartSession: (treeId: string, treeType?: string) => void
|
onStartSession: (treeId: string, treeType?: string) => void
|
||||||
|
onPrepareSession?: (tree: TreeListItem) => void
|
||||||
onTagClick: (tag: string) => void
|
onTagClick: (tag: string) => void
|
||||||
onFolderCreated: (parentId?: string | null) => void
|
onFolderCreated: (parentId?: string | null) => void
|
||||||
onDeleteTree: (tree: TreeListItem) => void
|
onDeleteTree: (tree: TreeListItem) => void
|
||||||
@@ -26,6 +27,7 @@ type SortColumn = 'name' | 'category' | 'version' | 'usage' | 'updated'
|
|||||||
export function TreeTableView({
|
export function TreeTableView({
|
||||||
trees,
|
trees,
|
||||||
onStartSession,
|
onStartSession,
|
||||||
|
onPrepareSession,
|
||||||
onTagClick,
|
onTagClick,
|
||||||
onDeleteTree,
|
onDeleteTree,
|
||||||
onSortChange,
|
onSortChange,
|
||||||
@@ -283,6 +285,20 @@ export function TreeTableView({
|
|||||||
</button>
|
</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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onStartSession(tree.id, tree.tree_type)}
|
onClick={() => onStartSession(tree.id, tree.tree_type)}
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ import { treesApi } from '@/api/trees'
|
|||||||
import { categoriesApi } from '@/api/categories'
|
import { categoriesApi } from '@/api/categories'
|
||||||
import { foldersApi } from '@/api/folders'
|
import { foldersApi } from '@/api/folders'
|
||||||
import { sessionsApi } from '@/api/sessions'
|
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 { FolderEditModal } from '@/components/library/FolderEditModal'
|
||||||
import { ForkModal } from '@/components/library/ForkModal'
|
import { ForkModal } from '@/components/library/ForkModal'
|
||||||
import { ExportFlowModal } from '@/components/library/ExportFlowModal'
|
import { ExportFlowModal } from '@/components/library/ExportFlowModal'
|
||||||
import { ImportFlowModal } from '@/components/library/ImportFlowModal'
|
import { ImportFlowModal } from '@/components/library/ImportFlowModal'
|
||||||
|
import { PrepareSessionModal } from '@/components/procedural/PrepareSessionModal'
|
||||||
|
import { accountsApi } from '@/api/accounts'
|
||||||
import { ConfirmDialog } from '@/components/common/ConfirmDialog'
|
import { ConfirmDialog } from '@/components/common/ConfirmDialog'
|
||||||
import { TreeGridView } from '@/components/library/TreeGridView'
|
import { TreeGridView } from '@/components/library/TreeGridView'
|
||||||
import { TreeListView } from '@/components/library/TreeListView'
|
import { TreeListView } from '@/components/library/TreeListView'
|
||||||
@@ -83,6 +85,13 @@ export function TreeLibraryPage() {
|
|||||||
const [showImportModal, setShowImportModal] = useState(false)
|
const [showImportModal, setShowImportModal] = useState(false)
|
||||||
const [exportTarget, setExportTarget] = useState<TreeListItem | null>(null)
|
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
|
// AI builder state
|
||||||
|
|
||||||
const { aiEnabled } = useCachedQuota()
|
const { aiEnabled } = useCachedQuota()
|
||||||
@@ -268,6 +277,24 @@ export function TreeLibraryPage() {
|
|||||||
if (tree) setExportTarget(tree)
|
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 =
|
const hasActiveFilters =
|
||||||
selectedCategoryId || selectedTags.length > 0 || searchQuery || selectedFolderId
|
selectedCategoryId || selectedTags.length > 0 || searchQuery || selectedFolderId
|
||||||
|
|
||||||
@@ -498,6 +525,7 @@ export function TreeLibraryPage() {
|
|||||||
<TreeGridView
|
<TreeGridView
|
||||||
trees={trees}
|
trees={trees}
|
||||||
onStartSession={handleStartSession}
|
onStartSession={handleStartSession}
|
||||||
|
onPrepareSession={handlePrepareSession}
|
||||||
onTagClick={handleTagClick}
|
onTagClick={handleTagClick}
|
||||||
onFolderCreated={handleCreateFolder}
|
onFolderCreated={handleCreateFolder}
|
||||||
onDeleteTree={(tree) => {
|
onDeleteTree={(tree) => {
|
||||||
@@ -515,6 +543,7 @@ export function TreeLibraryPage() {
|
|||||||
<TreeListView
|
<TreeListView
|
||||||
trees={trees}
|
trees={trees}
|
||||||
onStartSession={handleStartSession}
|
onStartSession={handleStartSession}
|
||||||
|
onPrepareSession={handlePrepareSession}
|
||||||
onTagClick={handleTagClick}
|
onTagClick={handleTagClick}
|
||||||
onFolderCreated={handleCreateFolder}
|
onFolderCreated={handleCreateFolder}
|
||||||
onDeleteTree={(tree) => {
|
onDeleteTree={(tree) => {
|
||||||
@@ -532,6 +561,7 @@ export function TreeLibraryPage() {
|
|||||||
<TreeTableView
|
<TreeTableView
|
||||||
trees={trees}
|
trees={trees}
|
||||||
onStartSession={handleStartSession}
|
onStartSession={handleStartSession}
|
||||||
|
onPrepareSession={handlePrepareSession}
|
||||||
onTagClick={handleTagClick}
|
onTagClick={handleTagClick}
|
||||||
onFolderCreated={handleCreateFolder}
|
onFolderCreated={handleCreateFolder}
|
||||||
onDeleteTree={(tree) => {
|
onDeleteTree={(tree) => {
|
||||||
@@ -604,6 +634,18 @@ export function TreeLibraryPage() {
|
|||||||
onClose={() => { setShowImportModal(false); loadTrees() }}
|
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>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user