import React from "react"; import { createRoot } from "react-dom/client"; import { calculateEstimate } from "./pricing/calculate"; import { PRICING } from "./pricing/config"; import type { Slab, Estimate } from "./pricing/types"; import "./style.css"; const money = (n: number) => new Intl.NumberFormat("en-CA",{style:"currency",currency:"CAD"}).format(n); const initialSlab = (): Slab => ({name:"",lengthIn:120,widthIn:63,retailPrice:0,acquisitionCost:0,quantity:1}); function App() { const [customer,setCustomer] = React.useState({name:"",email:"",phone:"",project:""}); const [slabs,setSlabs] = React.useState([initialSlab()]); const [fabricationHours,setFabricationHours] = React.useState(0); const [fabricationRate,setFabricationRate] = React.useState(85); const [miscHours,setMiscHours] = React.useState(0); const [miscRate,setMiscRate] = React.useState(52.5); const [delivery,setDelivery] = React.useState(PRICING.defaultSupplierDeliveryPerSlab); const [destination,setDestination] = React.useState(""); const [fixedTravel,setFixedTravel] = React.useState(""); const [markup,setMarkup] = React.useState(0); const [competitor,setCompetitor] = React.useState(""); const [result,setResult] = React.useState(null); const [error,setError] = React.useState(""); const updateSlab=(index:number,patch:Partial)=>{setSlabs(prev=>prev.map((s,i)=>i===index?{...s,...patch}:s));setResult(null);}; const numberField=(label:string,value:number,set:(n:number)=>void,step="0.01")=>; const quote=()=>{ try { setError(""); if (!customer.name.trim()) throw new Error("Customer name is required"); if (!slabs.length || slabs.some(s=>!s.name.trim())) throw new Error("Name every selected slab"); if (!destination.trim() || fixedTravel==="") throw new Error("Enter the destination and its verified fixed travel charge"); setResult(calculateEstimate({slabs,fabricationHours,fabricationRate,miscellaneousHours:miscHours,miscellaneousRate:miscRate,supplierDeliveryPerSlab:delivery,destination,fixedTravelCharges:{[destination.trim()]:fixedTravel},materialMarkupPercent:markup,competitorQuote:competitor===""?undefined:competitor})); } catch(e) {setResult(null);setError(e instanceof Error?e.message:String(e));} }; return

Fixit Granite Estimator

v0.01 • Internal development preview • CAD

QUOTE BUILDER
Draft estimates only. Verify all supplier prices and destination charges. Tax, authentication, saved quotes and administrator approvals are not configured.

1. Customer & project

{([["name","Customer name"],["email","Email"],["phone","Phone"],["project","Project / address"]] as const).map(([key,label])=>)}

2. Slab selection & dimensions

{slabs.map((slab,index)=>

Slab {index+1}

{([["lengthIn","Length (in)"],["widthIn","Full width (in)"],["usableWidthIn","Usable width (in)"],["quantity","Quantity"],["retailPrice","Retail price per slab (CAD)"],["acquisitionCost","Actual supplier cost (internal CAD)"]] as const).map(([key,label])=>)}
)}

3. Labour, delivery & travel

{numberField("Fabrication hours",fabricationHours,setFabricationHours)}{numberField("Fabrication rate (CAD 70–100/hr)",fabricationRate,setFabricationRate)}{numberField("Miscellaneous repair hours",miscHours,setMiscHours)}{numberField("Miscellaneous rate (CAD 50–55/hr)",miscRate,setMiscRate)}{numberField("Supplier delivery per slab (CAD)",delivery,setDelivery)}{numberField("Material markup (0–15%)",markup,setMarkup)}

No mileage, fuel, or driving-time surcharge is added. Competitor matching only; further discounts require authenticated administrator approval in a future release.

{result&&}
{error&&

{error}

} {result&&<>

Internal estimate breakdown

Material retail: {money(result.material)}

Material acquisition: {money(result.materialCost)}

Fabrication: {money(result.fabrication)}

Other labour: {money(result.miscellaneous)}

Supplier delivery: {money(result.delivery)}

Fixed travel: {money(result.travel)}

Estimated gross profit before overhead: {money(result.estimatedGrossProfit)}

{result.warnings.map(w=>

{w}

)}

FIXIT GRANITE

Project estimate • Draft

CAD

Prepared for: {customer.name}

{customer.project&&

Project: {customer.project}

}{customer.email&&

Email: {customer.email}

}{customer.phone&&

Phone: {customer.phone}

}

Materials

{slabs.map((s,i)=>)}
MaterialDimensions (in)Qty
{s.name}{s.lengthIn} × {s.widthIn}{s.usableWidthIn!==undefined?` (usable ${s.usableWidthIn})`:""}{s.quantity}
Estimated subtotal (before tax){money(result.sellingPrice)}

Preliminary estimate, subject to site measurements, material availability, final scope and applicable taxes. Not an invoice or binding offer.

}
; } createRoot(document.getElementById("root")!).render();