229 lines
7.2 KiB
TypeScript
229 lines
7.2 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { useRouter } from 'next/navigation'
|
|
import { Calendar, Clock, Target } from 'lucide-react'
|
|
|
|
import { Card, Button, showToast } from '@/components/ui'
|
|
|
|
interface TreatmentPlanFormProps {
|
|
workspaceId: string
|
|
initialData?: {
|
|
title: string
|
|
totalCycles: number
|
|
startDate: string
|
|
cycleType: string
|
|
cycleDays: number
|
|
}
|
|
}
|
|
|
|
const CYCLE_TYPES = [
|
|
{ value: 'WEEKLY', label: 'Weekly', days: 7 },
|
|
{ value: 'BIWEEKLY', label: 'Every 2 weeks', days: 14 },
|
|
{ value: 'MONTHLY', label: 'Monthly', days: 30 },
|
|
{ value: 'CUSTOM', label: 'Custom', days: 0 },
|
|
]
|
|
|
|
export function TreatmentPlanForm({ workspaceId, initialData }: TreatmentPlanFormProps) {
|
|
const router = useRouter()
|
|
const [title, setTitle] = useState(initialData?.title || '')
|
|
const [totalCycles, setTotalCycles] = useState(initialData?.totalCycles || 12)
|
|
const [startDate, setStartDate] = useState(initialData?.startDate || '')
|
|
const [cycleType, setCycleType] = useState(initialData?.cycleType || 'WEEKLY')
|
|
const [customDays, setCustomDays] = useState(initialData?.cycleDays || 14)
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
const selectedCycleType = CYCLE_TYPES.find(t => t.value === cycleType)
|
|
const cycleDays = cycleType === 'CUSTOM' ? customDays : (selectedCycleType?.days || 7)
|
|
|
|
// Calculate estimated end date
|
|
const estimatedEndDate = startDate
|
|
? new Date(new Date(startDate).getTime() + (cycleDays * totalCycles * 24 * 60 * 60 * 1000))
|
|
: null
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
|
|
if (!title.trim()) {
|
|
showToast('Please enter a treatment name', 'error')
|
|
return
|
|
}
|
|
|
|
if (!startDate) {
|
|
showToast('Please select a start date', 'error')
|
|
return
|
|
}
|
|
|
|
setSaving(true)
|
|
try {
|
|
const response = await fetch(`/api/workspaces/${workspaceId}/treatment-plan`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
title: title.trim(),
|
|
totalCycles,
|
|
startDate,
|
|
cycleType,
|
|
cycleDays,
|
|
}),
|
|
})
|
|
|
|
if (!response.ok) throw new Error('Failed to create plan')
|
|
|
|
showToast('Treatment plan created!', 'success')
|
|
router.push('/treatment')
|
|
} catch {
|
|
showToast('Failed to create plan', 'error')
|
|
} finally {
|
|
setSaving(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Card>
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
{/* Treatment Name */}
|
|
<div>
|
|
<label className="block text-sm font-medium text-secondary-700 mb-2">
|
|
Treatment Name
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={title}
|
|
onChange={(e) => setTitle(e.target.value)}
|
|
placeholder="e.g., Grace's Chemotherapy Plan"
|
|
className="w-full px-3 py-2 border border-border rounded-lg text-base focus:outline-none focus:ring-2 focus:ring-primary-200 focus:border-primary-500"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
{/* Total Cycles */}
|
|
<div>
|
|
<label className="block text-sm font-medium text-secondary-700 mb-2">
|
|
<Target className="w-4 h-4 inline mr-1" />
|
|
Total Cycles/Sessions
|
|
</label>
|
|
<div className="flex items-center gap-4">
|
|
<input
|
|
type="range"
|
|
min="1"
|
|
max="50"
|
|
value={totalCycles}
|
|
onChange={(e) => setTotalCycles(parseInt(e.target.value))}
|
|
className="flex-1"
|
|
/>
|
|
<input
|
|
type="number"
|
|
min="1"
|
|
max="50"
|
|
value={totalCycles}
|
|
onChange={(e) => setTotalCycles(parseInt(e.target.value) || 1)}
|
|
className="w-20 px-3 py-2 border border-border rounded-lg text-center"
|
|
/>
|
|
</div>
|
|
<p className="text-xs text-secondary-500 mt-1">
|
|
Number of chemo sessions or treatment cycles
|
|
</p>
|
|
</div>
|
|
|
|
{/* Start Date */}
|
|
<div>
|
|
<label className="block text-sm font-medium text-secondary-700 mb-2">
|
|
<Calendar className="w-4 h-4 inline mr-1" />
|
|
Start Date
|
|
</label>
|
|
<input
|
|
type="date"
|
|
value={startDate}
|
|
onChange={(e) => setStartDate(e.target.value)}
|
|
className="w-full px-3 py-2 border border-border rounded-lg text-base focus:outline-none focus:ring-2 focus:ring-primary-200 focus:border-primary-500"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
{/* Cycle Frequency */}
|
|
<div>
|
|
<label className="block text-sm font-medium text-secondary-700 mb-2">
|
|
<Clock className="w-4 h-4 inline mr-1" />
|
|
How Often?
|
|
</label>
|
|
<div className="grid grid-cols-2 gap-2">
|
|
{CYCLE_TYPES.map((type) => (
|
|
<button
|
|
key={type.value}
|
|
type="button"
|
|
onClick={() => setCycleType(type.value)}
|
|
className={`p-3 rounded-lg border-2 text-left transition-all ${
|
|
cycleType === type.value
|
|
? 'border-primary-500 bg-primary-50'
|
|
: 'border-border hover:border-secondary-300'
|
|
}`}
|
|
>
|
|
<span className="font-medium text-secondary-900">{type.label}</span>
|
|
{type.value !== 'CUSTOM' && (
|
|
<span className="text-xs text-secondary-500 block">
|
|
Every {type.days} days
|
|
</span>
|
|
)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Custom Days Input */}
|
|
{cycleType === 'CUSTOM' && (
|
|
<div>
|
|
<label className="block text-sm font-medium text-secondary-700 mb-2">
|
|
Days Between Cycles
|
|
</label>
|
|
<input
|
|
type="number"
|
|
min="1"
|
|
max="90"
|
|
value={customDays}
|
|
onChange={(e) => setCustomDays(parseInt(e.target.value) || 1)}
|
|
className="w-full px-3 py-2 border border-border rounded-lg text-base focus:outline-none focus:ring-2 focus:ring-primary-200 focus:border-primary-500"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Estimated End Date */}
|
|
{estimatedEndDate && (
|
|
<div className="p-4 bg-primary-50 rounded-lg">
|
|
<p className="text-sm text-secondary-600">
|
|
Estimated completion:
|
|
</p>
|
|
<p className="font-semibold text-primary-700">
|
|
{estimatedEndDate.toLocaleDateString('en-US', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})}
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Submit */}
|
|
<div className="flex gap-3">
|
|
<Button
|
|
type="button"
|
|
variant="secondary"
|
|
href="/treatment"
|
|
fullWidth
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
loading={saving}
|
|
fullWidth
|
|
>
|
|
Create Plan
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</Card>
|
|
)
|
|
}
|