buckets/resources/js/components/SettingsPanel.tsx

124 lines
4.6 KiB
TypeScript

import { csrfToken } from '@/lib/utils';
import { type SharedData } from '@/types';
import { router, usePage } from '@inertiajs/react';
import { useState } from 'react';
type SaveStatus = 'idle' | 'saving' | 'success' | 'error';
type DistributionMode = 'even' | 'priority';
interface DistributionOption {
value: DistributionMode;
label: string;
description: string;
}
const distributionOptions: DistributionOption[] = [
{
value: 'even',
label: 'EVEN SPLIT',
description:
'Split evenly across buckets in each phase, respecting individual capacity',
},
{
value: 'priority',
label: 'PRIORITY ORDER',
description: 'Fill highest-priority bucket first, then next',
},
];
interface SettingsPanelProps {
onOpenChange: (open: boolean) => void;
}
export default function SettingsPanel({
onOpenChange,
}: SettingsPanelProps) {
const { scenario } = usePage<SharedData>().props;
const [saveStatus, setSaveStatus] = useState<SaveStatus>('idle');
if (!scenario) return null;
const handleDistributionModeChange = async (value: DistributionMode) => {
if (value === scenario.distribution_mode) return;
setSaveStatus('saving');
try {
const response = await fetch(`/scenarios/${scenario.id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken(),
},
body: JSON.stringify({ distribution_mode: value }),
});
if (!response.ok) throw new Error('Failed to update');
setSaveStatus('success');
router.reload({ only: ['scenario', 'buckets'] });
setTimeout(() => setSaveStatus('idle'), 1500);
} catch {
setSaveStatus('error');
setTimeout(() => setSaveStatus('idle'), 1500);
}
};
return (
<div className="mb-8 border-4 border-red-500 bg-black p-6 glow-red">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-mono font-bold tracking-wider uppercase text-red-500">
SETTINGS
</h2>
<button
onClick={() => onOpenChange(false)}
className="text-red-500/60 hover:text-red-500 font-mono text-sm transition-colors"
>
CLOSE
</button>
</div>
<fieldset disabled={saveStatus === 'saving'}>
<legend className="text-xs font-mono uppercase text-red-500/60">
DISTRIBUTION MODE
{saveStatus === 'success' && (
<span className="ml-2 text-green-500">SAVED</span>
)}
{saveStatus === 'error' && (
<span className="ml-2 text-red-500">FAILED</span>
)}
</legend>
<div className="mt-3 space-y-2">
{distributionOptions.map((option) => (
<label
key={option.value}
className={`flex cursor-pointer items-start gap-3 border-2 p-3 transition-colors ${
scenario.distribution_mode === option.value
? 'border-red-500 bg-red-500/10'
: 'border-red-500/30 hover:border-red-500/60'
}`}
>
<input
type="radio"
name="distribution_mode"
value={option.value}
checked={scenario.distribution_mode === option.value}
onChange={(e) =>
handleDistributionModeChange(e.target.value as DistributionMode)
}
className="mt-0.5 accent-red-500"
/>
<div>
<div className="text-sm font-mono font-bold text-red-500">
{option.label}
</div>
<div className="text-xs font-mono text-red-500/60">
{option.description}
</div>
</div>
</label>
))}
</div>
</fieldset>
</div>
);
}