diff --git a/src/modules/estimateWizard/components/steps/RecommendationStep.jsx b/src/modules/estimateWizard/components/steps/RecommendationStep.jsx index 0778b93..28f12ef 100644 --- a/src/modules/estimateWizard/components/steps/RecommendationStep.jsx +++ b/src/modules/estimateWizard/components/steps/RecommendationStep.jsx @@ -9,16 +9,47 @@ * The recommendation record is frozen into the session (spec §12 recommendations). */ import React, { useEffect, useMemo, useState } from 'react'; -import { Sparkles, Loader2, Check, ShieldCheck } from 'lucide-react'; +import { Sparkles, Loader2, Check, ShieldCheck, Info } from 'lucide-react'; import { useEstimateWizard } from '../../EstimateWizardContext'; import { score } from '../../engine/scoringEngine'; import { computeTenYearValue } from '../../engine/valueModel'; import { generateExplanation } from '../../ai/explain'; +import { formatUSD } from '../../engine/pricing'; import GoodBetterBestCards from '../GoodBetterBestCards'; import TenYearValueChart from '../TenYearValueChart'; const PKG_LABEL = { GOOD: 'Good', BETTER: 'Better', BEST: 'Best' }; +/** + * Deterministic, manufacturer-neutral description for a package the customer + * clicked that is NOT the recommended highlight. Built purely from the tenant + * config package fields (+ the value model for the savings line) — no AI call, + * so it's instant and always on-message. Mirrors the guardrailed tone of the + * recommendation copy ("illustrative", "confirm with your carrier"). + */ +function describePackage(pkg, valueModel) { + const label = PKG_LABEL[pkg.type]; + const row = valueModel?.rows?.find((r) => r.type === pkg.type); + + const paragraph = + `The ${label} option — ${pkg.name} — is our "${(pkg.position || '').toLowerCase()}" choice` + + `${pkg.fit ? `. It tends to fit best when: ${pkg.fit}` : '.'}`; + + const bullets = []; + if (pkg.warrantyLabel) bullets.push(pkg.warrantyLabel); + if (pkg.serviceLifeYears) bullets.push(`~${pkg.serviceLifeYears}-year expected service life`); + if (pkg.hailResistance) bullets.push(`${pkg.hailResistance} hail resistance`); + if (valueModel?.premiumKnown && row?.savings) { + bullets.push( + `Illustrative 10-yr insurance savings ${formatUSD(row.savings.low)}–${formatUSD(row.savings.high)} — confirm with your carrier` + ); + } else if (pkg.insuranceDiscountPct > 0) { + bullets.push('May qualify for an insurance discount — confirm with your carrier'); + } + + return { paragraph, bullets }; +} + const RecommendationStep = () => { const { inputs, session, patchSession, tenantConfig, logEvent } = useEstimateWizard(); @@ -69,50 +100,84 @@ const RecommendationStep = () => { }, []); const highlight = recommendation.selectedPackage; + const selectedType = session.selectedPackage || highlight; + const isRecommendedSelected = selectedType === highlight; + const selectedPkg = tenantConfig.packages.find((p) => p.type === selectedType); + const selectedDesc = !isRecommendedSelected && selectedPkg ? describePackage(selectedPkg, value) : null; return (
+ {explanation?.customerExplanation} +
+- {explanation?.customerExplanation} -
-+ {selectedDesc.paragraph} +
++ We still suggest {PKG_LABEL[highlight]} based on your answers — but every option stays available. +
+