19 - Add save distribution button to income preview component

This commit is contained in:
myrmidex 2026-03-22 02:18:00 +01:00
parent f4e5a186fa
commit 3e894503fe

View file

@ -1,4 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import { router } from '@inertiajs/react';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@ -33,12 +34,15 @@ export default function IncomeDistributionPreview({ scenarioId }: IncomeDistribu
const [amount, setAmount] = useState(''); const [amount, setAmount] = useState('');
const [preview, setPreview] = useState<PreviewResult | null>(null); const [preview, setPreview] = useState<PreviewResult | null>(null);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isApplying, setIsApplying] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [applied, setApplied] = useState(false);
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
setIsLoading(true); setIsLoading(true);
setError(null); setError(null);
setApplied(false);
const parsed = parseFloat(amount); const parsed = parseFloat(amount);
if (isNaN(parsed) || parsed <= 0) { if (isNaN(parsed) || parsed <= 0) {
@ -78,6 +82,44 @@ export default function IncomeDistributionPreview({ scenarioId }: IncomeDistribu
} }
}; };
const handleApply = async () => {
const parsed = parseFloat(amount);
if (isNaN(parsed) || parsed <= 0) return;
setIsApplying(true);
setError(null);
try {
const response = await fetch(`/scenarios/${scenarioId}/projections/apply`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken(),
},
body: JSON.stringify({ amount: Math.round(parsed * 100) }),
});
if (!response.ok) {
let message = 'Failed to save distribution';
try {
const data = await response.json();
message = data.message || message;
} catch { /* non-JSON error body */ }
setError(message);
return;
}
setPreview(null);
setAmount('');
setApplied(true);
router.reload({ only: ['buckets'] });
} catch {
setError('Failed to connect to server');
} finally {
setIsApplying(false);
}
};
return ( return (
<Card> <Card>
<CardHeader> <CardHeader>
@ -101,7 +143,7 @@ export default function IncomeDistributionPreview({ scenarioId }: IncomeDistribu
required required
/> />
</div> </div>
<Button type="submit" disabled={isLoading}> <Button type="submit" disabled={isLoading || isApplying}>
{isLoading ? 'Loading...' : 'Preview Distribution'} {isLoading ? 'Loading...' : 'Preview Distribution'}
</Button> </Button>
</form> </form>
@ -112,6 +154,12 @@ export default function IncomeDistributionPreview({ scenarioId }: IncomeDistribu
</div> </div>
)} )}
{applied && (
<div className="mt-4 rounded-md bg-green-50 p-3 text-sm text-green-700">
Distribution saved successfully. Bucket balances have been updated.
</div>
)}
{preview && ( {preview && (
<div className="mt-6 space-y-4"> <div className="mt-6 space-y-4">
{preview.allocations.length > 0 ? ( {preview.allocations.length > 0 ? (
@ -152,6 +200,16 @@ export default function IncomeDistributionPreview({ scenarioId }: IncomeDistribu
<span className="text-amber-900">${(preview.unallocated / 100).toFixed(2)}</span> <span className="text-amber-900">${(preview.unallocated / 100).toFixed(2)}</span>
</div> </div>
)} )}
{preview.allocations.length > 0 && (
<Button
onClick={handleApply}
disabled={isApplying}
className="w-full"
>
{isApplying ? 'Saving...' : 'Save Distribution'}
</Button>
)}
</div> </div>
)} )}
</CardContent> </CardContent>