feat: flexible intake — deferred variables + prepared sessions (#103)
* feat: flexible intake — deferred variables + prepared sessions
Remove blocking intake form modal. Variables are now filled inline during
flow execution or pre-filled via prepared sessions. Adds PATCH /sessions/{id}/variables
endpoint, POST /sessions/prepare for session pre-staging, inline variable prompts
in StepDetail, editable Session Variables panel, and "Prepared for You" dashboard section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: pass treeData directly to startSession to avoid stale state
setTree(treeData) hasn't committed when startSession runs immediately
after, so tree is still null and getStepsFromTree returns []. This
caused the step detail area to render empty on new session start.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 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>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #103.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -436,7 +463,7 @@ export function TreeLibraryPage() {
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{s.client_name && `${s.client_name} · `}
|
||||
Started {formatTimeAgo(s.started_at)}
|
||||
{s.started_at ? `Started ${formatTimeAgo(s.started_at)}` : 'Not started'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user