From fe383f40d05c12ba60a7461bb4e619a75c77544a Mon Sep 17 00:00:00 2001 From: John Mizerek Date: Thu, 15 Jan 2026 16:53:09 -0800 Subject: [PATCH] Fix wizard flow and add detailed modifier view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major Changes: 1. Fixed infinite loop in wizard flow - uncertain modifiers step now correctly advances to final review instead of looping back to items 2. Moved uncertain modifier assignment to AFTER items review (makes more sense for user to see items first) 3. Added detailed modifier visualization on uncertain modifiers step showing: - Source image indicator (which image the modifier was extracted from) - Full list of all options with prices - Required/optional status - Option count summary Technical Details: - Backend: Added sourceImageIndex tracking in analyzeMenuImages.cfm to record which image each modifier came from - Frontend: Enhanced uncertain modifiers step with inline detailed view showing complete modifier structure - Flow correction: showUncertainModifiersStep() now calls showFinalStep() instead of showItemsStep() to prevent loop - Improved error handling in API calls with detailed error messages from Claude API Flow Changes: - Old: Upload → Business → Categories → Modifiers → Uncertain Modifiers → Items → [LOOP] - New: Upload → Business → Categories → Modifiers → Items → Uncertain Modifiers → Final Review Model Configuration: - Using claude-sonnet-4-20250514 for menu image analysis - Added better error reporting to surface API issues (auth, credits, etc.) Co-Authored-By: Claude Sonnet 4.5 --- api/setup/analyzeMenuImages.cfm | 14 +++++++- portal/login.html | 58 +++++++++++++++++++++++---------- portal/setup-wizard.html | 28 ++++++++++++++-- 3 files changed, 78 insertions(+), 22 deletions(-) diff --git a/api/setup/analyzeMenuImages.cfm b/api/setup/analyzeMenuImages.cfm index 40e8052..92b7489 100644 --- a/api/setup/analyzeMenuImages.cfm +++ b/api/setup/analyzeMenuImages.cfm @@ -141,7 +141,19 @@ - + + + + + + + + + + + + + diff --git a/portal/login.html b/portal/login.html index 3cb774f..df52202 100644 --- a/portal/login.html +++ b/portal/login.html @@ -218,7 +218,7 @@ - + @@ -311,16 +311,12 @@ // Load user's businesses await this.loadBusinesses(); - if (this.businesses.length === 1) { - // Auto-select if only one business - this.selectBusinessById(this.businesses[0].BusinessID); - } else if (this.businesses.length > 1) { - // Show business selection - this.showStep('business'); + if (this.businesses.length === 0) { + // No businesses - go directly to wizard + this.startNewRestaurant(); } else { - // No businesses - show error - errorEl.textContent = 'No businesses associated with this account.'; - errorEl.classList.add('show'); + // Show business selection (even if just one, so they can access wizard) + this.showStep('business'); } } else { errorEl.textContent = data.ERROR || data.MESSAGE || 'Invalid credentials'; @@ -374,14 +370,31 @@ populateBusinessSelect() { const select = document.getElementById('businessSelect'); - select.innerHTML = ''; - this.businesses.forEach(biz => { - const option = document.createElement('option'); - option.value = biz.BusinessID; - option.textContent = biz.BusinessName; - select.appendChild(option); - }); + if (this.businesses.length === 0) { + select.innerHTML = ''; + select.disabled = true; + document.getElementById('continueBtn').disabled = true; + } else { + select.innerHTML = ''; + select.disabled = false; + document.getElementById('continueBtn').disabled = false; + + this.businesses.forEach(biz => { + const option = document.createElement('option'); + option.value = biz.BusinessID; + option.textContent = biz.BusinessName; + select.appendChild(option); + }); + + // Add "New Business Wizard" option at the end + const wizardOption = document.createElement('option'); + wizardOption.value = 'NEW_WIZARD'; + wizardOption.textContent = '✨ New Business Wizard'; + wizardOption.style.fontWeight = 'bold'; + wizardOption.style.color = 'var(--primary)'; + select.appendChild(wizardOption); + } }, showStep(step) { @@ -391,7 +404,9 @@ selectBusiness() { const businessId = document.getElementById('businessSelect').value; - if (businessId) { + if (businessId === 'NEW_WIZARD') { + this.startNewRestaurant(); + } else if (businessId) { this.selectBusinessById(businessId); } }, @@ -401,6 +416,13 @@ window.location.href = BASE_PATH + `/portal/index.html?bid=${businessId}`; }, + startNewRestaurant() { + // Clear any existing business selection + localStorage.removeItem('payfrit_portal_business'); + // Redirect to wizard without businessId + window.location.href = BASE_PATH + `/portal/setup-wizard.html`; + }, + logout() { localStorage.removeItem('payfrit_portal_token'); localStorage.removeItem('payfrit_portal_userid'); diff --git a/portal/setup-wizard.html b/portal/setup-wizard.html index 7d1ca0e..02f46a8 100644 --- a/portal/setup-wizard.html +++ b/portal/setup-wizard.html @@ -1624,8 +1624,8 @@ ); if (uncertainModifiers.length === 0 || categories.length === 0) { - // No uncertain modifiers or no categories, skip to items - showItemsStep(); + // No uncertain modifiers or no categories, skip to final review + showFinalStep(); return; } @@ -1640,12 +1640,22 @@ if (currentIndex >= uncertainModifiers.length) { // All uncertain modifiers have been processed, apply assignments and continue applyUncertainModifierAssignments(); - showItemsStep(); + showFinalStep(); return; } const modifier = uncertainModifiers[currentIndex]; + // Build detailed modifier view + const sourceImg = modifier.sourceImageIndex ? `Image ${modifier.sourceImageIndex}` : 'Unknown source'; + const optionsCount = (modifier.options || []).length; + const optionsList = (modifier.options || []).filter(opt => opt && opt.name).map(opt => ` +
+ ${opt.name} + ${opt.price ? `+$${opt.price.toFixed(2)}` : '$0.00'} +
+ `).join(''); + // Ask user about this modifier const categoryOptions = categories.map((cat, i) => `