@metron-labs/quote-react 0.1.68 → 0.1.69
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.
package/dist/app/QuickQuote.js
CHANGED
|
@@ -62,7 +62,9 @@ function QuickQuoteInner({ fullPage = false, hideHeader = false, internal = fals
|
|
|
62
62
|
catch { }
|
|
63
63
|
// Older than the TTL (or saved before there was a stamp): start fresh. The save effect
|
|
64
64
|
// below then removes it, as scheduleResult stays null.
|
|
65
|
-
if (
|
|
65
|
+
if (process.env.NODE_ENV === "development")
|
|
66
|
+
saved = null;
|
|
67
|
+
else if (saved && !(Date.now() - (saved.savedAt ?? 0) < SAVED_QUOTE_TTL_MS))
|
|
66
68
|
saved = null;
|
|
67
69
|
if (saved?.scheduleResult) {
|
|
68
70
|
setEntryMode("schedule");
|
|
@@ -121,11 +121,6 @@ export function refreshedCartItem(refreshed, existing) {
|
|
|
121
121
|
conversionNotes: existing.conversionNotes,
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
|
-
function classificationLabel(value) {
|
|
125
|
-
if (!value)
|
|
126
|
-
return "not stated";
|
|
127
|
-
return value.replace(/_/g, " ");
|
|
128
|
-
}
|
|
129
124
|
function ConfChip({ band }) {
|
|
130
125
|
return (_jsx("span", { className: `status-dot status-dot--${band}`, title: band, children: _jsx("span", { className: "status-dot__mark" }) }));
|
|
131
126
|
}
|
|
@@ -288,10 +283,10 @@ function AdjudicationDebugBlock({ result }) {
|
|
|
288
283
|
];
|
|
289
284
|
return (_jsxs("div", { className: "results-detail-block", children: [_jsx("h4", { style: { margin: "0 0 12px" }, children: "Adjudication context" }), _jsx("dl", { className: "results-detail-grid", children: details.map(([label, value]) => (_jsxs(Fragment, { children: [_jsx("dt", { children: label }), _jsx("dd", { children: value })] }, label))) }), debug.rerank_line_item_input ? (_jsxs(_Fragment, { children: [_jsx("h5", { style: { margin: "12px 0 8px" }, children: "Rerank input" }), _jsx("pre", { className: "results-pre", children: JSON.stringify(debug.rerank_line_item_input, null, 2) })] })) : null] }));
|
|
290
285
|
}
|
|
291
|
-
function DetailedDecision({ item, decision, result, showRetrievalDebug, reviewOnly = false, linkedGroupMeta, }) {
|
|
286
|
+
function DetailedDecision({ item, decision, selectedProductId, result, showRetrievalDebug, reviewOnly = false, linkedGroupMeta, }) {
|
|
292
287
|
if (!decision)
|
|
293
288
|
return null;
|
|
294
|
-
const reviewLabel =
|
|
289
|
+
const reviewLabel = selectedProductId == null ? "Needs review:" : "Selected:";
|
|
295
290
|
if (reviewOnly) {
|
|
296
291
|
return decision.reason ? (_jsxs("div", { className: "results-detail-block", children: [_jsxs("p", { className: `basket-calc-text basket-calc-text--review`, children: [_jsx("strong", { children: reviewLabel }), "\u00A0", decision.reason] }), hasVisibleLinkedGroup(item) ? (_jsxs("p", { className: "results-row-meta", style: { marginTop: 8 }, children: ["Linked context: ", linkedGroupMeta ? `Link ${linkedGroupMeta.token} - ` : "", linkedGroupSummary(item)] })) : null] })) : null;
|
|
297
292
|
}
|
|
@@ -305,7 +300,7 @@ function CandidateBreakdown({ item, candidates, result, decision, unmatchable, s
|
|
|
305
300
|
// Metron Labs' own tooling — only surfaced under the `debug` config's detail mode.
|
|
306
301
|
const showBusinessDetail = internal || detailMode === "detailed";
|
|
307
302
|
const showScoreColumns = debugEnabled && detailMode === "detailed";
|
|
308
|
-
const detailBlock = showBusinessDetail || forceReviewNote ? (_jsx(DetailedDecision, { item: item, decision: decision, result: result, showRetrievalDebug: showScoreColumns, reviewOnly: forceReviewNote && !showBusinessDetail, linkedGroupMeta: linkedGroupMeta })) : null;
|
|
303
|
+
const detailBlock = showBusinessDetail || forceReviewNote ? (_jsx(DetailedDecision, { item: item, decision: decision, selectedProductId: selectedProductId, result: result, showRetrievalDebug: showScoreColumns, reviewOnly: forceReviewNote && !showBusinessDetail, linkedGroupMeta: linkedGroupMeta })) : null;
|
|
309
304
|
if (unmatchable) {
|
|
310
305
|
return (_jsxs(_Fragment, { children: [detailBlock, _jsx("p", { className: "results-empty-hint", children: "Classified as labour/plant or referencing another line item - skipped by design." })] }));
|
|
311
306
|
}
|
|
@@ -390,7 +385,7 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
390
385
|
let totalValue = hasPendingReview ? null : getCartSubtotal(Object.values(cart), { wasteFactorEnabled: false, wasteFactorOverride: 0 });
|
|
391
386
|
let needsPricingCount = 0;
|
|
392
387
|
for (const row of rows) {
|
|
393
|
-
if (row.unmatchable ||
|
|
388
|
+
if (row.unmatchable || !row.best)
|
|
394
389
|
continue;
|
|
395
390
|
const cartItem = cart[row.item.line_id];
|
|
396
391
|
if (!cartItem || getPricingBreakdown(cartItem).lineTotal == null)
|
|
@@ -399,7 +394,7 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
399
394
|
if (needsPricingCount > 0)
|
|
400
395
|
totalValue = null;
|
|
401
396
|
const totalLabel = formatResultsTotal(totalValue, needsPricingCount);
|
|
402
|
-
const hasBestAlternative = (row) => !row.unmatchable && !row.best &&
|
|
397
|
+
const hasBestAlternative = (row) => !row.unmatchable && !row.best && recommendedCandidates(row.result).length > 0;
|
|
403
398
|
const previewCandidate = (row) => row.best ?? recommendedCandidates(row.result)[0];
|
|
404
399
|
const reviewRequiredRows = rows.filter((row) => !row.unmatchable && !row.best);
|
|
405
400
|
const pendingReviewRows = reviewRequiredRows.filter((row) => !skippedReviewIds.has(row.item.line_id));
|
|
@@ -417,9 +412,8 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
417
412
|
const currentPage = Math.min(page, totalPages);
|
|
418
413
|
const pagedRows = visibleRows.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE);
|
|
419
414
|
const matchedTileRows = pagedRows.filter((row) => !row.unmatchable && row.best);
|
|
420
|
-
const
|
|
421
|
-
const
|
|
422
|
-
const unmatchedTileRows = pagedRows.filter((row) => row.unmatchable || (!row.best && !hasBestAlternative(row)));
|
|
415
|
+
const matchedDisplayRows = pagedRows.filter((row) => !row.unmatchable && row.best);
|
|
416
|
+
const unmatchedTileRows = pagedRows.filter((row) => row.unmatchable || !row.best);
|
|
423
417
|
const activeReviewRow = selectedReviewId ? (rows.find((row) => row.item.line_id === selectedReviewId) ?? null) : null;
|
|
424
418
|
const reviewGateOpen = !internal && !reviewGateComplete && !activeReviewRow && pendingReviewRows.length > 0;
|
|
425
419
|
const sortMenuRef = useRef(null);
|
|
@@ -533,7 +527,7 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
533
527
|
else
|
|
534
528
|
closeReview(true);
|
|
535
529
|
};
|
|
536
|
-
const unmatchedSection = unmatchedTileRows.length > 0 ? (_jsxs("section", { className: "results-tile-section results-tile-section--unmatched", "aria-label": "Items
|
|
530
|
+
const unmatchedSection = unmatchedTileRows.length > 0 ? (_jsxs("section", { className: "results-tile-section results-tile-section--unmatched", "aria-label": "Items needing review", children: [_jsxs("div", { className: "results-tile-section__head", children: [_jsx("h3", { children: "Needs review" }), _jsx("p", { children: "We couldn\u2019t confidently match these to a product. Open one to pick from the closest options." })] }), _jsx("div", { className: "results-unmatched-list", children: unmatchedTileRows.map((row) => {
|
|
537
531
|
const { item, qty } = row;
|
|
538
532
|
return (_jsxs("button", { type: "button", className: "results-unmatched-card", onClick: () => openReview(item.line_id), children: [_jsx("span", { className: "results-unmatched-card__main", children: _jsx("span", { className: "results-unmatched-card__title", children: displayLineLabel(item, lineItems) }) }), _jsx("span", { className: "bom-toolbar-btn results-unmatched-card__prompt", children: "Review" })] }, item.line_id));
|
|
539
533
|
}) })] })) : null;
|
|
@@ -564,7 +558,9 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
564
558
|
})));
|
|
565
559
|
}
|
|
566
560
|
function toCartItem(row) {
|
|
567
|
-
const candidate = row.best
|
|
561
|
+
const candidate = row.best;
|
|
562
|
+
if (!candidate)
|
|
563
|
+
throw new Error("Cannot add a line item without a selected product.");
|
|
568
564
|
return candidateToCartItem(candidate, {
|
|
569
565
|
lineId: row.item.line_id,
|
|
570
566
|
description: row.item.description_original,
|
|
@@ -589,8 +585,8 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
589
585
|
: undefined,
|
|
590
586
|
});
|
|
591
587
|
}
|
|
592
|
-
// All
|
|
593
|
-
//
|
|
588
|
+
// All selected items land in the basket automatically so the common case starts from
|
|
589
|
+
// a complete draft basket. Gated by cartSeeded (lifted
|
|
594
590
|
// to BoQApp) so this fires once per schedule result, not on every remount of this stage.
|
|
595
591
|
useEffect(() => {
|
|
596
592
|
if (cartSeeded)
|
|
@@ -649,8 +645,8 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
649
645
|
const cartCount = Object.keys(cart).length;
|
|
650
646
|
const reviewQuery = reviewSearch.trim();
|
|
651
647
|
const llmReviewCandidates = recommendedCandidates(activeReviewRow?.result);
|
|
652
|
-
const hasNoSuitableCandidate = activeReviewRow?.
|
|
653
|
-
const hasAiVerdict = activeReviewRow?.
|
|
648
|
+
const hasNoSuitableCandidate = !!activeReviewRow && !activeReviewRow.best && (activeReviewRow.result?.candidates.length === 0 || activeReviewRow.result?.rerank?.selectedCandidateId === null);
|
|
649
|
+
const hasAiVerdict = activeReviewRow?.result?.rerank != null;
|
|
654
650
|
const activeReviewReason = activeReviewRow?.decision?.reason ?? (hasNoSuitableCandidate ? "No catalogue candidate found. Refine the request or search the catalogue." : null);
|
|
655
651
|
const catalogueSearchCandidates = catalogueSearchResults.map((candidate) => ({
|
|
656
652
|
product_id: candidate.product_id,
|
|
@@ -749,22 +745,13 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
749
745
|
}, children: _jsx("span", { children: activeReviewCartMatches ? `Remove from ${customerCollection}` : `Add selected product to ${customerCollection}` }) }))) : null, reviewFlowActive ? (_jsx("button", { type: "button", className: "cta-btn cta-btn-secondary customer-review-skip", onClick: () => skipReviewAndReturnToGate(activeReviewRow.item.line_id), children: "Skip this item" })) : null] })) })] }) })) : null, _jsxs("div", { className: "results-tile-layout", children: [visibleRows.length === 0 ? _jsx("div", { className: "results-empty-card", children: "No line items match the current search/filter." }) : null, matchedDisplayRows.length > 0 ? (_jsx("section", { className: "results-tile-section", "aria-label": "Matched and review items", children: _jsx("div", { className: "results-tile-grid", children: matchedDisplayRows.map((row) => {
|
|
750
746
|
const { item, best, qty } = row;
|
|
751
747
|
const preview = previewCandidate(row);
|
|
752
|
-
|
|
753
|
-
return (_jsxs("article", { className: "results-tile-card", children: [_jsxs("button", { type: "button", className: "results-tile-card__surface", onClick: () => openReview(item.line_id), "aria-label": `Review match for ${displayLineLabel(item, lineItems) || "item"}`, children: [_jsxs("div", { className: "results-tile-card__media", children: [isPartial || hasVisibleLinkedGroup(item) ? (_jsxs("div", { className: "results-tile-card__badges", children: [hasVisibleLinkedGroup(item) ? _jsx(LinkedGroupPill, { meta: linkedGroupMetaById.get(item.linked_group_id) }) : _jsx("span", {}), isPartial ? (_jsx("span", { style: {
|
|
754
|
-
padding: "6px 10px",
|
|
755
|
-
borderRadius: 999,
|
|
756
|
-
background: "#fef3c7",
|
|
757
|
-
color: "#92400e",
|
|
758
|
-
fontSize: 12,
|
|
759
|
-
fontWeight: 700,
|
|
760
|
-
lineHeight: 1,
|
|
761
|
-
}, children: "Partially matched" })) : null] })) : null, _jsx(ProductImage, { className: "fill-img results-tile-card__image", description: preview?.description || item.description_original, photoUrl: preview?.pdp_photo_url })] }), _jsxs("div", { className: "results-tile-card__body", children: [preview ? (_jsx(_Fragment, { children: _jsx("div", { className: "results-tile-card__heading", children: _jsx("div", { className: "results-tile-card__title", children: preview.description }) }) })) : (_jsx("div", { className: "results-empty-hint", children: "No product currently selected for this line item." })), _jsx("div", { className: "results-tile-card__match", children: preview ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "results-tile-card__match-label", children: isPartial ? "↳ partially matched from" : "↳ matched from" }), _jsx("div", { className: "results-tile-card__match-name", children: displayLineLabel(item, lineItems) }), hasVisibleLinkedGroup(item) ? _jsx("div", { className: "results-row-meta", children: linkedGroupSummary(item) }) : null] })) : (_jsx("span", { className: "results-empty-hint", children: "No matched product selected." })) })] }), _jsx("span", { className: "results-tile-card__hover-prompt", "aria-hidden": "true", children: "Click to view options" })] }), _jsxs("div", { className: "results-tile-card__actions", children: [internal && (_jsx("button", { type: "button", className: "bom-toolbar-btn", onClick: () => openReview(item.line_id), children: "Review" })), _jsx("button", { type: "button", className: `bom-toolbar-btn${cart[item.line_id] ? " bom-toolbar-btn--added" : " bom-toolbar-btn--accent"}`, onClick: () => {
|
|
748
|
+
return (_jsxs("article", { className: "results-tile-card", children: [_jsxs("button", { type: "button", className: "results-tile-card__surface", onClick: () => openReview(item.line_id), "aria-label": `Review match for ${displayLineLabel(item, lineItems) || "item"}`, children: [_jsxs("div", { className: "results-tile-card__media", children: [hasVisibleLinkedGroup(item) ? (_jsx("div", { className: "results-tile-card__badges", children: _jsx(LinkedGroupPill, { meta: linkedGroupMetaById.get(item.linked_group_id) }) })) : null, _jsx(ProductImage, { className: "fill-img results-tile-card__image", description: preview?.description || item.description_original, photoUrl: preview?.pdp_photo_url })] }), _jsxs("div", { className: "results-tile-card__body", children: [preview ? (_jsx(_Fragment, { children: _jsx("div", { className: "results-tile-card__heading", children: _jsx("div", { className: "results-tile-card__title", children: preview.description }) }) })) : (_jsx("div", { className: "results-empty-hint", children: "No product currently selected for this line item." })), _jsx("div", { className: "results-tile-card__match", children: preview ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "results-tile-card__match-label", children: "\u21B3 matched from" }), _jsx("div", { className: "results-tile-card__match-name", children: displayLineLabel(item, lineItems) }), hasVisibleLinkedGroup(item) ? _jsx("div", { className: "results-row-meta", children: linkedGroupSummary(item) }) : null] })) : (_jsx("span", { className: "results-empty-hint", children: "No matched product selected." })) })] }), _jsx("span", { className: "results-tile-card__hover-prompt", "aria-hidden": "true", children: "Click to view options" })] }), _jsxs("div", { className: "results-tile-card__actions", children: [internal && (_jsx("button", { type: "button", className: "bom-toolbar-btn", onClick: () => openReview(item.line_id), children: "Review" })), _jsx("button", { type: "button", className: `bom-toolbar-btn${cart[item.line_id] ? " bom-toolbar-btn--added" : " bom-toolbar-btn--accent"}`, onClick: () => {
|
|
762
749
|
if (cart[item.line_id])
|
|
763
750
|
onRemoveFromCart(item.line_id);
|
|
764
751
|
else {
|
|
765
752
|
onAddToCart(toCartItem(row));
|
|
766
753
|
}
|
|
767
|
-
}, disabled: !
|
|
754
|
+
}, disabled: !best, "aria-label": cart[item.line_id]
|
|
768
755
|
? internal
|
|
769
756
|
? "Remove from selection"
|
|
770
757
|
: `Remove from ${customerCollection}`
|
|
@@ -784,7 +771,7 @@ export default function ScheduleResultsStage({ scheduleResult, onStartOver, cart
|
|
|
784
771
|
alignItems: "center",
|
|
785
772
|
gap: 8,
|
|
786
773
|
marginTop: 4,
|
|
787
|
-
}, children: [_jsx(LinkedGroupPill, { meta: linkedGroupMetaById.get(item.linked_group_id) }), _jsx("div", { className: "results-row-meta", style: { marginTop: 0 }, children: linkedGroupSummary(item) })] })) : null] }), _jsx("td", { children: best ? (_jsxs("div", { children: [_jsx("div", { className: "results-match-cell-text", title: best.description, children: best.description }), detailMode === "detailed" && decision
|
|
774
|
+
}, children: [_jsx(LinkedGroupPill, { meta: linkedGroupMetaById.get(item.linked_group_id) }), _jsx("div", { className: "results-row-meta", style: { marginTop: 0 }, children: linkedGroupSummary(item) })] })) : null] }), _jsx("td", { children: best ? (_jsxs("div", { children: [_jsx("div", { className: "results-match-cell-text", title: best.description, children: best.description }), detailMode === "detailed" && decision && (_jsx("div", { className: "results-row-meta", children: decision.selected_candidate_id === best.product_id ? "Automatically selected" : "Selected" }))] })) : (_jsx("span", { className: "results-empty-hint" })) }), _jsx("td", { style: { textAlign: "center" }, children: best?.price != null ? fmtPrice(best.price, best.price_basis) : "-" }), detailMode === "simple" && (_jsx("td", { style: { textAlign: "center" }, children: _jsx("button", { type: "button", className: `bom-toolbar-btn${cart[item.line_id] ? " bom-toolbar-btn--added" : " bom-toolbar-btn--accent"}`, style: { width: 84, justifyContent: "center" }, onClick: (event) => {
|
|
788
775
|
event.stopPropagation();
|
|
789
776
|
if (cart[item.line_id])
|
|
790
777
|
onRemoveFromCart(item.line_id);
|