import { useState } from 'react'; import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; interface Allocation { bucket_id: string; bucket_name: string; bucket_type: 'need' | 'want' | 'overflow'; allocated_amount: number; remaining_capacity: number | null; } interface PreviewResult { allocations: Allocation[]; total_allocated: number; unallocated: number; } interface IncomeDistributionPreviewProps { scenarioId: string; } const bucketTypeColor = { need: 'bg-blue-100 text-blue-800', want: 'bg-green-100 text-green-800', overflow: 'bg-amber-100 text-amber-800', } as const; const csrfToken = () => document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || ''; export default function IncomeDistributionPreview({ scenarioId }: IncomeDistributionPreviewProps) { const [amount, setAmount] = useState(''); const [preview, setPreview] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setIsLoading(true); setError(null); const parsed = parseFloat(amount); if (isNaN(parsed) || parsed <= 0) { setError('Please enter a valid amount'); setIsLoading(false); return; } try { const response = await fetch(`/scenarios/${scenarioId}/projections/preview`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken(), }, body: JSON.stringify({ amount: parsed }), }); if (!response.ok) { let message = 'Failed to preview allocation'; try { const data = await response.json(); message = data.message || message; } catch { /* non-JSON error body */ } setError(message); setPreview(null); return; } const data: PreviewResult = await response.json(); setPreview(data); } catch { setError('Failed to connect to server'); setPreview(null); } finally { setIsLoading(false); } }; return ( Income Distribution Preview
setAmount(e.target.value)} className="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-gray-900" placeholder="e.g., 3000" step="0.01" min="0.01" required />
{error && (
{error}
)} {preview && (
{preview.allocations.length > 0 ? (
{preview.allocations.map((allocation) => (
{allocation.bucket_type} {allocation.bucket_name}
${allocation.allocated_amount.toFixed(2)} {allocation.remaining_capacity !== null && ( (${allocation.remaining_capacity.toFixed(2)} remaining) )}
))}
) : (

No buckets to allocate to.

)}
Total Allocated ${preview.total_allocated.toFixed(2)}
{preview.unallocated > 0 && (
Unallocated ${preview.unallocated.toFixed(2)}
)}
)}
); }