From 778ac4b9ff4c99586aa366f0b5d2f21838fc758f Mon Sep 17 00:00:00 2001 From: Prashant Sridharan Date: Thu, 29 Jan 2026 15:07:41 +0000 Subject: [PATCH] Fixed a few bugs with input fields --- .../stages/Stage1ProjectBasics.tsx | 18 ++-- .../stages/Stage2UsageEstimation.tsx | 84 +++++++++++-------- .../stages/Stage4TimeAllocation.tsx | 46 ++++++---- 3 files changed, 88 insertions(+), 60 deletions(-) diff --git a/apps/www/components/PricingCalculator/stages/Stage1ProjectBasics.tsx b/apps/www/components/PricingCalculator/stages/Stage1ProjectBasics.tsx index 987f11de270..5c47a2f48fe 100644 --- a/apps/www/components/PricingCalculator/stages/Stage1ProjectBasics.tsx +++ b/apps/www/components/PricingCalculator/stages/Stage1ProjectBasics.tsx @@ -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) })} />

Most users need 1–3 (dev/staging/prod).

@@ -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) })} />

Developers working on backend/infrastructure.

@@ -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) })} />

Default $150/hour (US). Used for time savings.

diff --git a/apps/www/components/PricingCalculator/stages/Stage2UsageEstimation.tsx b/apps/www/components/PricingCalculator/stages/Stage2UsageEstimation.tsx index e2dae220981..8a6d8665262 100644 --- a/apps/www/components/PricingCalculator/stages/Stage2UsageEstimation.tsx +++ b/apps/www/components/PricingCalculator/stages/Stage2UsageEstimation.tsx @@ -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]" /> @@ -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]" /> @@ -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]" /> @@ -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]" /> @@ -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]" /> diff --git a/apps/www/components/PricingCalculator/stages/Stage4TimeAllocation.tsx b/apps/www/components/PricingCalculator/stages/Stage4TimeAllocation.tsx index 1a196df2608..0cc4114dcde 100644 --- a/apps/www/components/PricingCalculator/stages/Stage4TimeAllocation.tsx +++ b/apps/www/components/PricingCalculator/stages/Stage4TimeAllocation.tsx @@ -63,24 +63,34 @@ export default function Stage4TimeAllocation({ return (
- {(Object.keys(FIELD_LABELS) as TimeAllocationKey[]).map((key) => ( - - - { - const raw = e.target.value - if (raw === '') return clearOverride(key) - return setOverride(key, Math.max(0, Number(raw))) - }} - /> -

Optional override. Leave blank to use smart defaults.

-
- ))} + {(Object.keys(FIELD_CONFIG) as TimeAllocationKey[]).map((key) => { + const config = FIELD_CONFIG[key] + return ( + + + { + 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])) + } + }} + /> +

{config.hint}

+

{config.examples}

+
+ ) + })}
) }