Fixed a few bugs with input fields

This commit is contained in:
Prashant Sridharan committed 2026-01-29 15:07:41 +00:00
1 parent 66b88862d3
commit 778ac4b9ff
3 files changed
+88 -60

No files matched your search

@@ -31,8 +31,10 @@ export default function Stage1ProjectBasics({
type="number"
size="small"
layout="vertical"
value={String(inputs.projects)}
onChange={(e: any) => set({ projects: Math.max(1, Number(e.target.value || 1)) })}
min={1}
value={inputs.projects}
onChange={(e: any) => set({ projects: Number(e.target.value) || 0 })}
onBlur={() => set({ projects: Math.max(1, inputs.projects) })}
/>
<p className="text-foreground-lighter text-xs">Most users need 1–3 (dev/staging/prod).</p>
</Panel>
@@ -63,8 +65,10 @@ export default function Stage1ProjectBasics({
type="number"
size="small"
layout="vertical"
value={String(inputs.teamSize)}
onChange={(e: any) => set({ teamSize: Math.max(1, Number(e.target.value || 1)) })}
min={1}
value={inputs.teamSize}
onChange={(e: any) => set({ teamSize: Number(e.target.value) || 0 })}
onBlur={() => set({ teamSize: Math.max(1, inputs.teamSize) })}
/>
<p className="text-foreground-lighter text-xs">Developers working on backend/infrastructure.</p>
</Panel>
@@ -75,8 +79,10 @@ export default function Stage1ProjectBasics({
type="number"
size="small"
layout="vertical"
value={String(inputs.hourlyCostUsd)}
onChange={(e: any) => set({ hourlyCostUsd: Math.max(0, Number(e.target.value || 0)) })}
min={0}
value={inputs.hourlyCostUsd}
onChange={(e: any) => set({ hourlyCostUsd: Number(e.target.value) || 0 })}
onBlur={() => set({ hourlyCostUsd: Math.max(0, inputs.hourlyCostUsd) })}
/>
<p className="text-foreground-lighter text-xs">Default $150/hour (US). Used for time savings.</p>
</Panel>
@@ -159,10 +159,10 @@ export default function Stage2UsageEstimation({
type="number"
size="small"
layout="vertical"
value={String(inputs.databaseSizeGb)}
onChange={(e: any) =>
set({ databaseSizeGb: clamp(Number(e.target.value || 0.5), 0.5, 10_000) })
}
min={0.5}
value={inputs.databaseSizeGb}
onChange={(e: any) => set({ databaseSizeGb: Number(e.target.value) || 0 })}
onBlur={() => set({ databaseSizeGb: clamp(inputs.databaseSizeGb, 0.5, 10_000) })}
className="max-w-[120px]"
/>
</div>
@@ -292,11 +292,11 @@ export default function Stage2UsageEstimation({
type="number"
size="small"
layout="vertical"
value={String(inputs.readReplicas)}
onChange={(e: any) =>
set({
readReplicas: clamp(Math.round(Number(e.target.value || 0)), 0, 10),
})
min={0}
value={inputs.readReplicas}
onChange={(e: any) => set({ readReplicas: Number(e.target.value) || 0 })}
onBlur={() =>
set({ readReplicas: clamp(Math.round(inputs.readReplicas), 0, 10) })
}
className="w-20 shrink-0"
/>
@@ -317,11 +317,15 @@ export default function Stage2UsageEstimation({
type="number"
size="small"
layout="vertical"
value={String(inputs.branchingHoursPerMonth)}
min={0}
value={inputs.branchingHoursPerMonth}
onChange={(e: any) =>
set({ branchingHoursPerMonth: Number(e.target.value) || 0 })
}
onBlur={() =>
set({
branchingHoursPerMonth: clamp(
Math.round(Number(e.target.value || 0)),
Math.round(inputs.branchingHoursPerMonth),
0,
10000
),
@@ -391,10 +395,10 @@ export default function Stage2UsageEstimation({
type="number"
size="small"
layout="vertical"
value={String(inputs.storageSizeGb)}
onChange={(e: any) =>
set({ storageSizeGb: clamp(Number(e.target.value || 0), 0, 100_000) })
}
min={0}
value={inputs.storageSizeGb}
onChange={(e: any) => set({ storageSizeGb: Number(e.target.value) || 0 })}
onBlur={() => set({ storageSizeGb: clamp(inputs.storageSizeGb, 0, 100_000) })}
className="max-w-[120px]"
/>
</div>
@@ -435,10 +439,10 @@ export default function Stage2UsageEstimation({
type="number"
size="small"
layout="vertical"
value={String(inputs.mau)}
onChange={(e: any) =>
set({ mau: clamp(Math.round(Number(e.target.value || 0)), 0, 50_000_000) })
}
min={0}
value={inputs.mau}
onChange={(e: any) => set({ mau: Number(e.target.value) || 0 })}
onBlur={() => set({ mau: clamp(Math.round(inputs.mau), 0, 50_000_000) })}
className="max-w-[140px]"
/>
</div>
@@ -539,11 +543,15 @@ export default function Stage2UsageEstimation({
type="number"
size="small"
layout="vertical"
value={String(inputs.realtimePeakConnections)}
min={0}
value={inputs.realtimePeakConnections}
onChange={(e: any) =>
set({ realtimePeakConnections: Number(e.target.value) || 0 })
}
onBlur={() =>
set({
realtimePeakConnections: clamp(
Math.round(Number(e.target.value || 0)),
Math.round(inputs.realtimePeakConnections),
0,
10_000_000
),
@@ -573,11 +581,13 @@ export default function Stage2UsageEstimation({
type="number"
size="small"
layout="vertical"
value={String(inputs.realtimeMessages)}
onChange={(e: any) =>
min={0}
value={inputs.realtimeMessages}
onChange={(e: any) => set({ realtimeMessages: Number(e.target.value) || 0 })}
onBlur={() =>
set({
realtimeMessages: clamp(
Math.round(Number(e.target.value || 0)),
Math.round(inputs.realtimeMessages),
0,
10_000_000_000
),
@@ -625,11 +635,13 @@ export default function Stage2UsageEstimation({
type="number"
size="small"
layout="vertical"
value={String(inputs.edgeInvocations)}
onChange={(e: any) =>
min={0}
value={inputs.edgeInvocations}
onChange={(e: any) => set({ edgeInvocations: Number(e.target.value) || 0 })}
onBlur={() =>
set({
edgeInvocations: clamp(
Math.round(Number(e.target.value || 0)),
Math.round(inputs.edgeInvocations),
0,
10_000_000_000
),
@@ -678,10 +690,10 @@ export default function Stage2UsageEstimation({
type="number"
size="small"
layout="vertical"
value={String(inputs.egressGb)}
onChange={(e: any) =>
set({ egressGb: clamp(Number(e.target.value || 0), 0, 100_000) })
}
min={0}
value={inputs.egressGb}
onChange={(e: any) => set({ egressGb: Number(e.target.value) || 0 })}
onBlur={() => set({ egressGb: clamp(inputs.egressGb, 0, 100_000) })}
className="max-w-[120px]"
/>
</div>
@@ -772,11 +784,11 @@ export default function Stage2UsageEstimation({
type="number"
size="small"
layout="vertical"
value={String(inputs.logDrains)}
onChange={(e: any) =>
set({
logDrains: clamp(Math.round(Number(e.target.value || 1)), 1, 10),
})
min={1}
value={inputs.logDrains}
onChange={(e: any) => set({ logDrains: Number(e.target.value) || 0 })}
onBlur={() =>
set({ logDrains: clamp(Math.round(inputs.logDrains), 1, 10) })
}
className="max-w-[60px]"
/>
@@ -63,24 +63,34 @@ export default function Stage4TimeAllocation({
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{(Object.keys(FIELD_LABELS) as TimeAllocationKey[]).map((key) => (
<Panel key={key} outerClassName="w-full" innerClassName="p-4 md:p-5 flex flex-col gap-2">
<label className="text-foreground text-sm">{FIELD_LABELS[key]}</label>
<Input
type="number"
size="small"
layout="vertical"
value={merged[key] == null ? '' : String(merged[key])}
placeholder="Use default"
onChange={(e: any) => {
const raw = e.target.value
if (raw === '') return clearOverride(key)
return setOverride(key, Math.max(0, Number(raw)))
}}
/>
<p className="text-foreground-lighter text-xs">Optional override. Leave blank to use smart defaults.</p>
</Panel>
))}
{(Object.keys(FIELD_CONFIG) as TimeAllocationKey[]).map((key) => {
const config = FIELD_CONFIG[key]
return (
<Panel key={key} outerClassName="w-full" innerClassName="p-4 md:p-5 flex flex-col gap-2">
<label className="text-foreground text-sm">{config.label}</label>
<Input
type="number"
size="small"
layout="vertical"
min={0}
value={merged[key] == null ? '' : merged[key]}
placeholder="Use default"
onChange={(e: any) => {
const raw = e.target.value
if (raw === '') return clearOverride(key)
return setOverride(key, Number(raw) || 0)
}}
onBlur={() => {
if (merged[key] != null) {
setOverride(key, Math.max(0, merged[key]))
}
}}
/>
<p className="text-foreground-lighter text-xs">{config.hint}</p>
<p className="text-foreground-lighter text-xs italic">{config.examples}</p>
</Panel>
)
})}
</div>
)
}