feat: task lane persistence + sidebar cleanup #121
@@ -117,6 +117,9 @@ def _build_session_detail(session: AISession) -> AISessionDetail:
|
||||
resolved_at=session.resolved_at,
|
||||
steps=step_responses,
|
||||
conversation_messages=session.conversation_messages or [],
|
||||
pending_task_lane=session.pending_task_lane,
|
||||
is_branching=getattr(session, 'is_branching', False),
|
||||
active_branch_id=str(session.active_branch_id) if getattr(session, 'active_branch_id', None) else None,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { ArrowLeft, Loader2, Save, Scan, Trash2 } from 'lucide-react'
|
||||
import { Input } from '@/components/ui/Input'
|
||||
import { Textarea } from '@/components/ui/Textarea'
|
||||
@@ -64,16 +64,19 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
|
||||
const [detectedCandidates, setDetectedCandidates] = useState<ParameterCandidate[]>([])
|
||||
const [showStepper, setShowStepper] = useState(false)
|
||||
const [detectionSummary, setDetectionSummary] = useState<string | null>(null)
|
||||
const acceptingCandidateRef = useRef(false)
|
||||
|
||||
const { canShareScriptTemplate } = usePermissions()
|
||||
|
||||
// Dismiss stepper if user edits the script body during detection
|
||||
// Dismiss stepper if user manually edits the script body during detection
|
||||
// (but NOT when handleAcceptCandidate programmatically updates script_body)
|
||||
const scriptBodyRef = form.script_body
|
||||
useEffect(() => {
|
||||
if (showStepper) {
|
||||
if (showStepper && !acceptingCandidateRef.current) {
|
||||
setShowStepper(false)
|
||||
setDetectedCandidates([])
|
||||
}
|
||||
acceptingCandidateRef.current = false
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [scriptBodyRef])
|
||||
|
||||
@@ -263,6 +266,7 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
|
||||
sensitive: overrides.sensitive,
|
||||
}
|
||||
|
||||
acceptingCandidateRef.current = true
|
||||
setForm(f => ({
|
||||
...f,
|
||||
script_body: updatedScript,
|
||||
|
||||
@@ -102,7 +102,9 @@ export default function AssistantChatPage() {
|
||||
|
||||
// Restore session from sessionStorage on mount (when URL has no session ID)
|
||||
useEffect(() => {
|
||||
console.log('[AssistantChat] Mount restore check — urlSessionId:', urlSessionId, 'activeChatId:', activeChatId)
|
||||
if (!urlSessionId && activeChatId) {
|
||||
console.log('[AssistantChat] Calling selectChat to restore:', activeChatId)
|
||||
selectChat(activeChatId)
|
||||
}
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
@@ -207,6 +209,7 @@ export default function AssistantChatPage() {
|
||||
}
|
||||
|
||||
const selectChat = useCallback(async (chatId: string) => {
|
||||
console.log('[AssistantChat] selectChat called with:', chatId)
|
||||
setActiveChatId(chatId)
|
||||
// Clear TaskLane when switching chats — will restore from backend if available
|
||||
setShowTaskLane(false)
|
||||
@@ -214,6 +217,7 @@ export default function AssistantChatPage() {
|
||||
setActiveActions([])
|
||||
try {
|
||||
const detail = await aiSessionsApi.getSession(chatId)
|
||||
console.log('[AssistantChat] getSession response — messages:', detail.conversation_messages?.length, 'pending_task_lane:', !!detail.pending_task_lane)
|
||||
setMessages(
|
||||
(detail.conversation_messages || []).map(m => ({
|
||||
role: m.role as 'user' | 'assistant',
|
||||
@@ -237,7 +241,8 @@ export default function AssistantChatPage() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.error('[AssistantChat] Failed to load chat session:', err)
|
||||
setMessages([])
|
||||
}
|
||||
}, [])
|
||||
|
||||
Reference in New Issue
Block a user