From 813628cecb71baa8c43fab7de71321cde1c6e2d3 Mon Sep 17 00:00:00 2001 From: John Mizerek Date: Thu, 12 Feb 2026 17:13:32 -0800 Subject: [PATCH] Add HTML file upload option for menu import - Backend now accepts either url or html content in request body - Frontend adds HTML file upload option below URL input - Useful when websites block the crawler (403 errors) Co-Authored-By: Claude Opus 4.5 --- api/setup/analyzeMenuUrl.cfm | 70 ++++++++++++++++---------- portal/setup-wizard.html | 97 ++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 25 deletions(-) diff --git a/api/setup/analyzeMenuUrl.cfm b/api/setup/analyzeMenuUrl.cfm index 29ea73e..41691e7 100644 --- a/api/setup/analyzeMenuUrl.cfm +++ b/api/setup/analyzeMenuUrl.cfm @@ -28,37 +28,57 @@ - - - - - - - - - - - + + + + - - - - + + + + + + + + + - - - + + + + - - + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/portal/setup-wizard.html b/portal/setup-wizard.html index 2e75242..6cf36ec 100644 --- a/portal/setup-wizard.html +++ b/portal/setup-wizard.html @@ -808,6 +808,23 @@

Works with most restaurant websites, DoorDash, Yelp, Toast, Square, and more

+ + +
+
+ or upload saved page +
+
+ + +

If the website blocks our crawler, save the menu page and upload it here

+ + @@ -1358,6 +1375,86 @@ switchImportTab('upload'); } + // Handle uploaded HTML file + async function handleHtmlFileUpload(event) { + const file = event.target.files[0]; + if (!file) return; + + // Read the file content + const reader = new FileReader(); + reader.onload = async function(e) { + const htmlContent = e.target.result; + + // Hide upload section, show conversation + document.getElementById('uploadSection').style.display = 'none'; + + addMessage('ai', ` +
+
+ Analyzing saved page: ${file.name}... +
+

Extracting menu data from HTML content.

+ `); + + try { + const response = await fetch(`${config.apiBaseUrl}/setup/analyzeMenuUrl.cfm`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ html: htmlContent }) + }); + + const result = await response.json(); + + if (!result.OK) { + throw new Error(result.MESSAGE || 'Failed to analyze HTML file'); + } + + // Store extracted data + config.extractedData = result.DATA; + + // Log debug info + console.log('=== HTML FILE IMPORT RESPONSE ==='); + console.log('File:', file.name); + console.log('Extracted data:', result.DATA); + if (result.steps) { + console.log('Steps:', result.steps); + } + console.log('================================='); + + // Remove loading message and start conversation flow + document.getElementById('conversation').innerHTML = ''; + + // In add-menu mode, skip business info and header + if (config.businessId && config.menuId) { + showCategoriesStep(); + } else { + showBusinessInfoStep(); + } + + } catch (error) { + console.error('HTML analysis error:', error); + document.getElementById('conversation').innerHTML = ''; + addMessage('ai', ` +

Sorry, I encountered an error analyzing that file:

+

${error.message}

+
+ + +
+ `); + } + }; + + reader.onerror = function() { + showToast('Failed to read file', 'error'); + }; + + reader.readAsText(file); + + // Reset the input so the same file can be selected again + event.target.value = ''; + } + async function startAnalysis() { if (config.uploadedFiles.length === 0) { showToast('Please upload at least one menu image', 'error');