import type { RecyclePolicy } from "@/lib/types"; import type { FieldErrors } from "@/lib/schemas"; function NumberInput({ value, onChange, min, max, step, placeholder, error, }: { value: number | ""; onChange: (v: number | "") => void; min?: number; max?: number; step?: number | "any"; placeholder?: string; error?: boolean; }) { return ( onChange(e.target.value === "" ? "" : Number(e.target.value)) } className={`w-full rounded-md border bg-zinc-800 px-3 py-2 text-sm text-zinc-100 placeholder:text-zinc-600 focus:outline-none ${error ? "border-red-500 focus:border-red-400" : "border-zinc-700 focus:border-zinc-500"}`} /> ); } function Field({ label, hint, error, children, }: { label: string; hint?: string; error?: string; children: React.ReactNode; }) { return (
{children} {error ? (

{error}

) : hint ? (

{hint}

) : null}
); } interface RecyclePolicyEditorProps { policy: RecyclePolicy; errors: FieldErrors; onChange: (policy: RecyclePolicy) => void; } export function RecyclePolicyEditor({ policy, errors, onChange, }: RecyclePolicyEditorProps) { return (
onChange({ ...policy, cooldown_days: v === "" ? null : (v as number) }) } min={0} placeholder="7" /> onChange({ ...policy, cooldown_generations: v === "" ? null : (v as number) }) } min={0} placeholder="3" />
onChange({ ...policy, min_available_ratio: v === "" ? 0.1 : (v as number) }) } min={0} max={1} step={0.01} placeholder="0.1" error={!!errors["recycle_policy.min_available_ratio"]} />
); }