Implement session outcomes, step timing, and live timer fixes
This commit is contained in:
@@ -19,9 +19,10 @@ interface UseCustomStepFlowParams {
|
||||
setPathTaken: (path: string[]) => void
|
||||
setDecisions: (decisions: DecisionRecord[]) => void
|
||||
setNotes: (notes: string) => void
|
||||
setIsCompleting: (completing: boolean) => void
|
||||
setError: (error: string | null) => void
|
||||
onEnterNode: (enteredAtIso: string) => void
|
||||
isCompleting: boolean
|
||||
onRequestCompletion: (completionDecision: DecisionRecord, source: 'custom') => void
|
||||
}
|
||||
|
||||
export function useCustomStepFlow({
|
||||
@@ -36,9 +37,10 @@ export function useCustomStepFlow({
|
||||
setPathTaken,
|
||||
setDecisions,
|
||||
setNotes,
|
||||
setIsCompleting,
|
||||
setError,
|
||||
onEnterNode,
|
||||
isCompleting,
|
||||
onRequestCompletion,
|
||||
}: UseCustomStepFlowParams) {
|
||||
const navigate = useNavigate()
|
||||
|
||||
@@ -112,9 +114,11 @@ export function useCustomStepFlow({
|
||||
|
||||
// Navigate back to a previously-created custom step from the decision node
|
||||
const handleNavigateToCustomStep = (customStep: CustomStep) => {
|
||||
const enteredAt = new Date().toISOString()
|
||||
const newPath = [...pathTaken, customStep.id]
|
||||
setPathTaken(newPath)
|
||||
setCurrentNodeId(customStep.id)
|
||||
onEnterNode(enteredAt)
|
||||
}
|
||||
|
||||
// Called when CustomStepModal submits - show action modal instead of inserting directly
|
||||
@@ -169,6 +173,7 @@ export function useCustomStepFlow({
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
|
||||
const decisionTimestamp = new Date().toISOString()
|
||||
const newDecision: DecisionRecord = {
|
||||
node_id: customStep.id,
|
||||
question: null,
|
||||
@@ -176,7 +181,10 @@ export function useCustomStepFlow({
|
||||
action_performed: `Custom Step: ${pendingStep.title}`,
|
||||
notes: pendingStep.content.instructions || null,
|
||||
automation_used: false,
|
||||
timestamp: new Date().toISOString(),
|
||||
timestamp: decisionTimestamp,
|
||||
entered_at: decisionTimestamp,
|
||||
exited_at: decisionTimestamp,
|
||||
duration_seconds: 0,
|
||||
attachments: []
|
||||
}
|
||||
|
||||
@@ -188,6 +196,7 @@ export function useCustomStepFlow({
|
||||
setDecisions(newDecisions)
|
||||
setPathTaken(newPath)
|
||||
setCurrentNodeId(customStep.id)
|
||||
onEnterNode(decisionTimestamp)
|
||||
|
||||
await sessionsApi.update(session.id, {
|
||||
path_taken: newPath,
|
||||
@@ -236,9 +245,11 @@ export function useCustomStepFlow({
|
||||
const handleContinueToDescendant = async () => {
|
||||
if (!pendingContinuationNodeId || !session) return
|
||||
|
||||
const enteredAt = new Date().toISOString()
|
||||
const newPath = [...pathTaken, pendingContinuationNodeId]
|
||||
setPathTaken(newPath)
|
||||
setCurrentNodeId(pendingContinuationNodeId)
|
||||
onEnterNode(enteredAt)
|
||||
setNotes('')
|
||||
setPendingContinuationNodeId(null)
|
||||
|
||||
@@ -259,38 +270,18 @@ export function useCustomStepFlow({
|
||||
const handleCustomBranchComplete = async () => {
|
||||
if (!session) return
|
||||
|
||||
setIsCompleting(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
const completionDecision: DecisionRecord = {
|
||||
node_id: currentNodeId,
|
||||
question: null,
|
||||
answer: null,
|
||||
action_performed: 'Custom Branch Completed',
|
||||
notes: notes || 'Issue resolved via custom troubleshooting steps',
|
||||
automation_used: false,
|
||||
timestamp: new Date().toISOString(),
|
||||
attachments: []
|
||||
}
|
||||
|
||||
await sessionsApi.update(session.id, {
|
||||
decisions: [...decisions, completionDecision]
|
||||
})
|
||||
|
||||
await sessionsApi.complete(session.id)
|
||||
|
||||
if (customSteps.length > 0) {
|
||||
setShowForkModal(true)
|
||||
} else {
|
||||
navigate(`/sessions/${session.id}`)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to complete session:', err)
|
||||
setError('Failed to complete session. Please try again.')
|
||||
} finally {
|
||||
setIsCompleting(false)
|
||||
const completionDecision: DecisionRecord = {
|
||||
node_id: currentNodeId,
|
||||
question: null,
|
||||
answer: null,
|
||||
action_performed: 'Custom Branch Completed',
|
||||
notes: notes || 'Issue resolved via custom troubleshooting steps',
|
||||
automation_used: false,
|
||||
timestamp: new Date().toISOString(),
|
||||
attachments: []
|
||||
}
|
||||
onRequestCompletion(completionDecision, 'custom')
|
||||
}
|
||||
|
||||
// Fork tree with custom branch
|
||||
|
||||
@@ -5,12 +5,23 @@ export function useSessionTimer(startedAt: string | undefined | null): string |
|
||||
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
// Always clear any previous interval before (re)initializing.
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current)
|
||||
intervalRef.current = null
|
||||
}
|
||||
|
||||
if (!startedAt) {
|
||||
setElapsed(null)
|
||||
return
|
||||
}
|
||||
|
||||
const startTime = new Date(startedAt).getTime()
|
||||
const parsedStartTime = new Date(startedAt).getTime()
|
||||
// If the server timestamp is invalid or ahead of the local clock, fall back to "now"
|
||||
// so the timer still starts ticking immediately for the user.
|
||||
const startTime = Number.isNaN(parsedStartTime) || parsedStartTime > Date.now()
|
||||
? Date.now()
|
||||
: parsedStartTime
|
||||
|
||||
const tick = () => {
|
||||
const diff = Math.max(0, Math.floor((Date.now() - startTime) / 1000))
|
||||
|
||||
Reference in New Issue
Block a user