@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),
|
|
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
|
}
|
package/dist/app/QuickQuote.js
CHANGED
|
@@ -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
|
|
200
|
+
? "Review the items you've added before exporting them for review."
|
|
197
201
|
: qzaBasketLayout
|
|
198
|
-
? "Check your items & quantities below, and we
|
|
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 ? (
|
|
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
|
}
|