Add quote bridge and quote handoff flow

This commit is contained in:
2026-06-29 18:41:45 -05:00
parent be1475dd8e
commit a329168765
17 changed files with 1707 additions and 509 deletions
+148
View File
@@ -10,6 +10,9 @@ let productData = [];
let accessoryData = [];
let bitwiseData = {};
// In-page order list for the current browser session
let orderItems = [];
// Track accumulated bit value for URL state
let accumulatedBitValue = 0;
@@ -625,6 +628,8 @@ function startOver() {
}
// Clear bit value
accumulatedBitValue = 0;
// Clear order list
orderItems = [];
// Clear URL
window.history.replaceState({}, '', window.location.pathname);
loadContent('start');
@@ -916,6 +921,11 @@ function showProductDetail(product) {
<div class="config-field" style="margin-top: 20px;">
<button class="back-button" onclick="addToOrder('${prodCode}')" style="width: 100%; padding: 12px; font-size: 16px;">Add to Order</button>
</div>
<div class="config-field" style="margin-top: 12px;">
<div style="font-weight: 600; margin-bottom: 8px;">Current Order</div>
<div id="order-items-list" style="background: #f8f9fb; border: 1px solid #e3e5ea; border-radius: 8px; padding: 10px;"></div>
<button class="back-button" onclick="submitQuoteOrder()" style="width: 100%; margin-top: 10px; padding: 12px; font-size: 16px;">Quote</button>
</div>
</div>
</div>
<div class="result-details">
@@ -964,6 +974,7 @@ function showProductDetail(product) {
contentDiv.innerHTML = html;
updateBreadcrumb();
renderOrderList();
// Initialize images based on type
if (useCanvasAPI) {
@@ -1328,6 +1339,143 @@ function handleSizeChange(productCode) {
}
}
function getSelectedSizeLabel() {
const sizeSelect = document.getElementById('config-size');
if (!sizeSelect) return '';
if (sizeSelect.value === 'custom') {
const customWidth = document.getElementById('custom-width')?.value || '';
const customHeight = document.getElementById('custom-height')?.value || '';
if (customWidth && customHeight) {
return `Custom ${customWidth}" x ${customHeight}"`;
}
return 'Custom Size';
}
const selectedOption = sizeSelect.options[sizeSelect.selectedIndex];
return selectedOption ? selectedOption.textContent.trim() : '';
}
function buildOrderItemKey(item) {
return [
item.productCode,
item.material,
item.color,
item.size,
item.hingeLocation
].join('|');
}
function addToOrder(productCode) {
const product = productData.find(p => p.productCode === productCode || p.id === productCode);
if (!product) {
showNotification('Unable to add item: product not found.');
return;
}
const material = document.getElementById('config-material')?.value || 'N/A';
const color = document.getElementById('config-color')?.value || 'N/A';
const hingeLocation = document.querySelector('[name="hinge-location"]:checked')?.value || 'left';
const size = getSelectedSizeLabel() || 'N/A';
const item = {
productCode,
description: product.description || product.DESCRIPTION || product.title || productCode,
material,
color,
size,
hingeLocation,
quantity: 1
};
const itemKey = buildOrderItemKey(item);
const existingIndex = orderItems.findIndex(existing => buildOrderItemKey(existing) === itemKey);
if (existingIndex >= 0) {
orderItems[existingIndex].quantity += 1;
} else {
orderItems.push(item);
}
renderOrderList();
showNotification('Added to order.');
}
async function submitQuoteOrder() {
if (!Array.isArray(orderItems) || orderItems.length === 0) {
showNotification('Add at least one item before submitting a quote.');
return;
}
try {
const response = await fetch('/quiz/api/submit-quote', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
items: orderItems,
bitValue: accumulatedBitValue,
answers: userAnswers,
route: window.location.pathname,
url: window.location.href
})
});
const data = await response.json();
if (!response.ok || data.status !== 'success') {
showNotification(data.message || 'Quote submission failed.');
return;
}
const submissionId = data.submissionId || 'unknown';
orderItems = [];
renderOrderList();
showNotification(`Quote submitted (${submissionId}).`);
} catch (error) {
showNotification('Quote submission failed.');
}
}
function removeOrderItem(index) {
if (index < 0 || index >= orderItems.length) return;
orderItems.splice(index, 1);
renderOrderList();
}
function renderOrderList() {
const container = document.getElementById('order-items-list');
if (!container) return;
if (orderItems.length === 0) {
container.innerHTML = '<div style="color: #666; font-size: 0.95em;">No items added yet.</div>';
return;
}
const totalQuantity = orderItems.reduce((total, item) => total + item.quantity, 0);
container.innerHTML = `
<div style="display: grid; gap: 8px;">
${orderItems.map((item, index) => `
<div style="background: #fff; border: 1px solid #dfe3ea; border-radius: 6px; padding: 8px;">
<div style="display: flex; justify-content: space-between; align-items: flex-start; gap: 8px;">
<div>
<div style="font-weight: 600;">${item.productCode} (${item.quantity})</div>
<div style="font-size: 0.9em; color: #444;">${item.description}</div>
<div style="font-size: 0.85em; color: #666; margin-top: 4px;">
Material: ${item.material} | Color: ${item.color}<br>
Size: ${item.size} | Hinge: ${item.hingeLocation}
</div>
</div>
<button class="back-button" onclick="removeOrderItem(${index})" style="padding: 6px 10px; font-size: 0.85em;">Remove</button>
</div>
</div>
`).join('')}
<div style="font-size: 0.9em; color: #333; font-weight: 600; padding-top: 4px;">
Total Items: ${totalQuantity}
</div>
</div>
`;
}
// Go back to results from product detail
function goBackToResults() {
// Find results in history or load it directly