3,200+ hardcoded color values replaced with CSS variable-backed Tailwind classes (bg-card, text-foreground, border-border, etc.). Enables light mode via CSS variable swap. Only syntax highlighting colors and intentional one-offs remain hardcoded (~15 values). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
131 lines
4.5 KiB
TypeScript
131 lines
4.5 KiB
TypeScript
import { useRef } from 'react'
|
|
import { Modal } from '@/components/common/Modal'
|
|
import { cn } from '@/lib/utils'
|
|
import type { SessionOutcome } from '@/types'
|
|
import { Button } from '@/components/ui/Button'
|
|
|
|
interface SessionOutcomeModalProps {
|
|
isOpen: boolean
|
|
onClose: () => void
|
|
onSubmit: (data: { outcome: SessionOutcome; outcome_notes?: string; next_steps?: 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 formRef = useRef<HTMLFormElement | null>(null)
|
|
|
|
const handleSubmit = async () => {
|
|
if (!formRef.current) return
|
|
const formData = new FormData(formRef.current)
|
|
const outcome = (formData.get('session-outcome') as SessionOutcome | null) ?? 'resolved'
|
|
const outcomeNotes = ((formData.get('outcome-notes') as string | null) ?? '').trim()
|
|
const nextSteps = ((formData.get('next-steps') as string | null) ?? '').trim()
|
|
|
|
await onSubmit({
|
|
outcome,
|
|
outcome_notes: outcomeNotes || undefined,
|
|
next_steps: nextSteps || undefined,
|
|
})
|
|
}
|
|
|
|
return (
|
|
<Modal
|
|
isOpen={isOpen}
|
|
onClose={onClose}
|
|
title="Session Outcome"
|
|
footer={(
|
|
<div className="flex justify-end gap-2">
|
|
<Button
|
|
type="button"
|
|
variant="secondary"
|
|
onClick={onClose}
|
|
disabled={isSubmitting}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
onClick={handleSubmit}
|
|
loading={isSubmitting}
|
|
>
|
|
Complete Session
|
|
</Button>
|
|
</div>
|
|
)}
|
|
>
|
|
<form key={String(isOpen)} ref={formRef} className="space-y-4">
|
|
<p className="text-sm text-muted-foreground">
|
|
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-border p-3 transition-colors',
|
|
'hover:bg-accent/50'
|
|
)}
|
|
>
|
|
<div className="flex items-start gap-3">
|
|
<input
|
|
type="radio"
|
|
name="session-outcome"
|
|
value={option.value}
|
|
defaultChecked={option.value === 'resolved'}
|
|
className="mt-1 h-4 w-4"
|
|
/>
|
|
<div>
|
|
<p className="text-sm font-medium text-foreground">{option.label}</p>
|
|
<p className="text-xs text-muted-foreground">{option.description}</p>
|
|
</div>
|
|
</div>
|
|
</label>
|
|
))}
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-foreground">Outcome Notes (optional)</label>
|
|
<textarea
|
|
name="outcome-notes"
|
|
defaultValue=""
|
|
rows={3}
|
|
placeholder="Add context for this outcome..."
|
|
className={cn(
|
|
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
|
|
'text-sm text-foreground placeholder:text-muted-foreground',
|
|
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
|
)}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-foreground">Next Steps / Follow-Up (optional)</label>
|
|
<textarea
|
|
name="next-steps"
|
|
defaultValue=""
|
|
rows={3}
|
|
placeholder="Actions to take after this session..."
|
|
className={cn(
|
|
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
|
|
'text-sm text-foreground placeholder:text-muted-foreground',
|
|
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
|
)}
|
|
/>
|
|
</div>
|
|
</form>
|
|
</Modal>
|
|
)
|
|
}
|