16 - Update frontend for cents-based API

This commit is contained in:
myrmidex 2026-03-21 17:48:58 +01:00
parent d6f60ab987
commit cf89ee7cd2
2 changed files with 32 additions and 16 deletions

View file

@ -54,7 +54,7 @@ export default function IncomeDistributionPreview({ scenarioId }: IncomeDistribu
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken(),
},
body: JSON.stringify({ amount: parsed }),
body: JSON.stringify({ amount: Math.round(parsed * 100) }),
});
if (!response.ok) {
@ -126,11 +126,11 @@ export default function IncomeDistributionPreview({ scenarioId }: IncomeDistribu
</div>
<div className="text-right">
<span className="font-semibold text-gray-900">
${allocation.allocated_amount.toFixed(2)}
${(allocation.allocated_amount / 100).toFixed(2)}
</span>
{allocation.remaining_capacity !== null && (
<span className="ml-2 text-sm text-gray-500">
(${allocation.remaining_capacity.toFixed(2)} remaining)
(${(allocation.remaining_capacity / 100).toFixed(2)} remaining)
</span>
)}
</div>
@ -143,13 +143,13 @@ export default function IncomeDistributionPreview({ scenarioId }: IncomeDistribu
<div className="flex justify-between rounded-md bg-gray-50 px-4 py-3 text-sm font-medium">
<span className="text-gray-700">Total Allocated</span>
<span className="text-gray-900">${preview.total_allocated.toFixed(2)}</span>
<span className="text-gray-900">${(preview.total_allocated / 100).toFixed(2)}</span>
</div>
{preview.unallocated > 0 && (
<div className="flex justify-between rounded-md bg-amber-50 px-4 py-3 text-sm font-medium">
<span className="text-amber-700">Unallocated</span>
<span className="text-amber-900">${preview.unallocated.toFixed(2)}</span>
<span className="text-amber-900">${(preview.unallocated / 100).toFixed(2)}</span>
</div>
)}
</div>

View file

@ -24,11 +24,11 @@ interface Bucket {
allocation_value: number | null;
allocation_type_label: string;
buffer_multiplier: number;
effective_capacity: number;
effective_capacity: number | null;
starting_amount: number;
current_balance: number;
has_available_space: boolean;
available_space: number;
available_space: number | null;
}
interface Stream {
@ -76,11 +76,23 @@ const bucketTypeOptions = [
{ value: 'want', label: 'Want' },
];
/** Convert cents to dollars for display */
const centsToDollars = (cents: number): number => cents / 100;
/** Convert dollars to cents for storage */
const dollarsToCents = (dollars: number): number => Math.round(dollars * 100);
/** Convert basis points to percent for display */
const basisPointsToPercent = (bp: number): number => bp / 100;
/** Convert percent to basis points for storage */
const percentToBasisPoints = (pct: number): number => Math.round(pct * 100);
const formatAllocationValue = (bucket: Bucket): string => {
if (bucket.allocation_type === 'unlimited') return 'All remaining';
if (bucket.allocation_value === null) return '--';
if (bucket.allocation_type === 'percentage') return `${Number(bucket.allocation_value).toFixed(2)}%`;
return `$${Number(bucket.allocation_value).toFixed(2)}`;
if (bucket.allocation_type === 'percentage') return `${basisPointsToPercent(bucket.allocation_value).toFixed(2)}%`;
return `$${centsToDollars(bucket.allocation_value).toFixed(2)}`;
};
const csrfToken = () =>
@ -158,7 +170,11 @@ export default function Show({ scenario, buckets, streams = { data: [] }, stream
name: formData.name,
type: formData.type,
allocation_type: formData.allocation_type,
allocation_value: formData.allocation_value ? parseFloat(formData.allocation_value) : null,
allocation_value: formData.allocation_value
? (formData.allocation_type === 'percentage'
? percentToBasisPoints(parseFloat(formData.allocation_value))
: dollarsToCents(parseFloat(formData.allocation_value)))
: null,
buffer_multiplier: formData.allocation_type === 'fixed_limit' ? parseFloat(formData.buffer_multiplier) || 0 : 0,
}),
});
@ -306,11 +322,11 @@ export default function Show({ scenario, buckets, streams = { data: [] }, stream
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600">Current Filling</span>
<InlineEditInput
value={bucket.starting_amount}
onSave={(val) => patchBucket(bucket.id, { starting_amount: val })}
value={centsToDollars(bucket.starting_amount)}
onSave={(val) => patchBucket(bucket.id, { starting_amount: dollarsToCents(val) })}
formatDisplay={(v) => `$${v.toFixed(2)}`}
min={0}
step="1"
step="0.01"
className="text-lg font-semibold text-gray-900"
/>
</div>
@ -329,8 +345,8 @@ export default function Show({ scenario, buckets, streams = { data: [] }, stream
min={0}
step="0.01"
/>
{bucket.buffer_multiplier > 0 && (
<> = ${bucket.effective_capacity.toFixed(2)} effective</>
{bucket.buffer_multiplier > 0 && bucket.effective_capacity !== null && (
<> = ${centsToDollars(bucket.effective_capacity).toFixed(2)} effective</>
)}
)
</span>
@ -342,7 +358,7 @@ export default function Show({ scenario, buckets, streams = { data: [] }, stream
<div className="flex justify-between text-sm">
<span>Progress</span>
<span>
${bucket.current_balance.toFixed(2)} / ${bucket.effective_capacity.toFixed(2)}
${centsToDollars(bucket.current_balance).toFixed(2)} / ${bucket.effective_capacity !== null ? centsToDollars(bucket.effective_capacity).toFixed(2) : '∞'}
</span>
</div>
<div className="mt-1 h-2 bg-gray-200 rounded-full">