@metron-labs/quote-react 0.1.62 → 0.1.63
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.
|
@@ -17,5 +17,5 @@ export declare function candidateFromCart(item: CartItem): MatchCandidate;
|
|
|
17
17
|
export declare function refreshedCartItem(refreshed: CartItem, existing: CartItem): CartItem;
|
|
18
18
|
export declare function displayLineLabel(item: LineItem, allItems: LineItem[]): string;
|
|
19
19
|
export declare function formatResultsTotal(totalValue: number | null, needsPricingCount: number): string | null;
|
|
20
|
-
export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart, onAddToCart, onRemoveFromCart, onGoToBasket, cartSeeded, onCartSeeded, reviewGateComplete, onReviewGateComplete, internal }: ResultsStageProps): import("react").JSX.Element;
|
|
20
|
+
export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart, onAddToCart, onRemoveFromCart, onGoToBasket, cartSeeded, onCartSeeded, reviewGateComplete, onReviewGateComplete, internal, }: ResultsStageProps): import("react").JSX.Element;
|
|
21
21
|
export {};
|
|
@@ -198,7 +198,9 @@ function curatedCandidates(candidates, result, selectedProductId) {
|
|
|
198
198
|
const recommendedIds = result?.rerank?.recommendedCandidateIds ?? [];
|
|
199
199
|
if (recommendedIds.length === 0) {
|
|
200
200
|
const topCandidates = candidates.slice(0, 6);
|
|
201
|
-
const pinnedSelected = !topCandidates.some((candidate) => candidate.product_id === selectedProductId)
|
|
201
|
+
const pinnedSelected = !topCandidates.some((candidate) => candidate.product_id === selectedProductId)
|
|
202
|
+
? candidates.find((candidate) => candidate.product_id === selectedProductId)
|
|
203
|
+
: undefined;
|
|
202
204
|
return pinnedSelected ? [...topCandidates, pinnedSelected] : topCandidates;
|
|
203
205
|
}
|
|
204
206
|
const byId = new Map(candidates.map((candidate) => [candidate.product_id, candidate]));
|
|
@@ -326,7 +328,7 @@ function CandidateBreakdown({ item, candidates, result, decision, unmatchable, s
|
|
|
326
328
|
}, "aria-label": isCurrent ? `${c.description} selected` : `Use ${c.description}` }), _jsx("span", { className: "switch-toggle-track", "aria-hidden": "true" })] }) })] }, `${c.product_id}-${i}`));
|
|
327
329
|
}) })] })] }));
|
|
328
330
|
}
|
|
329
|
-
export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart, onAddToCart, onRemoveFromCart, onGoToBasket, cartSeeded, onCartSeeded, reviewGateComplete, onReviewGateComplete, internal = false }) {
|
|
331
|
+
export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart, onAddToCart, onRemoveFromCart, onGoToBasket, cartSeeded, onCartSeeded, reviewGateComplete, onReviewGateComplete, internal = false, }) {
|
|
330
332
|
const qzaQuoteRequest = !internal && getBoqClientId() === "QZA6-MRA4";
|
|
331
333
|
const customerCollection = qzaQuoteRequest ? "quote" : "basket";
|
|
332
334
|
const { results, lineItems, parseReport, timings } = scheduleResult;
|
|
@@ -368,7 +370,10 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
368
370
|
const pick = pickOverrides[item.line_id];
|
|
369
371
|
const automaticMatch = resolveBest(result, pick);
|
|
370
372
|
const savedCartMatch = cart[item.line_id];
|
|
371
|
-
const matched = pick === NO_SELECTION
|
|
373
|
+
const matched = pick === NO_SELECTION
|
|
374
|
+
? undefined
|
|
375
|
+
: (pickOverrideCandidates[item.line_id] ??
|
|
376
|
+
(savedCartMatch && savedCartMatch.productId !== automaticMatch?.product_id ? candidateFromCart(savedCartMatch) : automaticMatch));
|
|
372
377
|
const best = unmatchable ? undefined : matched;
|
|
373
378
|
const qty = item.qty ?? 0;
|
|
374
379
|
return {
|
|
@@ -671,7 +676,9 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
671
676
|
}));
|
|
672
677
|
const reviewCandidates = reviewQuery ? catalogueSearchCandidates : llmReviewCandidates;
|
|
673
678
|
const savedSearchCandidate = activeReviewRow ? savedSearchCandidates[activeReviewRow.item.line_id] : undefined;
|
|
674
|
-
const reviewDisplayCandidates = !reviewQuery && savedSearchCandidate && !reviewCandidates.some((candidate) => candidate.product_id === savedSearchCandidate.product_id)
|
|
679
|
+
const reviewDisplayCandidates = !reviewQuery && savedSearchCandidate && !reviewCandidates.some((candidate) => candidate.product_id === savedSearchCandidate.product_id)
|
|
680
|
+
? [...reviewCandidates, savedSearchCandidate]
|
|
681
|
+
: reviewCandidates;
|
|
675
682
|
const reviewOptionCandidates = (() => {
|
|
676
683
|
const topCandidates = reviewDisplayCandidates.slice(0, reviewQuery ? 12 : 6);
|
|
677
684
|
if (reviewQuery || !savedSearchCandidate || topCandidates.some((candidate) => candidate.product_id === savedSearchCandidate.product_id))
|
|
@@ -680,22 +687,22 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
680
687
|
})();
|
|
681
688
|
const activeReviewCartMatches = !!activeReviewRow?.best && cart[activeReviewRow.item.line_id]?.productId === activeReviewRow.best.product_id;
|
|
682
689
|
const activeReviewHasPendingUpdate = !!activeReviewRow?.best && !!cart[activeReviewRow.item.line_id] && !activeReviewCartMatches;
|
|
683
|
-
return (_jsxs("div", { className: "wizard-step entry-stage", children: [_jsxs("div", { className: `quick-order-card${internal ? "" : " quick-order-card--customer"}`, children: [_jsx("div", { className: `quick-order-hero${internal ? "" : " quick-order-hero--customer"}`, children: internal ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "quick-order-eyebrow", children: "Construction Services" }), _jsxs("div", { className: "quick-order-hero-title-row", children: [_jsx("h1", { children: "Your Results" }), _jsxs("span", { className: "summary-chip summary-chip--scope", children: [_jsx("strong", { children: totalLabel ?? "Review quantities" }), " Est. total"] })] }), _jsx("p", { children: "Each line item from your schedule matched against your catalogue, ranked by confidence." })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsx("h1", { children: "Review your matched products" }), _jsxs("p", { children: ["Check you're happy with these items, then click ", qzaQuoteRequest ? "View Quote" : "View Basket", " to continue."] })] }), _jsxs("button", { type: "button", className: "results-total-action", onClick: onGoToBasket, disabled: cartCount === 0, "aria-label": qzaQuoteRequest ? "View quote" : "View basket", children: [_jsxs("span", { children: [_jsx("small", { children: qzaQuoteRequest ? "View Quote" : "View Basket" }), totalLabel != null && _jsx("strong", { children: totalLabel })] }), _jsx("span", { className: "arrow", children: _jsx(ForwardArrow, {}) })] })] })) }), _jsxs("div", { className: "quick-order-panel", style: { paddingTop: 16 }, children: [debugEnabled && (_jsx("div", { className: "results-summary", style: { marginBottom: 12 }, children: _jsx("button", { type: "button", className: "bom-toolbar-btn", "aria-pressed": showTimings, onClick: () => setShowTimings((value) => !value), children: showTimings ? "Hide timings" : "Show timings" }) })), debugEnabled && showTimings && _jsx(TimingBreakdown, { ...timings }), !qzaQuoteRequest && (_jsx("div", { className: "results-toolbar-row", children: !activeReviewRow && (
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
690
|
+
return (_jsxs("div", { className: "wizard-step entry-stage", children: [_jsxs("div", { className: `quick-order-card${internal ? "" : " quick-order-card--customer"}`, children: [_jsx("div", { className: `quick-order-hero${internal ? "" : " quick-order-hero--customer"}`, children: internal ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "quick-order-eyebrow", children: "Construction Services" }), _jsxs("div", { className: "quick-order-hero-title-row", children: [_jsx("h1", { children: "Your Results" }), _jsxs("span", { className: "summary-chip summary-chip--scope", children: [_jsx("strong", { children: totalLabel ?? "Review quantities" }), " Est. total"] })] }), _jsx("p", { children: "Each line item from your schedule matched against your catalogue, ranked by confidence." })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsx("h1", { children: "Review your matched products" }), _jsxs("p", { children: ["Check you're happy with these items, then click ", qzaQuoteRequest ? "View Quote" : "View Basket", " to continue."] })] }), _jsxs("button", { type: "button", className: "results-total-action", onClick: onGoToBasket, disabled: cartCount === 0, "aria-label": qzaQuoteRequest ? "View quote" : "View basket", children: [_jsxs("span", { children: [_jsx("small", { children: qzaQuoteRequest ? "View Quote" : "View Basket" }), totalLabel != null && _jsx("strong", { children: totalLabel })] }), _jsx("span", { className: "arrow", children: _jsx(ForwardArrow, {}) })] })] })) }), _jsxs("div", { className: "quick-order-panel", style: { paddingTop: 16 }, children: [debugEnabled && (_jsx("div", { className: "results-summary", style: { marginBottom: 12 }, children: _jsx("button", { type: "button", className: "bom-toolbar-btn", "aria-pressed": showTimings, onClick: () => setShowTimings((value) => !value), children: showTimings ? "Hide timings" : "Show timings" }) })), debugEnabled && showTimings && _jsx(TimingBreakdown, { ...timings }), !qzaQuoteRequest && (_jsx("div", { className: "results-toolbar-row", children: !activeReviewRow && (_jsx("div", { className: "results-summary", style: { marginBottom: 0 }, children: _jsxs("div", { style: {
|
|
691
|
+
display: "flex",
|
|
692
|
+
gap: 8,
|
|
693
|
+
flexWrap: "wrap",
|
|
694
|
+
alignItems: "center",
|
|
695
|
+
}, children: [_jsx("input", { type: "text", className: "results-search-input", placeholder: "Search description...", value: search, onChange: (e) => setSearch(e.target.value), "aria-label": "Search line items" }), !internal && (_jsxs("div", { className: "results-view-toggle", "aria-label": "Results view", children: [_jsx("button", { type: "button", className: `results-view-toggle__btn${viewMode === "tile" ? " is-active" : ""}`, "aria-pressed": viewMode === "tile", "aria-label": "Tile view", title: "Tile view", onClick: () => setViewMode("tile"), children: _jsxs("svg", { width: "18", height: "12", viewBox: "0 0 15 10", fill: "currentColor", children: [_jsx("rect", { x: "0.5", y: "0.5", width: "4", height: "4" }), _jsx("rect", { x: "5.5", y: "0.5", width: "4", height: "4" }), _jsx("rect", { x: "10.5", y: "0.5", width: "4", height: "4" }), _jsx("rect", { x: "0.5", y: "5.5", width: "4", height: "4" }), _jsx("rect", { x: "5.5", y: "5.5", width: "4", height: "4" }), _jsx("rect", { x: "10.5", y: "5.5", width: "4", height: "4" })] }) }), _jsx("button", { type: "button", className: `results-view-toggle__btn${viewMode === "list" ? " is-active" : ""}`, "aria-pressed": viewMode === "list", "aria-label": "List view", title: "List view", onClick: () => setViewMode("list"), children: _jsxs("svg", { width: "17", height: "17", viewBox: "0 0 15 15", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", children: [_jsx("line", { x1: "0.5", y1: "2.5", x2: "14.5", y2: "2.5" }), _jsx("line", { x1: "0.5", y1: "7.5", x2: "14.5", y2: "7.5" }), _jsx("line", { x1: "0.5", y1: "12.5", x2: "14.5", y2: "12.5" })] }) })] })), debugEnabled && (_jsx("button", { type: "button", className: "bom-toolbar-btn", onClick: () => setDetailMode((mode) => (mode === "simple" ? "detailed" : "simple")), children: detailMode === "detailed" ? "Hide detailed view" : "Show detailed view" })), _jsxs("details", { ref: sortMenuRef, style: { position: "relative" }, children: [_jsx("summary", { className: "bom-toolbar-btn sort-menu-summary", "aria-label": "Sort line items", style: { fontSize: 16 }, children: "\u21C5" }), _jsx("div", { className: "sort-menu-panel", children: SORT_OPTIONS.map((opt) => {
|
|
696
|
+
const active = sortConfig === opt.dir;
|
|
697
|
+
return (_jsxs("button", { type: "button", className: `sort-menu-item${active ? " is-active" : ""}`, onClick: () => {
|
|
698
|
+
setSortConfig(active ? null : opt.dir);
|
|
699
|
+
sortMenuRef.current?.removeAttribute("open");
|
|
700
|
+
}, children: [_jsx("span", { className: "sort-menu-item__check", children: active ? "✓" : "" }), opt.label] }, opt.dir));
|
|
701
|
+
}) })] })] }) })) })), viewMode === "tile" || activeReviewRow ? (_jsxs(_Fragment, { children: [activeReviewRow ? (_jsx("div", { className: "modal-backdrop quick-order-modal-backdrop", role: "dialog", "aria-modal": "true", "aria-label": "Product options", onClick: () => closeReview(), children: _jsxs("section", { className: "quick-order-workflow-modal results-review-modal", onClick: (event) => event.stopPropagation(), children: [_jsx("button", { type: "button", className: "quick-order-workflow-close results-review-modal__close", onClick: () => closeReview(), "aria-label": "Close product options", children: _jsx(CloseIcon, {}) }), _jsx("div", { className: "results-review-page", children: internal ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "results-review-page__top", children: [_jsxs("button", { type: "button", className: "bom-toolbar-btn bom-toolbar-btn--accent", onClick: () => closeReview(), children: [_jsx(BackArrow, {}), "Back to results"] }), _jsxs("div", { style: {
|
|
695
702
|
display: "flex",
|
|
696
703
|
gap: 8,
|
|
697
704
|
flexWrap: "wrap",
|
|
698
|
-
}, children: [debugEnabled && (_jsx("button", { type: "button", className: "bom-toolbar-btn", onClick: () => setDetailMode((mode) => (mode === "simple" ? "detailed" : "simple")), children: detailMode === "detailed" ? "Hide detailed info" : "Show detailed info" })), _jsx("div", { className: "results-review-searchbar", children: _jsx("input", { type: "text", className: "results-search-input", placeholder: "Search products in catalogue...", value: reviewSearch, onChange: (e) => setReviewSearch(e.target.value), "aria-label": "Search database candidates" }) }), _jsx("div", { className: "results-candidate-actions", children: _jsx("button", { type: "button", className: "bom-toolbar-btn", onClick: () => clearPickedProduct(activeReviewRow.item.line_id), children: "Clear selected product" }) })] })] }), _jsxs("div", { className: "results-review-page__hero", children: [_jsx("div", { className: "results-review-page__image-wrap", children: _jsx("img", { className: "fill-img results-review-page__image", src: productImageSrc(previewCandidate(activeReviewRow)?.description || activeReviewRow.item.description_original, previewCandidate(activeReviewRow)?.product_id, previewCandidate(activeReviewRow)?.pdp_photo_url), alt: "" }) }), _jsxs("div", { className: "results-review-page__summary", children: [_jsx("h2", { children: displayLineLabel(activeReviewRow.item, lineItems) }), _jsxs("p", { className: "results-review-page__meta", children: [fmtQty(activeReviewRow.qty), " ", renderDisplayUnit(activeReviewRow.item.unit, activeReviewRow.qty)] }), activeReviewRow.best ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "results-review-page__product", children: activeReviewRow.best.description }), activeReviewRow.best.price != null ? _jsx("div", { className: "results-row-meta", children: fmtPrice(activeReviewRow.best.price, activeReviewRow.best.price_basis) }) : null] })) : previewCandidate(activeReviewRow) ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "results-review-page__product", children: previewCandidate(activeReviewRow)?.description }), previewCandidate(activeReviewRow)?.price != null ? (_jsx("div", { className: "results-row-meta", children: fmtPrice(previewCandidate(activeReviewRow)?.price ?? null, previewCandidate(activeReviewRow)?.price_basis ?? null) })) : null, _jsx("div", { className: "results-row-meta", children: "Best reviewed alternative - not yet selected" })] })) : (_jsx("div", { className: "results-empty-hint", children: "No product currently selected for this line item." })), hasVisibleLinkedGroup(activeReviewRow.item) ? (_jsxs("div", { style: {
|
|
705
|
+
}, children: [debugEnabled && (_jsx("button", { type: "button", className: "bom-toolbar-btn", onClick: () => setDetailMode((mode) => (mode === "simple" ? "detailed" : "simple")), children: detailMode === "detailed" ? "Hide detailed info" : "Show detailed info" })), _jsx("div", { className: "results-review-searchbar", children: _jsx("input", { type: "text", className: "results-search-input", placeholder: "Search products in catalogue...", value: reviewSearch, onChange: (e) => setReviewSearch(e.target.value), "aria-label": "Search database candidates" }) }), _jsx("div", { className: "results-candidate-actions", children: _jsx("button", { type: "button", className: "bom-toolbar-btn", onClick: () => clearPickedProduct(activeReviewRow.item.line_id), children: "Clear selected product" }) })] })] }), _jsxs("div", { className: "results-review-page__hero", children: [_jsx("div", { className: "results-review-page__image-wrap", children: _jsx("img", { className: "fill-img results-review-page__image", src: productImageSrc(previewCandidate(activeReviewRow)?.description || activeReviewRow.item.description_original, previewCandidate(activeReviewRow)?.product_id, previewCandidate(activeReviewRow)?.pdp_photo_url), alt: "" }) }), _jsxs("div", { className: "results-review-page__summary", children: [_jsx("h2", { children: displayLineLabel(activeReviewRow.item, lineItems) }), _jsxs("p", { className: "results-review-page__meta", children: [fmtQty(activeReviewRow.qty), " ", renderDisplayUnit(activeReviewRow.item.unit, activeReviewRow.qty)] }), activeReviewRow.best ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "results-review-page__product", children: activeReviewRow.best.description }), activeReviewRow.best.price != null ? (_jsx("div", { className: "results-row-meta", children: fmtPrice(activeReviewRow.best.price, activeReviewRow.best.price_basis) })) : null] })) : previewCandidate(activeReviewRow) ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "results-review-page__product", children: previewCandidate(activeReviewRow)?.description }), previewCandidate(activeReviewRow)?.price != null ? (_jsx("div", { className: "results-row-meta", children: fmtPrice(previewCandidate(activeReviewRow)?.price ?? null, previewCandidate(activeReviewRow)?.price_basis ?? null) })) : null, _jsx("div", { className: "results-row-meta", children: "Best reviewed alternative - not yet selected" })] })) : (_jsx("div", { className: "results-empty-hint", children: "No product currently selected for this line item." })), hasVisibleLinkedGroup(activeReviewRow.item) ? (_jsxs("div", { style: {
|
|
699
706
|
display: "flex",
|
|
700
707
|
alignItems: "center",
|
|
701
708
|
gap: 8,
|
|
@@ -708,7 +715,19 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
708
715
|
saveSearchCandidate(activeReviewRow.item.line_id, activeReviewRow.best);
|
|
709
716
|
onAddToCart(toCartItem(activeReviewRow));
|
|
710
717
|
}
|
|
711
|
-
}, disabled: !activeReviewRow.best, "aria-label": cart[activeReviewRow.item.line_id]
|
|
718
|
+
}, disabled: !activeReviewRow.best, "aria-label": cart[activeReviewRow.item.line_id]
|
|
719
|
+
? internal
|
|
720
|
+
? "Remove from selection"
|
|
721
|
+
: `Remove from ${customerCollection}`
|
|
722
|
+
: internal
|
|
723
|
+
? "Add to selection"
|
|
724
|
+
: `Add to ${customerCollection}`, children: cart[activeReviewRow.item.line_id] ? "✓ Added" : internal ? "+ Add to selection" : `Add to ${customerCollection}` }) })] })] }), catalogueSearchLoading ? _jsx("div", { className: "results-empty-card", children: "Searching catalogue\u2026" }) : null, !catalogueSearchLoading && catalogueSearchError ? _jsx("div", { className: "results-empty-card", children: catalogueSearchError }) : null, !catalogueSearchLoading && !catalogueSearchError && reviewQuery && reviewCandidates.length === 0 ? (_jsx("div", { className: "results-empty-card", children: "No candidate products match this database search." })) : !catalogueSearchLoading && !catalogueSearchError ? (_jsx(CandidateBreakdown, { item: activeReviewRow.item, candidates: reviewCandidates, result: activeReviewRow.result, decision: activeReviewRow.decision, unmatchable: activeReviewRow.unmatchable, selectedProductId: activeReviewRow.best?.product_id ?? null, detailMode: detailMode, linkedGroupMeta: hasVisibleLinkedGroup(activeReviewRow.item) ? linkedGroupMetaById.get(activeReviewRow.item.linked_group_id) : undefined, onPick: (candidate) => handlePickProduct(activeReviewRow.item.line_id, candidate), onClear: () => clearPickedProduct(activeReviewRow.item.line_id), showAll: internal || debugEnabled || reviewQuery.length > 0, internal: internal, debugEnabled: debugEnabled, forceReviewNote: true })) : null] })) : (_jsxs(_Fragment, { children: [_jsxs("section", { className: "customer-review-request", children: [_jsx("span", { className: "customer-review-eyebrow", children: "Original request" }), _jsx("strong", { children: displayLineLabel(activeReviewRow.item, lineItems) || "Original request" })] }), activeReviewReason ? (_jsxs("section", { className: "customer-review-ai", children: [_jsx("strong", { children: hasNoSuitableCandidate ? "No automatic match:" : "AI review:" }), " ", activeReviewReason] })) : null, _jsxs("section", { className: "customer-review-options", children: [_jsx("div", { className: "customer-review-options__head", children: _jsxs("div", { children: [_jsx("h2", { children: hasNoSuitableCandidate
|
|
725
|
+
? llmReviewCandidates.length > 0
|
|
726
|
+
? "Choose a product"
|
|
727
|
+
: "No suitable product found"
|
|
728
|
+
: hasAiVerdict
|
|
729
|
+
? "Choose a product"
|
|
730
|
+
: "We couldn't review this item" }), _jsx("p", { children: hasNoSuitableCandidate
|
|
712
731
|
? llmReviewCandidates.length > 0
|
|
713
732
|
? "No product was selected automatically. Review these related options before adding one."
|
|
714
733
|
: "Search the catalogue to choose an alternative."
|
|
@@ -717,7 +736,7 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
717
736
|
: "Search the catalogue to choose a product." })] }) }), !reviewQuery && reviewDisplayCandidates.length > 0 ? (_jsx("div", { className: "customer-review-option-grid", children: reviewOptionCandidates.map((candidate) => {
|
|
718
737
|
const selected = candidate.product_id === activeReviewRow.best?.product_id;
|
|
719
738
|
return (_jsxs("button", { type: "button", className: `customer-review-option${selected ? " is-selected" : ""}`, onClick: () => handlePickProduct(activeReviewRow.item.line_id, candidate), "aria-pressed": selected, children: [_jsx("img", { className: "customer-review-option__image", src: productImageSrc(candidate.description, candidate.product_id, candidate.pdp_photo_url), alt: "", width: 64, height: 64 }), _jsx("span", { className: "customer-review-option__copy", children: _jsx("strong", { children: candidate.description }) }), _jsx("span", { className: "customer-review-option__price", children: candidate.price != null ? `£${fmtPrice(candidate.price, candidate.price_basis)}` : "-" }), _jsx("span", { className: "customer-review-option__state", children: selected ? "Selected" : "Choose" })] }, candidate.product_id));
|
|
720
|
-
}) })) : null, reviewDisplayCandidates.length > 0 && (_jsx("div", { className: "customer-review-divider", children: _jsx("span", { children: "or" }) })), _jsxs("label", { className: "customer-review-search", children: [_jsx("span", { children: "Search our full catalogue" }), _jsx("input", { type: "text", className: "results-search-input", placeholder: "Search products...", value: reviewSearch, onChange: (e) => setReviewSearch(e.target.value), "aria-label": "Search catalogue" })] }), reviewQuery && catalogueSearchLoading ? _jsx("div", { className: "results-empty-card", children: "Searching catalogue\u2026" }) : null, reviewQuery && !catalogueSearchLoading && catalogueSearchError ? _jsx("div", { className: "results-empty-card", children: catalogueSearchError }) : null, reviewQuery && !catalogueSearchLoading && !catalogueSearchError && reviewDisplayCandidates.length === 0 ? _jsx("div", { className: "results-empty-card", children: "No products match this search." }) : null, reviewQuery && !catalogueSearchLoading && !catalogueSearchError && reviewDisplayCandidates.length > 0 ? (_jsx("div", { className: "customer-review-option-grid", children: reviewOptionCandidates.map((candidate) => {
|
|
739
|
+
}) })) : null, reviewDisplayCandidates.length > 0 && (_jsx("div", { className: "customer-review-divider", children: _jsx("span", { children: "or" }) })), _jsxs("label", { className: "customer-review-search", children: [_jsx("span", { children: "Search our full catalogue" }), _jsx("input", { type: "text", className: "results-search-input", placeholder: "Search products...", value: reviewSearch, onChange: (e) => setReviewSearch(e.target.value), "aria-label": "Search catalogue" })] }), reviewQuery && catalogueSearchLoading ? _jsx("div", { className: "results-empty-card", children: "Searching catalogue\u2026" }) : null, reviewQuery && !catalogueSearchLoading && catalogueSearchError ? _jsx("div", { className: "results-empty-card", children: catalogueSearchError }) : null, reviewQuery && !catalogueSearchLoading && !catalogueSearchError && reviewDisplayCandidates.length === 0 ? (_jsx("div", { className: "results-empty-card", children: "No products match this search." })) : null, reviewQuery && !catalogueSearchLoading && !catalogueSearchError && reviewDisplayCandidates.length > 0 ? (_jsx("div", { className: "customer-review-option-grid", children: reviewOptionCandidates.map((candidate) => {
|
|
721
740
|
const selected = candidate.product_id === activeReviewRow.best?.product_id;
|
|
722
741
|
return (_jsxs("button", { type: "button", className: `customer-review-option${selected ? " is-selected" : ""}`, onClick: () => handlePickProduct(activeReviewRow.item.line_id, candidate), "aria-pressed": selected, children: [_jsx("img", { className: "customer-review-option__image", src: productImageSrc(candidate.description, candidate.product_id, candidate.pdp_photo_url), alt: "", width: 64, height: 64 }), _jsx("span", { className: "customer-review-option__copy", children: _jsx("strong", { children: candidate.description }) }), _jsx("span", { className: "customer-review-option__price", children: candidate.price != null ? `£${fmtPrice(candidate.price, candidate.price_basis)}` : "-" }), _jsx("span", { className: "customer-review-option__state", children: selected ? "Selected" : "Choose" })] }, candidate.product_id));
|
|
723
742
|
}) })) : null] }), activeReviewRow.best ? (activeReviewHasPendingUpdate ? (_jsxs("div", { style: { display: "grid", gap: 8 }, children: [_jsx("button", { type: "button", className: "cta-btn customer-review-add", onClick: () => {
|
|
@@ -746,7 +765,13 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
746
765
|
else {
|
|
747
766
|
onAddToCart(toCartItem(row));
|
|
748
767
|
}
|
|
749
|
-
}, disabled: !preview, "aria-label": cart[item.line_id]
|
|
768
|
+
}, disabled: !preview, "aria-label": cart[item.line_id]
|
|
769
|
+
? internal
|
|
770
|
+
? "Remove from selection"
|
|
771
|
+
: `Remove from ${customerCollection}`
|
|
772
|
+
: internal
|
|
773
|
+
? "Add to selection"
|
|
774
|
+
: `Add to ${customerCollection}`, children: cart[item.line_id] ? "✓ Added" : internal ? "+ Add" : `Add to ${customerCollection}` })] })] }, item.line_id));
|
|
750
775
|
}) }) })) : null, unmatchedSection] })] })) : (_jsxs(_Fragment, { children: [hoveredRowBounds && (_jsx("div", { className: "results-table__hover-overlay", style: hoveredRowBounds, "aria-hidden": "true", children: "Click to view options" })), _jsx("div", { className: "bom-table-wrap", children: _jsxs("table", { className: "bom-table results-table", "aria-label": "Matched line items", children: [_jsxs("colgroup", { children: [_jsx("col", { className: "col-item" }), _jsx("col", { style: { width: "220px" } }), _jsx("col", { style: { width: "100px" } }), detailMode === "simple" && _jsx("col", { style: { width: "110px" } })] }), _jsx("thead", { children: _jsxs("tr", { className: "bom-col-header", children: [_jsx("th", { style: { textAlign: "left" }, children: "Description" }), _jsx("th", { style: { textAlign: "center" }, children: "Product Matched" }), _jsx("th", { style: { textAlign: "center" }, children: "Price" }), detailMode === "simple" && _jsx("th", { style: { textAlign: "center" }, children: internal ? "Selection" : qzaQuoteRequest ? "Quote" : "Basket" })] }) }), _jsxs("tbody", { children: [visibleRows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: detailMode === "simple" ? 4 : 3, className: "results-empty-hint", style: { padding: "16px 8px" }, children: "No line items match the current search/filter." }) })), matchedDisplayRows.map((row) => {
|
|
751
776
|
const { item, result, decision, best } = row;
|
|
752
777
|
return (_jsxs("tr", { className: "bom-row results-table__review-row", onClick: () => openReview(item.line_id), onMouseEnter: (event) => {
|
|
@@ -767,7 +792,13 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
767
792
|
else {
|
|
768
793
|
onAddToCart(toCartItem(row));
|
|
769
794
|
}
|
|
770
|
-
}, "aria-label": cart[item.line_id]
|
|
795
|
+
}, "aria-label": cart[item.line_id]
|
|
796
|
+
? internal
|
|
797
|
+
? "Remove from selection"
|
|
798
|
+
: `Remove from ${customerCollection}`
|
|
799
|
+
: internal
|
|
800
|
+
? "Add to selection"
|
|
801
|
+
: `Add to ${customerCollection}`, children: cart[item.line_id] ? "✓ Added" : internal ? "+ Add" : `Add to ${customerCollection}` }) }))] }, item.line_id));
|
|
771
802
|
})] })] }) }), unmatchedSection] })), !activeReviewRow && totalPages > 1 && (_jsxs("div", { className: "results-pagination", children: [_jsxs("span", { className: "results-pagination-count", children: ["Showing ", (currentPage - 1) * PAGE_SIZE + 1, "\u2013", Math.min(currentPage * PAGE_SIZE, visibleRows.length), " of ", visibleRows.length] }), _jsxs("div", { className: "results-pagination-controls", children: [_jsx("button", { type: "button", className: "bom-toolbar-btn", onClick: () => setPage(currentPage - 1), disabled: currentPage === 1, children: "Previous" }), _jsxs("span", { className: "results-pagination-page", children: ["Page ", currentPage, " of ", totalPages] }), _jsx("button", { type: "button", className: "bom-toolbar-btn", onClick: () => setPage(currentPage + 1), disabled: currentPage === totalPages, children: "Next" })] })] })), (parseReport.itemCount != null || parseReport.headingCount != null || parseReport.skippedCount != null) && (_jsxs("div", { className: "results-summary", style: { marginTop: 16 }, children: [parseReport.itemCount != null && (_jsxs("span", { className: "summary-chip", children: [_jsx("strong", { children: parseReport.itemCount }), " items parsed"] })), parseReport.headingCount != null && (_jsxs("span", { className: "summary-chip", children: [_jsx("strong", { children: parseReport.headingCount }), " headings"] })), parseReport.skippedCount != null && (_jsxs("span", { className: "summary-chip", children: [_jsx("strong", { children: parseReport.skippedCount }), " rows skipped"] }))] })), _jsxs("div", { className: "disclaimer-box", children: [_jsx("span", { className: "disclaimer-icon", children: "\u26A0\uFE0F" }), _jsx("p", { className: "disclaimer-text", children: "These matches are generated automatically and should be reviewed before purchase." })] }), !activeReviewRow && (_jsxs("div", { className: "btn-row results-actions", children: [_jsxs("button", { className: "cta-btn cta-btn-secondary", style: {
|
|
772
803
|
flex: "0 1 30%",
|
|
773
804
|
gap: 10,
|
|
@@ -272,13 +272,13 @@ export default function EstimatorResults({ toolId, requirements, ready, onBack,
|
|
|
272
272
|
const options = pickerResults.length > 0 ? pickerResults : row.candidates;
|
|
273
273
|
return (_jsxs("div", { className: "estimator-products__picker", children: [_jsx("input", { type: "text", className: "results-search-input", value: pickerSearch, autoFocus: true, placeholder: "Search products in catalogue...", onChange: (event) => setPickerSearch(event.target.value), "aria-label": `Search a product for ${row.requirement.label}` }), _jsxs("div", { className: "estimator-products__options", children: [options.map((candidate) => (_jsxs("button", { type: "button", className: `estimator-products__option${candidate.product_id === row.chosen?.product_id ? " is-selected" : ""}`, onClick: () => chooseCandidate(row.requirement.key, candidate), children: [_jsx("span", { children: candidate.description }), _jsxs("span", { className: "results-row-meta", children: [candidate.product_id, candidate.price != null && ` · ${fmtCurrency(candidate.price)}`] })] }, candidate.product_id))), options.length === 0 && _jsx("p", { className: "results-empty-hint", children: "No matching products." })] })] }));
|
|
274
274
|
};
|
|
275
|
-
return (_jsxs("section", { className: "estimator-products results-tile-section", "aria-label": "Matched products", children: [!loading && (_jsxs("div", { className: "estimator-products__head", children: [_jsxs("div", { className: "results-tile-section__head", children: [_jsx("h3", { children: "Matched Products" }), _jsx("p", { children: "Products matched to your estimate, check before adding to your basket." })] }), !reviewRow && (
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
275
|
+
return (_jsxs("section", { className: "estimator-products results-tile-section", "aria-label": "Matched products", children: [!loading && (_jsxs("div", { className: "estimator-products__head", children: [_jsxs("div", { className: "results-tile-section__head", children: [_jsx("h3", { children: "Matched Products" }), _jsx("p", { children: "Products matched to your estimate, check before adding to your basket." })] }), !reviewRow && (_jsx("div", { className: "results-summary estimator-products__toolbar", children: _jsxs("div", { className: "estimator-products__controls", children: [_jsx("input", { type: "text", className: "results-search-input", placeholder: "Search description...", value: search, onChange: (event) => setSearch(event.target.value), "aria-label": "Search matched products" }), _jsxs("div", { className: "results-view-toggle", "aria-label": "Results view", children: [_jsx("button", { type: "button", className: `results-view-toggle__btn${viewMode === "tile" ? " is-active" : ""}`, "aria-pressed": viewMode === "tile", "aria-label": "Tile view", title: "Tile view", onClick: () => setViewMode("tile"), children: _jsxs("svg", { width: "18", height: "12", viewBox: "0 0 15 10", fill: "currentColor", children: [_jsx("rect", { x: "0.5", y: "0.5", width: "4", height: "4" }), _jsx("rect", { x: "5.5", y: "0.5", width: "4", height: "4" }), _jsx("rect", { x: "10.5", y: "0.5", width: "4", height: "4" }), _jsx("rect", { x: "0.5", y: "5.5", width: "4", height: "4" }), _jsx("rect", { x: "5.5", y: "5.5", width: "4", height: "4" }), _jsx("rect", { x: "10.5", y: "5.5", width: "4", height: "4" })] }) }), _jsx("button", { type: "button", className: `results-view-toggle__btn${viewMode === "list" ? " is-active" : ""}`, "aria-pressed": viewMode === "list", "aria-label": "List view", title: "List view", onClick: () => setViewMode("list"), children: _jsxs("svg", { width: "17", height: "17", viewBox: "0 0 15 15", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", children: [_jsx("line", { x1: "0.5", y1: "2.5", x2: "14.5", y2: "2.5" }), _jsx("line", { x1: "0.5", y1: "7.5", x2: "14.5", y2: "7.5" }), _jsx("line", { x1: "0.5", y1: "12.5", x2: "14.5", y2: "12.5" })] }) })] }), _jsxs("details", { ref: sortMenuRef, style: { position: "relative" }, children: [_jsx("summary", { className: "bom-toolbar-btn sort-menu-summary", "aria-label": "Sort materials", style: { fontSize: 16 }, children: "\u21C5" }), _jsx("div", { className: "sort-menu-panel", children: SORT_OPTIONS.map((option) => {
|
|
276
|
+
const active = sortConfig === option.dir;
|
|
277
|
+
return (_jsxs("button", { type: "button", className: `sort-menu-item${active ? " is-active" : ""}`, onClick: () => {
|
|
278
|
+
setSortConfig(active ? null : option.dir);
|
|
279
|
+
sortMenuRef.current?.removeAttribute("open");
|
|
280
|
+
}, children: [_jsx("span", { className: "sort-menu-item__check", children: active ? "✓" : "" }), option.label] }, option.dir));
|
|
281
|
+
}) })] })] }) }))] })), reviewRow && (_jsxs("div", { className: "results-review-page", children: [_jsxs("div", { className: "results-review-page__top", children: [_jsxs("button", { type: "button", className: "bom-toolbar-btn bom-toolbar-btn--accent", onClick: closeReview, children: [_jsx(BackArrow, {}), "Back to results"] }), _jsxs("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [_jsx("div", { className: "results-review-searchbar", children: _jsx("input", { type: "text", className: "results-search-input", placeholder: "Search products in catalogue...", value: reviewSearch, onChange: (event) => setReviewSearch(event.target.value), "aria-label": `Search a product for ${reviewRow.requirement.label}` }) }), reviewRow.chosen && (_jsx("div", { className: "results-candidate-actions", children: _jsx("button", { type: "button", className: "bom-toolbar-btn", onClick: () => clearChosen(reviewRow.requirement.key), children: "Clear selected product" }) }))] })] }), _jsxs("div", { className: "results-review-page__hero", children: [_jsx("div", { className: "results-review-page__image-wrap", children: _jsx("img", { className: "fill-img results-review-page__image", src: productImageSrc(reviewRow.chosen?.description || reviewRow.requirement.label, reviewRow.chosen?.product_id, reviewRow.chosen?.pdp_photo_url), alt: "" }) }), _jsxs("div", { className: "results-review-page__summary", children: [_jsx("h2", { children: reviewRow.requirement.label }), _jsxs("p", { className: "results-review-page__meta", children: [fmtQty(reviewRow.requirement.qty), " ", renderDisplayUnit(reviewRow.requirement.unit, reviewRow.requirement.qty)] }), reviewRow.chosen ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "results-review-page__product", children: reviewRow.chosen.description }), _jsxs("div", { className: "results-row-meta", children: [reviewRow.chosen.product_id, reviewRow.chosen.price != null ? ` · ${fmtPrice(reviewRow.chosen.price, reviewRow.chosen.price_basis)}` : ""] })] })) : (_jsx("div", { className: "results-empty-hint", children: "No product currently selected for this line." })), _jsx("div", { className: "results-review-page__actions", children: _jsx("button", { type: "button", className: `bom-toolbar-btn${cart?.cart[lineIdFor(reviewRow.requirement.key)] ? " bom-toolbar-btn--added" : " bom-toolbar-btn--accent"}`, onClick: () => toggleInCart(reviewRow), disabled: !reviewRow.chosen || !cart, children: cart?.cart[lineIdFor(reviewRow.requirement.key)] ? "✓ Added" : "+ Add to basket" }) })] })] }), reviewCandidates.length === 0 ? (_jsx("p", { className: "results-empty-hint", children: "No matching products." })) : (_jsxs("table", { className: "score-breakdown-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Product" }), _jsx("th", { children: "Dims (mm)" }), _jsx("th", { children: "Price" }), _jsx("th", { "aria-label": "Actions" })] }) }), _jsx("tbody", { children: reviewCandidates.map((candidate) => {
|
|
282
282
|
const isCurrent = candidate.product_id === reviewRow.chosen?.product_id;
|
|
283
283
|
return (_jsxs("tr", { className: isCurrent ? "score-breakdown-row--best" : "", children: [_jsxs("td", { children: [_jsx("div", { children: candidate.description }), _jsx("div", { className: "results-row-meta", children: candidate.product_id })] }), _jsx("td", { children: _jsx("span", { className: "unit-badge", children: fmtMeasurements(candidate.measurements_mm) }) }), _jsx("td", { children: candidate.price != null ? fmtPrice(candidate.price, candidate.price_basis) : "-" }), _jsx("td", { children: _jsxs("label", { className: "switch-toggle", title: isCurrent ? "Currently selected" : "Use this candidate", children: [_jsx("input", { type: "checkbox", checked: isCurrent, onChange: () => {
|
|
284
284
|
if (isCurrent)
|