Fix wizard flow and add detailed modifier view
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 <noreply@anthropic.com>
This commit is contained in:
parent
8999805ec6
commit
fe383f40d0
3 changed files with 78 additions and 22 deletions
|
|
@ -141,7 +141,19 @@
|
|||
</cfif>
|
||||
|
||||
<cfif httpStatusCode NEQ 200>
|
||||
<cfthrow message="Claude API error on image #imgIndex#: #httpResult.statusCode#" detail="#httpResult.fileContent#">
|
||||
<cfset errorDetail = "">
|
||||
<cftry>
|
||||
<cfset errorResponse = deserializeJSON(httpResult.fileContent)>
|
||||
<cfif structKeyExists(errorResponse, "error") AND structKeyExists(errorResponse.error, "message")>
|
||||
<cfset errorDetail = errorResponse.error.message>
|
||||
<cfelse>
|
||||
<cfset errorDetail = httpResult.fileContent>
|
||||
</cfif>
|
||||
<cfcatch>
|
||||
<cfset errorDetail = httpResult.fileContent>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
<cfthrow message="Claude API error on image #imgIndex#: #httpResult.statusCode# - #errorDetail#">
|
||||
</cfif>
|
||||
|
||||
<!--- Parse response --->
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@
|
|||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="login-btn" id="continueBtn">Continue to Portal</button>
|
||||
<button type="submit" class="login-btn" id="continueBtn">Continue</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -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,7 +370,15 @@
|
|||
|
||||
populateBusinessSelect() {
|
||||
const select = document.getElementById('businessSelect');
|
||||
|
||||
if (this.businesses.length === 0) {
|
||||
select.innerHTML = '<option value="">No businesses yet - use wizard below</option>';
|
||||
select.disabled = true;
|
||||
document.getElementById('continueBtn').disabled = true;
|
||||
} else {
|
||||
select.innerHTML = '<option value="">Choose a business...</option>';
|
||||
select.disabled = false;
|
||||
document.getElementById('continueBtn').disabled = false;
|
||||
|
||||
this.businesses.forEach(biz => {
|
||||
const option = document.createElement('option');
|
||||
|
|
@ -382,6 +386,15 @@
|
|||
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');
|
||||
|
|
|
|||
|
|
@ -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 => `
|
||||
<div class="modifier-option-detail">
|
||||
<span class="option-name">${opt.name}</span>
|
||||
<span class="option-price">${opt.price ? `+$${opt.price.toFixed(2)}` : '$0.00'}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Ask user about this modifier
|
||||
const categoryOptions = categories.map((cat, i) => `
|
||||
<label class="category-option">
|
||||
|
|
@ -1656,6 +1666,18 @@
|
|||
|
||||
addMessage('ai', `
|
||||
<p>I found the modifier template <strong>"${modifier.name}"</strong> but I'm not sure which items it applies to.</p>
|
||||
<div class="modifier-details-view" style="background: var(--gray-50); border: 1px solid var(--gray-200); border-radius: 8px; padding: 16px; margin: 16px 0;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
|
||||
<span style="font-weight: 500;">${modifier.name}</span>
|
||||
<span class="source-badge">${sourceImg}</span>
|
||||
</div>
|
||||
<div style="color: var(--gray-600); font-size: 14px; margin-bottom: 8px;">
|
||||
<strong>${optionsCount} option${optionsCount !== 1 ? 's' : ''}</strong> • ${modifier.required ? 'Required' : 'Optional'}
|
||||
</div>
|
||||
<div class="modifier-options-list">
|
||||
${optionsList}
|
||||
</div>
|
||||
</div>
|
||||
<p>Select the categories where this modifier should be applied, or skip if it doesn't apply automatically:</p>
|
||||
<div class="category-selection" style="display: flex; flex-direction: column; gap: 8px; margin: 16px 0;">
|
||||
${categoryOptions}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue