Files
resolutionflow/frontend/src/components/session/SessionOutcomeModal.tsx
Michael Chihlas 303a558432 refactor: replace hardcoded hex values with Tailwind semantic tokens
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>
2026-03-22 04:34:35 -04:00

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>
)
}