5 - Add buffer multiplier UI with preset and custom options
This commit is contained in:
parent
ed6be6249e
commit
8f6de4aace
1 changed files with 70 additions and 8 deletions
|
|
@ -18,7 +18,8 @@ interface Bucket {
|
|||
allocation_type: string;
|
||||
allocation_value: number | null;
|
||||
allocation_type_label: string;
|
||||
formatted_allocation_value: string;
|
||||
buffer_multiplier: number;
|
||||
effective_capacity: number;
|
||||
current_balance: number;
|
||||
has_available_space: boolean;
|
||||
available_space: number;
|
||||
|
|
@ -64,11 +65,22 @@ const bucketTypeBorderColor = {
|
|||
overflow: 'border-amber-500',
|
||||
} as const;
|
||||
|
||||
const BUFFER_PRESETS = ['0', '0.5', '1', '1.5', '2'] as const;
|
||||
|
||||
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)}`;
|
||||
};
|
||||
|
||||
const defaultFormData = {
|
||||
name: '',
|
||||
type: 'need' as Bucket['type'],
|
||||
allocation_type: 'fixed_limit',
|
||||
allocation_value: ''
|
||||
allocation_value: '',
|
||||
buffer_multiplier: '0',
|
||||
buffer_mode: 'preset' as 'preset' | 'custom',
|
||||
};
|
||||
|
||||
export default function Show({ scenario, buckets, streams = { data: [] }, streamStats }: Props) {
|
||||
|
|
@ -79,11 +91,15 @@ export default function Show({ scenario, buckets, streams = { data: [] }, stream
|
|||
|
||||
const handleEdit = (bucket: Bucket) => {
|
||||
setEditingBucket(bucket);
|
||||
const bufferStr = bucket.buffer_multiplier.toString();
|
||||
const isPreset = (BUFFER_PRESETS as readonly string[]).includes(bufferStr);
|
||||
setFormData({
|
||||
name: bucket.name,
|
||||
type: bucket.type,
|
||||
allocation_type: bucket.allocation_type,
|
||||
allocation_value: bucket.allocation_value ? bucket.allocation_value.toString() : ''
|
||||
allocation_value: bucket.allocation_value ? bucket.allocation_value.toString() : '',
|
||||
buffer_multiplier: bufferStr,
|
||||
buffer_mode: isPreset ? 'preset' : 'custom',
|
||||
});
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
|
@ -133,6 +149,7 @@ export default function Show({ scenario, buckets, streams = { data: [] }, stream
|
|||
type: formData.type,
|
||||
allocation_type: formData.allocation_type,
|
||||
allocation_value: formData.allocation_value ? parseFloat(formData.allocation_value) : null,
|
||||
buffer_multiplier: formData.allocation_type === 'fixed_limit' ? parseFloat(formData.buffer_multiplier) || 0 : 0,
|
||||
priority: editingBucket ? editingBucket.priority : undefined
|
||||
}),
|
||||
});
|
||||
|
|
@ -174,6 +191,7 @@ export default function Show({ scenario, buckets, streams = { data: [] }, stream
|
|||
type: bucket.type,
|
||||
allocation_type: bucket.allocation_type,
|
||||
allocation_value: bucket.allocation_value,
|
||||
buffer_multiplier: bucket.buffer_multiplier,
|
||||
priority: newPriority
|
||||
}),
|
||||
});
|
||||
|
|
@ -283,23 +301,28 @@ export default function Show({ scenario, buckets, streams = { data: [] }, stream
|
|||
|
||||
<div className="mt-2">
|
||||
<span className="text-sm text-gray-600">
|
||||
Allocation: {bucket.formatted_allocation_value}
|
||||
Allocation: {formatAllocationValue(bucket)}
|
||||
</span>
|
||||
{bucket.allocation_type === 'fixed_limit' && bucket.buffer_multiplier > 0 && (
|
||||
<span className="text-sm text-gray-500 ml-2">
|
||||
({bucket.buffer_multiplier}x buffer = ${bucket.effective_capacity.toFixed(2)} effective)
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
{bucket.allocation_type === 'fixed_limit' && (
|
||||
<div className="mt-3">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>Progress</span>
|
||||
<span>
|
||||
${bucket.current_balance.toFixed(2)} / ${Number(bucket.allocation_value)?.toFixed(2)}
|
||||
${bucket.current_balance.toFixed(2)} / ${bucket.effective_capacity.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 h-2 bg-gray-200 rounded-full">
|
||||
<div
|
||||
<div
|
||||
className="h-2 bg-blue-600 rounded-full transition-all"
|
||||
style={{
|
||||
width: `${bucket.allocation_value ? Math.min((bucket.current_balance / Number(bucket.allocation_value)) * 100, 100) : 0}%`
|
||||
width: `${bucket.effective_capacity ? Math.min((bucket.current_balance / bucket.effective_capacity) * 100, 100) : 0}%`
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -564,6 +587,45 @@ export default function Show({ scenario, buckets, streams = { data: [] }, stream
|
|||
</div>
|
||||
)}
|
||||
|
||||
{formData.allocation_type === 'fixed_limit' && (
|
||||
<div>
|
||||
<label htmlFor="buffer_multiplier" className="block text-sm font-medium text-gray-700">
|
||||
Buffer
|
||||
</label>
|
||||
<select
|
||||
id="buffer_preset"
|
||||
value={formData.buffer_mode === 'custom' ? 'custom' : formData.buffer_multiplier}
|
||||
onChange={(e) => {
|
||||
if (e.target.value === 'custom') {
|
||||
setFormData({ ...formData, buffer_mode: 'custom' });
|
||||
} else {
|
||||
setFormData({ ...formData, buffer_multiplier: e.target.value, buffer_mode: 'preset' });
|
||||
}
|
||||
}}
|
||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-gray-900"
|
||||
>
|
||||
<option value="0">None</option>
|
||||
<option value="0.5">0.5x (50% extra)</option>
|
||||
<option value="1">1x (100% extra)</option>
|
||||
<option value="1.5">1.5x (150% extra)</option>
|
||||
<option value="2">2x (200% extra)</option>
|
||||
<option value="custom">Custom</option>
|
||||
</select>
|
||||
{formData.buffer_mode === 'custom' && (
|
||||
<input
|
||||
type="number"
|
||||
id="buffer_multiplier"
|
||||
value={formData.buffer_multiplier}
|
||||
onChange={(e) => setFormData({ ...formData, buffer_multiplier: e.target.value })}
|
||||
className="mt-2 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-gray-900"
|
||||
placeholder="e.g., 0.75"
|
||||
step="0.01"
|
||||
min="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-3 pt-4">
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
|||
Loading…
Reference in a new issue