Implement session outcomes, step timing, and live timer fixes
This commit is contained in:
122
frontend/src/components/session/SessionOutcomeModal.tsx
Normal file
122
frontend/src/components/session/SessionOutcomeModal.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Modal } from '@/components/common/Modal'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { SessionOutcome } from '@/types'
|
||||
|
||||
interface SessionOutcomeModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSubmit: (data: { outcome: SessionOutcome; outcome_notes?: string }) => Promise<void>
|
||||
isSubmitting?: boolean
|
||||
}
|
||||
|
||||
const OUTCOME_OPTIONS: Array<{ value: SessionOutcome; label: string; description: string }> = [
|
||||
{ value: 'resolved', label: 'Resolved', description: 'Issue fully resolved in this session.' },
|
||||
{ value: 'workaround', label: 'Workaround', description: 'Temporary fix applied, root cause remains.' },
|
||||
{ value: 'escalated', label: 'Escalated', description: 'Handed off to another engineer/team.' },
|
||||
{ value: 'unresolved', label: 'Unresolved', description: 'No fix or workaround identified yet.' },
|
||||
]
|
||||
|
||||
export function SessionOutcomeModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSubmit,
|
||||
isSubmitting = false,
|
||||
}: SessionOutcomeModalProps) {
|
||||
const [outcome, setOutcome] = useState<SessionOutcome>('resolved')
|
||||
const [outcomeNotes, setOutcomeNotes] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return
|
||||
setOutcome('resolved')
|
||||
setOutcomeNotes('')
|
||||
}, [isOpen])
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await onSubmit({
|
||||
outcome,
|
||||
outcome_notes: outcomeNotes.trim() || undefined,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
title="Session Outcome"
|
||||
footer={(
|
||||
<div className="flex justify-end gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
disabled={isSubmitting}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60',
|
||||
'hover:bg-white/10 hover:text-white disabled:opacity-50'
|
||||
)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSubmit}
|
||||
disabled={isSubmitting}
|
||||
className={cn(
|
||||
'rounded-md bg-white px-4 py-2 text-sm font-medium text-black',
|
||||
'hover:bg-white/90 disabled:opacity-50'
|
||||
)}
|
||||
>
|
||||
{isSubmitting ? 'Completing...' : 'Complete Session'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm text-white/70">
|
||||
Select the session outcome before completion.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{OUTCOME_OPTIONS.map((option) => (
|
||||
<label
|
||||
key={option.value}
|
||||
className={cn(
|
||||
'block cursor-pointer rounded-lg border border-white/10 p-3 transition-colors',
|
||||
outcome === option.value ? 'border-white/30 bg-white/10' : 'hover:bg-white/[0.04]'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<input
|
||||
type="radio"
|
||||
name="session-outcome"
|
||||
value={option.value}
|
||||
checked={outcome === option.value}
|
||||
onChange={() => setOutcome(option.value)}
|
||||
className="mt-1 h-4 w-4"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white">{option.label}</p>
|
||||
<p className="text-xs text-white/50">{option.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">Outcome Notes (optional)</label>
|
||||
<textarea
|
||||
value={outcomeNotes}
|
||||
onChange={(e) => setOutcomeNotes(e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Add context for this outcome..."
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-white/10 bg-black/50 px-3 py-2',
|
||||
'text-sm text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
@@ -2,3 +2,4 @@ export { PostStepActionModal } from './PostStepActionModal'
|
||||
export { ContinuationModal, type DescendantNode } from './ContinuationModal'
|
||||
export { ForkTreeModal } from './ForkTreeModal'
|
||||
export { ScratchpadSidebar } from './ScratchpadSidebar'
|
||||
export { SessionOutcomeModal } from './SessionOutcomeModal'
|
||||
|
||||
Reference in New Issue
Block a user