Fix sandblast-only toggle overflow and $0 AI quote pricing

Overflow: replaced Bootstrap form-check with an explicit flex row so the
two-line label (title + subtitle) never bleeds outside the card boundary.

$0 pricing: when sandblast-only was toggled on an AI item, manualUnitPrice
was cleared and isAiItem set to false. The pricing engine then returned $0
because no prep services with minutes were configured. Fix: preserve the AI
price when toggling sandblast-only, and keep isAiItem=true so the server
routes through the AI-price path (manualUnitPrice) rather than trying to
recalculate from prep labor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 14:29:03 -04:00
parent 0054b7d108
commit ecb285657a
+12 -11
View File
@@ -1461,10 +1461,12 @@ function aiShowError(message) {
function renderStep3Html() {
const isSandblastOnly = !!wz.data.sandblastOnly;
return `
<div class="form-check form-switch border rounded py-2 px-3 mb-3 bg-light">
<input class="form-check-input" type="checkbox" id="sandblastOnlyToggle"
${isSandblastOnly ? 'checked' : ''} onchange="onSandblastOnlyToggle()">
<label class="form-check-label" for="sandblastOnlyToggle" style="line-height:1.3">
<div class="d-flex align-items-center border rounded py-2 px-3 mb-3 bg-light gap-2">
<div class="form-check form-switch mb-0 flex-shrink-0">
<input class="form-check-input" type="checkbox" id="sandblastOnlyToggle"
${isSandblastOnly ? 'checked' : ''} onchange="onSandblastOnlyToggle()" style="cursor:pointer">
</div>
<label for="sandblastOnlyToggle" class="mb-0" style="line-height:1.3; cursor:pointer;">
<strong>Sandblast / Prep Only</strong>
<span class="d-block text-muted fw-normal small">No powder coating — no oven or powder costs</span>
</label>
@@ -1491,10 +1493,8 @@ function onSandblastOnlyToggle() {
wz.data.sandblastOnly = checked;
if (checked) {
wz.data.coats = [];
// AI price was estimated with coating in mind — clear it so pricing recalculates from prep labor
if (wz.itemType === 'ai') {
wz.data.manualUnitPrice = null;
}
// Keep manualUnitPrice — if the AI returned a price, preserve it as the sandblast price.
// The user can adjust it; clearing it causes a $0 quote when prep services aren't configured.
}
renderStep(3);
}
@@ -2309,9 +2309,10 @@ function preFillStep2() {
function buildItemFromWizard() {
const d = wz.data;
const isSandblastOnly = !!d.sandblastOnly;
// Sandblast-only AI items lose the AI pricing flag — the AI price included coating costs
// that no longer apply, so the server prices from prep labor instead.
const isAi = wz.itemType === 'ai' && !isSandblastOnly;
// AI flag is preserved even for sandblast-only so the server uses the AI price (manualUnitPrice).
// Without this, sandblast-only AI items fall through to the pricing engine and return $0 when
// no prep services with minutes are configured.
const isAi = wz.itemType === 'ai';
return {
description: d.description || null,
quantity: d.quantity || 1,