@metron-labs/quote-react 0.1.15 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,7 +19,7 @@ function EstimatingToolsInner({ fullPage = false, hideHeader = false, internal =
19
19
  const [activeEstimatorId, setActiveEstimatorId] = useState(null);
20
20
  const [review, setReview] = useState(null);
21
21
  const activeEstimator = ESTIMATOR_TOOLS.find((tool) => tool.id === activeEstimatorId);
22
- const { cart, updateCartQty, updateCartSellUnits, updateCartConversions, removeFromCart } = useCart();
22
+ const { cart, updateCartQty, updateCartSellUnits, updateCartConversions, removeFromCart, clearCart } = useCart();
23
23
  const { adding, basketError, addToBasket } = useFixedBasket(activeEstimator?.title ?? "calculator");
24
24
  const reviewOrBasket = (requirements) => {
25
25
  if (!activeEstimator)
@@ -30,5 +30,10 @@ function EstimatingToolsInner({ fullPage = false, hideHeader = false, internal =
30
30
  else
31
31
  setReview(requirements);
32
32
  };
33
- return (_jsx("div", { className: `app-wrapper${fullPage ? "" : " app-wrapper--embedded"}${hideHeader ? " app-wrapper--no-header" : ""}`, style: getQuoteConfig().theme, children: _jsx("main", { className: "main-content", children: showBasket ? (_jsx(BasketStage, { cart: cart, onUpdateQty: updateCartQty, onUpdateSellUnits: updateCartSellUnits, onResolveConversions: updateCartConversions, onRemove: removeFromCart, onBack: () => onShowBasket(false), internal: internal })) : adding && activeEstimator ? (_jsx(EstimatorBasketLoading, { toolTitle: activeEstimator.title })) : activeEstimator ? (_jsxs(_Fragment, { children: [_jsx("div", { hidden: review !== null, children: _jsx(activeEstimator.Component, { onBack: () => setActiveEstimatorId(null), onReview: reviewOrBasket }) }), basketError && _jsx("p", { className: "results-empty-hint estimator-products__error", children: basketError }), review !== null && (_jsx(EstimatorReviewStage, { toolId: activeEstimator.id, toolTitle: activeEstimator.title, requirements: review, onBack: () => setReview(null) }))] })) : (_jsx(_Fragment, { children: _jsxs("div", { className: "quick-order-card", children: [_jsxs("div", { className: "quick-order-hero", children: [_jsx("div", { className: "quick-order-eyebrow", children: "Construction Services" }), _jsx("h1", { children: "Estimating Tools" }), _jsx("p", { children: "Trade-specific calculators for trusses, fencing, decking, masonry and painting. Dial in your specs and get an instant material estimate." })] }), _jsxs("div", { className: "quick-order-alt", children: [_jsx("div", { className: "quick-order-alt-title", children: "Choose a Calculator" }), _jsx("div", { className: "estimator-tools-grid", children: ESTIMATOR_TOOLS.map((tool) => (_jsxs("button", { type: "button", className: "estimator-tool-card", onClick: () => { setActiveEstimatorId(tool.id); setReview(null); }, children: [_jsx("span", { className: "estimator-tool-card__image-wrap", children: _jsx("img", { className: "fill-img estimator-tool-card__image", src: assetUrl(tool.imageUrl), alt: "" }) }), _jsxs("span", { className: "estimator-tool-card__content", children: [_jsx("span", { className: "estimator-tool-card__title", children: tool.title }), _jsx("span", { className: "estimator-tool-card__desc", children: tool.desc })] })] }, tool.id))) })] })] }) })) }) }));
33
+ return (_jsx("div", { className: `app-wrapper${fullPage ? "" : " app-wrapper--embedded"}${hideHeader ? " app-wrapper--no-header" : ""}`, style: getQuoteConfig().theme, children: _jsx("main", { className: "main-content", children: showBasket ? (_jsx(BasketStage, { cart: cart, onUpdateQty: updateCartQty, onUpdateSellUnits: updateCartSellUnits, onResolveConversions: updateCartConversions, onRemove: removeFromCart, onBack: () => onShowBasket(false), onStartNewQuote: () => {
34
+ clearCart();
35
+ setActiveEstimatorId(null);
36
+ setReview(null);
37
+ onShowBasket(false);
38
+ }, internal: internal })) : adding && activeEstimator ? (_jsx(EstimatorBasketLoading, { toolTitle: activeEstimator.title })) : activeEstimator ? (_jsxs(_Fragment, { children: [_jsx("div", { hidden: review !== null, children: _jsx(activeEstimator.Component, { onBack: () => setActiveEstimatorId(null), onReview: reviewOrBasket }) }), basketError && _jsx("p", { className: "results-empty-hint estimator-products__error", children: basketError }), review !== null && (_jsx(EstimatorReviewStage, { toolId: activeEstimator.id, toolTitle: activeEstimator.title, requirements: review, onBack: () => setReview(null) }))] })) : (_jsx(_Fragment, { children: _jsxs("div", { className: "quick-order-card", children: [_jsxs("div", { className: "quick-order-hero", children: [_jsx("div", { className: "quick-order-eyebrow", children: "Construction Services" }), _jsx("h1", { children: "Estimating Tools" }), _jsx("p", { children: "Trade-specific calculators for trusses, fencing, decking, masonry and painting. Dial in your specs and get an instant material estimate." })] }), _jsxs("div", { className: "quick-order-alt", children: [_jsx("div", { className: "quick-order-alt-title", children: "Choose a Calculator" }), _jsx("div", { className: "estimator-tools-grid", children: ESTIMATOR_TOOLS.map((tool) => (_jsxs("button", { type: "button", className: "estimator-tool-card", onClick: () => { setActiveEstimatorId(tool.id); setReview(null); }, children: [_jsx("span", { className: "estimator-tool-card__image-wrap", children: _jsx("img", { className: "fill-img estimator-tool-card__image", src: assetUrl(tool.imageUrl), alt: "" }) }), _jsxs("span", { className: "estimator-tool-card__content", children: [_jsx("span", { className: "estimator-tool-card__title", children: tool.title }), _jsx("span", { className: "estimator-tool-card__desc", children: tool.desc })] })] }, tool.id))) })] })] }) })) }) }));
34
39
  }
@@ -173,5 +173,5 @@ function QuickQuoteInner({ fullPage = false, hideHeader = false, internal = fals
173
173
  setEntryMode("schedule");
174
174
  await runScheduleMatch(file);
175
175
  }, [runScheduleMatch]);
176
- return (_jsx("div", { className: `app-wrapper${fullPage ? "" : " app-wrapper--embedded"}${hideHeader ? " app-wrapper--no-header" : ""}${!fullPage && stage === "processing" ? " app-wrapper--loading" : ""}`, style: getQuoteConfig().theme, children: _jsxs("main", { className: "main-content", children: [error && (_jsxs("div", { className: "error-box", role: "alert", children: [_jsx("strong", { children: "Error:" }), " ", error] })), stage === "entry" && (_jsx(EntryStage, { onPasteSchedule: handlePasteSchedule, onUploadSchedule: handleUploadSchedule, onEntryModeChange: setEntryMode })), stage === "results" && entryMode === "schedule" && scheduleResult && (_jsx(ScheduleResultsStage, { scheduleResult: scheduleResult, onStartOver: resetToEntry, cart: cart, onAddToCart: addToCart, onRemoveFromCart: removeFromCart, onGoToBasket: goToBasket, cartSeeded: cartSeeded, onCartSeeded: () => setCartSeeded(true), reviewGateComplete: scheduleReviewGateComplete, onReviewGateComplete: () => setScheduleReviewGateComplete(true), internal: internal })), stage === "basket" && (_jsx(BasketStage, { cart: cart, onUpdateQty: updateCartQty, onUpdateSellUnits: updateCartSellUnits, onRemove: removeFromCart, onBack: backToResults, internal: internal }))] }) }));
176
+ return (_jsx("div", { className: `app-wrapper${fullPage ? "" : " app-wrapper--embedded"}${hideHeader ? " app-wrapper--no-header" : ""}${!fullPage && stage === "processing" ? " app-wrapper--loading" : ""}`, style: getQuoteConfig().theme, children: _jsxs("main", { className: "main-content", children: [error && (_jsxs("div", { className: "error-box", role: "alert", children: [_jsx("strong", { children: "Error:" }), " ", error] })), stage === "entry" && (_jsx(EntryStage, { onPasteSchedule: handlePasteSchedule, onUploadSchedule: handleUploadSchedule, onEntryModeChange: setEntryMode })), stage === "results" && entryMode === "schedule" && scheduleResult && (_jsx(ScheduleResultsStage, { scheduleResult: scheduleResult, onStartOver: resetToEntry, cart: cart, onAddToCart: addToCart, onRemoveFromCart: removeFromCart, onGoToBasket: goToBasket, cartSeeded: cartSeeded, onCartSeeded: () => setCartSeeded(true), reviewGateComplete: scheduleReviewGateComplete, onReviewGateComplete: () => setScheduleReviewGateComplete(true), internal: internal })), stage === "basket" && (_jsx(BasketStage, { cart: cart, onUpdateQty: updateCartQty, onUpdateSellUnits: updateCartSellUnits, onRemove: removeFromCart, onBack: backToResults, onStartNewQuote: resetToEntry, internal: internal }))] }) }));
177
177
  }
@@ -6,7 +6,9 @@ interface BasketStageProps {
6
6
  onResolveConversions?: (conversions: Record<string, Partial<CartItem>>) => void;
7
7
  onRemove: (lineId: string) => void;
8
8
  onBack: () => void;
9
+ /** Clears the basket and returns to the start, from the quote-request confirmation page. */
10
+ onStartNewQuote: () => void;
9
11
  internal?: boolean;
10
12
  }
11
- export default function BasketStage({ cart, onUpdateQty, onUpdateSellUnits, onResolveConversions, onRemove, onBack, internal }: BasketStageProps): import("react").JSX.Element;
13
+ export default function BasketStage({ cart, onUpdateQty, onUpdateSellUnits, onResolveConversions, onRemove, onBack, onStartNewQuote, internal }: BasketStageProps): import("react").JSX.Element;
12
14
  export {};
@@ -34,7 +34,7 @@ function TrashIcon() {
34
34
  function ChevronIcon() {
35
35
  return (_jsx("svg", { width: "8", height: "5", viewBox: "0 0 8 5", fill: "none", children: _jsx("path", { d: "M1 4L4 1L7 4", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }) }));
36
36
  }
37
- export default function BasketStage({ cart, onUpdateQty, onUpdateSellUnits, onResolveConversions, onRemove, onBack, internal = false }) {
37
+ export default function BasketStage({ cart, onUpdateQty, onUpdateSellUnits, onResolveConversions, onRemove, onBack, onStartNewQuote, internal = false }) {
38
38
  // Both widgets can render a basket on the same page, so element ids are made unique.
39
39
  const uid = useId();
40
40
  const qzaBasketLayout = !internal && getBoqClientId() === "QZA6-MRA4";
@@ -171,6 +171,7 @@ export default function BasketStage({ cart, onUpdateQty, onUpdateSellUnits, onRe
171
171
  return;
172
172
  }
173
173
  setQuoteRequestSent(true);
174
+ window.scrollTo(0, 0);
174
175
  }
175
176
  catch {
176
177
  setQuoteContactError("Your request couldn't be sent. Check your connection and try again.");
@@ -192,10 +193,13 @@ export default function BasketStage({ cart, onUpdateQty, onUpdateSellUnits, onRe
192
193
  const totalPages = Math.max(1, Math.ceil(pricedItems.length / PAGE_SIZE));
193
194
  const currentPage = Math.min(page, totalPages);
194
195
  const pagedItems = pricedItems.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE);
196
+ if (quoteRequestSent) {
197
+ return (_jsx("div", { className: "wizard-step entry-stage", children: _jsxs("div", { className: "quote-request-sent", role: "status", children: [_jsx("div", { className: "quote-request-sent-icon", "aria-hidden": "true", children: _jsx("svg", { width: "40", height: "40", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "5 12.5 10 17.5 19 7" }) }) }), _jsx("h1", { children: "Quote requested" }), _jsxs("p", { children: ["You're all done. We'll email your sharper price to ", _jsx("strong", { children: reference.trim() }), ", usually within one working day."] }), _jsx("button", { type: "button", className: "cta-btn", onClick: onStartNewQuote, children: _jsx("span", { children: "Start a new quote" }) }), _jsx("p", { className: "quote-request-sent-note", children: "Nothing is charged until you approve the quote." })] }) }));
198
+ }
195
199
  return (_jsx("div", { className: `wizard-step entry-stage${qzaBasketLayout ? " basket-layout--qza6-mra4" : ""}`, children: _jsxs("div", { className: `quick-order-card${internal ? "" : " quick-order-card--customer"}`, children: [_jsxs("div", { className: `quick-order-hero${internal ? "" : " quick-order-hero--customer"}`, children: [internal && _jsx("div", { className: "quick-order-eyebrow", children: "Construction Services" }), _jsxs("div", { className: "basket-hero-copy", children: [qzaBasketLayout && (_jsxs("button", { type: "button", className: "cta-btn cta-btn-secondary basket-hero-back", onClick: onBack, children: [_jsx(BackArrow, {}), _jsx("span", { children: "Back to results" })] })), _jsxs("div", { className: "quick-order-hero-title-row", children: [_jsx("h1", { children: internal ? "Your Selection" : qzaBasketLayout ? "Request a quote" : "Your Basket" }), internal && (_jsxs("span", { className: "summary-chip summary-chip--scope", children: [_jsx("strong", { children: items.length }), " ", items.length === 1 ? "item" : "items"] }))] }), _jsx("p", { children: internal
196
- ? "Review the items you&apos;ve added before exporting them for review."
200
+ ? "Review the items you've added before exporting them for review."
197
201
  : qzaBasketLayout
198
- ? "Check your items & quantities below, and we&apos;ll do our best to beat list prices."
202
+ ? "Check your items & quantities below, and we'll do our best to beat list prices."
199
203
  : "Review your quantities and order summary before you buy." })] })] }), _jsx("div", { className: "quick-order-panel", style: { paddingTop: 16 }, children: items.length === 0 ? (_jsxs("div", { className: "quick-order-history-empty", children: [_jsx("div", { className: "basket-empty-icon", "aria-hidden": "true", children: internal ? "📋" : "🧺" }), _jsx("p", { children: internal ? "Your selection is empty." : qzaBasketLayout ? "Your quote request is empty." : "Your basket is empty." }), _jsx("p", { className: "basket-empty-hint", children: qzaBasketLayout ? "Go back to your results and add items for your quote." : "Go back to your results and add quoted items." }), _jsx("div", { className: "btn-row", style: { marginTop: 20, justifyContent: "center" }, children: _jsx("button", { className: "cta-btn", style: { width: "auto", maxWidth: 280 }, onClick: onBack, "aria-label": "Back to results", children: _jsx("span", { children: "Back to Results" }) }) })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "basket-layout", children: [_jsxs("div", { className: "basket-main", children: [_jsx("div", { className: "basket-list", "aria-label": internal ? "Selection items" : qzaBasketLayout ? "Quote request items" : "Basket items", children: pagedItems.map(({ item, pricing }) => {
200
204
  const requestedQtyValue = qtyDrafts[item.lineId] ?? String(pricing.requestedAmount);
201
205
  const requestedLocked = pricing.requestedUnit === pricing.sellUnit || pricing.costingMode === "review";
@@ -220,7 +224,7 @@ export default function BasketStage({ cart, onUpdateQty, onUpdateSellUnits, onRe
220
224
  setReference(e.target.value);
221
225
  if (qzaBasketLayout)
222
226
  setQuoteContactError("");
223
- }, placeholder: qzaBasketLayout ? "you@example.com" : "PO number or job reference" }), qzaBasketLayout && quoteContactError && (_jsx("p", { className: "basket-quote-request-error", role: "alert", children: quoteContactError })), qzaBasketLayout ? (quoteRequestSent ? (_jsxs("div", { className: "basket-quote-request-success", role: "status", children: [_jsx("strong", { children: "Better price requested" }), _jsx("span", { children: "We'll use this email address to prepare your better price." })] })) : (_jsx("button", { type: "button", className: "cta-btn", onClick: () => void submitQuoteRequest(), disabled: quoteRequestSending, "aria-busy": quoteRequestSending, children: _jsx("span", { children: quoteRequestSending ? "Sending…" : "Request our best quote" }) }))) : (_jsx("button", { className: "cta-btn", onClick: () => void exportBasketExcel(items), "aria-label": internal ? "Download selection" : "Download basket", children: _jsx("span", { children: internal ? "Download Quote" : "Buy Now" }) })), _jsxs("p", { className: "basket-sidebar-disclaimer", children: [_jsx("span", { "aria-hidden": "true", children: "\u26A0\uFE0F" }), qzaBasketLayout
227
+ }, placeholder: qzaBasketLayout ? "you@example.com" : "PO number or job reference" }), qzaBasketLayout && quoteContactError && (_jsx("p", { className: "basket-quote-request-error", role: "alert", children: quoteContactError })), qzaBasketLayout ? (_jsx("button", { type: "button", className: "cta-btn", onClick: () => void submitQuoteRequest(), disabled: quoteRequestSending, "aria-busy": quoteRequestSending, children: _jsx("span", { children: quoteRequestSending ? "Sending…" : "Request our best quote" }) })) : (_jsx("button", { className: "cta-btn", onClick: () => void exportBasketExcel(items), "aria-label": internal ? "Download selection" : "Download basket", children: _jsx("span", { children: internal ? "Download Quote" : "Buy Now" }) })), _jsxs("p", { className: "basket-sidebar-disclaimer", children: [_jsx("span", { "aria-hidden": "true", children: "\u26A0\uFE0F" }), qzaBasketLayout
224
228
  ? "This is a quote request, not an order. We'll confirm our best price, availability and delivery before you approve."
225
229
  : "Prices and availability are estimates and should be confirmed before procurement."] })] })] }), _jsx("div", { className: "btn-row results-actions", children: _jsxs("button", { className: "cta-btn cta-btn-secondary", style: { flex: "0 1 30%", gap: 10, justifyContent: "center" }, onClick: onBack, "aria-label": "Back to results", children: [_jsx(BackArrow, {}), _jsx("span", { children: "Back to results" })] }) })] })) })] }) }));
226
230
  }