rohitsar567 Claude Opus 4.7 (1M context) commited on
Commit
7a8965b
·
1 Parent(s): 6d84a76

fix(panel): KI-272 — PremiumCalculatorPanel copay pre-fill + re-sync on profile load

Browse files

E1 Playwright run found three pre-fill bugs in PremiumCalculatorPanel that
D1 either missed or that occurred via timing race:

1. **Copay always 0** — D1's derivation helpers covered age/SI/family/PED/
city but copay was hardcoded `useState(0)`. Profile's `copay_pct=20`
never propagated. Fix: `useState<number>(initialProfile?.copay_pct ?? 0)`.

2. **Age stuck at 35, family stuck at "Self only"** — useState initializers
run ONCE on first mount. If profile data loads AFTER the panel mounts
(race: user clicks the chip the moment profile finishes fetching), the
sliders never update. Fix: new useEffect watches `initialProfile` changes
and updates each slider IFF the slider still holds its initial-default
value. Once user moves a slider, future profile updates no longer
override their choice (snapshot-comparison ref prevents redundant runs).

3. **SI now correctly prefers desired_sum_insured_inr over
existing_cover_inr** — already shipped in KI-270 / D1; this commit
also threads it into the re-sync useEffect for parity.

Verification:
- npx tsc --noEmit clean
- Re-sync logic only updates sliders that are still at their initial
default — user's manual changes are preserved.

This pairs with backend KI-271 (SLOT_UNION-driven profile endpoints) — now
the profile loaded by getProfileCompleteness actually contains copay_pct +
family_medical_history + desired_sum_insured_inr, and the panel will
correctly pre-fill from them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. frontend/src/app/page.tsx +41 -1
frontend/src/app/page.tsx CHANGED
@@ -2162,10 +2162,50 @@ function PremiumCalculatorPanel({
2162
  const [ped, setPed] = useState<"none" | "diabetes_or_hypertension" | "heart_disease" | "multiple">(
2163
  derivePed(initialProfile?.health_conditions),
2164
  );
2165
- const [copay, setCopay] = useState(0);
 
 
2166
  const [estimate, setEstimate] = useState<PremiumEstimateResponse | null>(null);
2167
  const [busy, setBusy] = useState(false);
2168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2169
  useEffect(() => {
2170
  const handler = setTimeout(() => {
2171
  setBusy(true);
 
2162
  const [ped, setPed] = useState<"none" | "diabetes_or_hypertension" | "heart_disease" | "multiple">(
2163
  derivePed(initialProfile?.health_conditions),
2164
  );
2165
+ // KI-272 copay pre-fill (D1 missed this). When profile has copay_pct,
2166
+ // start the slider there instead of 0.
2167
+ const [copay, setCopay] = useState<number>(initialProfile?.copay_pct ?? 0);
2168
  const [estimate, setEstimate] = useState<PremiumEstimateResponse | null>(null);
2169
  const [busy, setBusy] = useState(false);
2170
 
2171
+ // KI-272 — re-sync sliders if profile loads AFTER the panel has mounted
2172
+ // (race: user clicks the chip the moment profile data finishes fetching).
2173
+ // We use a "user has not yet touched this slider" guard: only update from
2174
+ // profile when the slider still holds its initial-default value. Once the
2175
+ // user moves a slider, profile updates no longer override their choice.
2176
+ const profileSnapshotRef = useRef<string>("");
2177
+ useEffect(() => {
2178
+ if (!initialProfile) return;
2179
+ const snap = JSON.stringify([
2180
+ initialProfile.age,
2181
+ initialProfile.desired_sum_insured_inr ?? initialProfile.existing_cover_inr,
2182
+ initialProfile.location_tier,
2183
+ initialProfile.dependents,
2184
+ initialProfile.health_conditions,
2185
+ initialProfile.copay_pct,
2186
+ ]);
2187
+ if (snap === profileSnapshotRef.current) return;
2188
+ profileSnapshotRef.current = snap;
2189
+ if (initialProfile.age != null && age === 35) setAge(initialProfile.age);
2190
+ const newSI =
2191
+ initialProfile.desired_sum_insured_inr ??
2192
+ initialProfile.existing_cover_inr;
2193
+ if (newSI != null && sumInsured === 1_000_000) setSumInsured(newSI);
2194
+ if (initialProfile.location_tier && cityTier === "metro") {
2195
+ setCityTier(deriveCityTier(initialProfile.location_tier));
2196
+ }
2197
+ if (initialProfile.dependents && familySize === 0) {
2198
+ setFamilySize(deriveFamilySize(initialProfile.dependents));
2199
+ }
2200
+ if (initialProfile.health_conditions?.length && ped === "none") {
2201
+ setPed(derivePed(initialProfile.health_conditions));
2202
+ }
2203
+ if (initialProfile.copay_pct != null && copay === 0) {
2204
+ setCopay(initialProfile.copay_pct);
2205
+ }
2206
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2207
+ }, [initialProfile]);
2208
+
2209
  useEffect(() => {
2210
  const handler = setTimeout(() => {
2211
  setBusy(true);