estimate module setup and wizard ui

This commit is contained in:
Mayur Shinde
2026-06-09 14:47:25 +05:30
parent 944a745892
commit 716d096fc8
14 changed files with 1013 additions and 3 deletions
@@ -0,0 +1,188 @@
/**
* tenantConfig.js — Estimate Wizard tenant-configurable defaults.
*
* Spec §14 (Admin Controls) requires that pricing, discounts, warranty text,
* package scope, service-life, and scoring weights are NOT hard-coded — they are
* tenant-controlled. For the demo these defaults live here and can be overridden
* by the admin page (Phase 5). Nothing customer-facing should bypass this object.
*
* Manufacturer-neutral by design (spec §3, §15): packages are generic categories,
* internal SKU/vendor names are intentionally absent from customer-facing fields.
*/
// ── Good / Better / Best packages (spec §7) ──────────────────────────────────
export const DEFAULT_PACKAGES = [
{
type: 'GOOD',
name: 'Standard Architectural',
category: 'Architectural / laminate shingle system',
materialClass: 'standard',
warrantyLabel: '30-year material warranty category',
position: 'Lowest upfront investment',
fit: 'Shorter ownership horizon, limited budget, lower hail exposure, or preparing to sell soon.',
// price rule: per-square installed price (demo). Real pricing engine lives in admin.
pricePerSquare: 420,
serviceLifeYears: 28, // tenant-configurable (e.g. 2530)
insuranceDiscountPct: 0, // Good is not impact-rated → no assumed discount
hailResistance: 'Standard',
scope: ['Architectural laminate shingles', 'Code-required underlayment', 'Standard hip/ridge', 'Standard accessories'],
accent: 'zinc',
},
{
type: 'BETTER',
name: 'Class 4 Impact-Rated',
category: 'Class 4 impact-rated architectural shingle system',
materialClass: 'impact-rated',
warrantyLabel: 'Enhanced manufacturer warranty category',
position: 'Balanced protection and budget',
fit: 'Medium-to-long ownership, meaningful hail exposure, wants stronger protection without premium-system cost.',
pricePerSquare: 565,
serviceLifeYears: 35, // tenant-configurable (e.g. 3040)
insuranceDiscountPct: 15, // illustrative; "ask your carrier to confirm"
hailResistance: 'Higher',
scope: ['Class 4 impact-rated shingles', 'Upgraded underlayment', 'Standard hip/ridge', 'Improved hail resistance'],
accent: 'blue',
},
{
type: 'BEST',
name: 'Premium Long-Life System',
category: 'Composite / synthetic or stone-coated steel category',
materialClass: 'premium',
warrantyLabel: 'Premium long-life warranty category',
position: 'Highest long-term protection',
fit: 'Long ownership, high hail exposure, high premium, wants fewer future roof cycles and strongest curb appeal.',
pricePerSquare: 950,
serviceLifeYears: 50, // tenant-configurable (e.g. 50+)
insuranceDiscountPct: 20, // illustrative; "ask your carrier to confirm"
hailResistance: 'Highest',
scope: ['Composite/synthetic or stone-coated steel', 'Premium underlayment', 'Upgraded accessories', 'Highest durability positioning'],
accent: 'amber',
},
];
// ── Add-ons (spec §8) — plain-language, never fear-based ──────────────────────
export const DEFAULT_ADDONS = [
{
id: 'hip-ridge',
name: 'High-profile hip & ridge',
value: 'Improves the finished look and strengthens the ridge-line appearance.',
price: 480,
compatibility: ['GOOD', 'BETTER', 'BEST'],
recommendedOn: ['BEST'],
triggerKeys: ['aesthetics'],
},
{
id: 'solar-vents',
name: 'Solar attic vents',
value: 'Helps move hot attic air and may improve comfort and roof-system health.',
price: 650,
compatibility: ['GOOD', 'BETTER', 'BEST'],
recommendedOn: [],
triggerKeys: ['attic_hot', 'energy_bills'],
},
{
id: 'gutter-guards',
name: 'Gutter guards',
value: 'Reduces gutter debris buildup and maintenance frequency.',
price: 900,
compatibility: ['GOOD', 'BETTER', 'BEST'],
recommendedOn: [],
triggerKeys: ['canopy'],
},
{
id: 'critter-guards',
name: 'Critter guards',
value: 'Helps block common rodent and small-animal roofline entry points.',
price: 350,
compatibility: ['GOOD', 'BETTER', 'BEST'],
recommendedOn: [],
triggerKeys: ['pests'],
},
{
id: 'upgraded-flashing',
name: 'Upgraded flashing / penetrations',
value: 'Improves protection around common leak points.',
price: 520,
compatibility: ['GOOD', 'BETTER', 'BEST'],
recommendedOn: ['BEST'],
triggerKeys: ['leak', 'old_roof', 'complex'],
},
{
id: 'enhanced-underlayment',
name: 'Enhanced underlayment / ice-water zones',
value: 'Adds protection in vulnerable valleys, eaves, and penetrations.',
price: 700,
compatibility: ['GOOD', 'BETTER', 'BEST'],
recommendedOn: [],
triggerKeys: ['complex', 'leak'],
},
];
// ── Scoring weights (spec §9) — deterministic, tenant-adjustable ──────────────
export const DEFAULT_SCORING_WEIGHTS = {
ownershipHorizon: 0.25,
hailRisk: 0.25,
insuranceSavings: 0.20,
budgetPreference: 0.15,
roofAge: 0.10,
aesthetics: 0.05,
};
// ── Storm-risk settings (spec §6, §14) ────────────────────────────────────────
export const DEFAULT_STORM_SETTINGS = {
windowYears: 20, // 10 fallback / 30 for smoother trends
radiusMiles: 10,
minHailSizeInches: 0.75,
dedupeWindowHours: 6,
};
// ── Pricing engine knobs (spec §14) ───────────────────────────────────────────
export const DEFAULT_PRICING = {
wasteFactor: 1.05,
minJobPrice: 6500,
pitchFactor: 1.0, // multiplied per pitch/stories/complexity in admin
taxPlaceholderPct: 0,
permitPlaceholder: 0,
};
// ── Insurance discount scenarios (spec §10/§14) — low/base/high ───────────────
export const DEFAULT_DISCOUNT_SCENARIOS = {
GOOD: { low: 0, base: 0, high: 0 },
BETTER: { low: 10, base: 15, high: 18 },
BEST: { low: 15, base: 18, high: 22 },
};
// ── Report branding (spec §14) ────────────────────────────────────────────────
export const DEFAULT_BRANDING = {
company: 'LynkedUp Pro',
phone: '866-259-6533',
email: 'estimates@lynkeduppro.com',
serviceArea: 'Texas & surrounding regions',
primaryColor: '#fda913',
legalFooter: 'Preliminary estimate. Not a binding quote.',
};
// ── Legal / disclaimer (spec §15) — verbatim required template ────────────────
export const DISCLAIMER_TEMPLATE =
'This report is an educational, preliminary estimate based on the information provided and available ' +
'property/storm data. Final pricing requires property measurement and contractor inspection. Insurance ' +
'premium savings are illustrative only and must be confirmed with your insurance carrier or licensed ' +
'insurance professional. Historical storm information is not a forecast or guarantee.';
export const TENANT_CONFIG_VERSION = 'tenant-config-v1';
export function getTenantConfig() {
// Phase 5 admin page will override these from localStorage / mock store.
return {
version: TENANT_CONFIG_VERSION,
packages: DEFAULT_PACKAGES,
addons: DEFAULT_ADDONS,
scoringWeights: DEFAULT_SCORING_WEIGHTS,
stormSettings: DEFAULT_STORM_SETTINGS,
pricing: DEFAULT_PRICING,
discountScenarios: DEFAULT_DISCOUNT_SCENARIOS,
branding: DEFAULT_BRANDING,
disclaimer: DISCLAIMER_TEMPLATE,
conservativeMode: false, // §18: hides customer-facing savings claims when true
};
}