diff --git a/api/setup/uploadSavedPage.cfm b/api/setup/uploadSavedPage.cfm new file mode 100644 index 0000000..9452e70 --- /dev/null +++ b/api/setup/uploadSavedPage.cfm @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + #serializeJSON(response)# + + + + + + + + + + + + + + + + + + #serializeJSON(response)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #serializeJSON(response)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #serializeJSON(response)# + + + + + + + + #serializeJSON(response)# + + diff --git a/portal/setup-wizard.html b/portal/setup-wizard.html index c362be0..80f41b0 100644 --- a/portal/setup-wizard.html +++ b/portal/setup-wizard.html @@ -816,15 +816,16 @@
- -

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

- - +

HTML file or ZIP (Save Page As > Webpage, Complete)

@@ -1385,37 +1386,76 @@ switchImportTab('upload'); } - // Handle uploaded HTML file - async function handleHtmlFileUpload(event) { + // Handle uploaded saved page (HTML or ZIP) + async function handleSavedPageUpload(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; + const isZip = file.name.toLowerCase().endsWith('.zip'); - // Hide upload section, show conversation - document.getElementById('uploadSection').style.display = 'none'; + // Hide upload section, show conversation + document.getElementById('uploadSection').style.display = 'none'; - addMessage('ai', ` -
-
- Analyzing saved page: ${file.name}... -
-

Extracting menu data from HTML content.

- `); + addMessage('ai', ` +
+
+ Analyzing saved page: ${file.name}... +
+

${isZip ? 'Uploading and extracting ZIP file...' : 'Extracting menu data from HTML content.'}

+ `); - let result = null; - try { + let result = null; + try { + if (isZip) { + // Upload ZIP file to server for extraction + const formData = new FormData(); + formData.append('zipFile', file); + + const uploadResponse = await fetch(`${config.apiBaseUrl}/setup/uploadSavedPage.cfm`, { + method: 'POST', + body: formData + }); + + const uploadResult = await uploadResponse.json(); + if (!uploadResult.OK) { + throw new Error(uploadResult.MESSAGE || 'Failed to upload ZIP file'); + } + + console.log('ZIP uploaded, extracted URL:', uploadResult.URL); + + // Update loading message + document.getElementById('conversation').innerHTML = ''; + addMessage('ai', ` +
+
+ Scanning extracted page with browser... +
+

Using Playwright to render the saved page and extract menu data.

+ `); + + // Now analyze the extracted URL with Playwright + const response = await fetch(`${config.apiBaseUrl}/setup/analyzeMenuUrl.cfm`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ url: uploadResult.URL }) + }); + + const responseText = await response.text(); + if (!responseText || responseText.trim().length === 0) { + throw new Error('Server returned empty response'); + } + result = JSON.parse(responseText); + } else { + // Read HTML file content and send directly + const htmlContent = await file.text(); console.log('Sending HTML content, length:', htmlContent.length); + const response = await fetch(`${config.apiBaseUrl}/setup/analyzeMenuUrl.cfm`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ html: htmlContent }) }); - console.log('Response status:', response.status, response.statusText); const responseText = await response.text(); console.log('Raw response (first 500 chars):', responseText.substring(0, 500)); @@ -1429,67 +1469,66 @@ console.error('JSON parse error. Full response:', responseText); throw new Error('Invalid JSON response from server. Check console for details.'); } - - console.log('=== HTML FILE IMPORT RESPONSE ==='); - console.log('File:', file.name); - console.log('Full response:', result); - console.log('================================='); - - if (!result.OK) { - throw new Error(result.MESSAGE || 'Failed to analyze HTML file'); - } - - // Store extracted data - config.extractedData = result.DATA; - - // Store image mappings for matching uploaded images to items - config.imageMappings = result.DATA.imageMappings || []; - console.log('Image mappings from HTML:', config.imageMappings.length); - - // Remove loading message and start conversation flow - document.getElementById('conversation').innerHTML = ''; - - // Check if any items have imageUrl - if so, offer image upload matching - const itemsWithImages = (config.extractedData.items || []).filter(item => item.imageUrl).length; - if (itemsWithImages > 0) { - showImageMatchingStep(); - } else if (config.businessId && config.menuId) { - // In add-menu mode, skip business info and header - showCategoriesStep(); - } else { - showBusinessInfoStep(); - } - - } catch (error) { - console.error('HTML analysis error:', error); - console.error('Full result object:', result); - document.getElementById('conversation').innerHTML = ''; - let debugInfo = ''; - if (result && result.debug) { - debugInfo = `

Debug: hasHtml=${result.debug.hasHtmlKey}, htmlLen=${result.debug.htmlLength}, url="${result.debug.urlValue}"

`; - } - addMessage('ai', ` -

Sorry, I encountered an error analyzing that file:

-

${error.message}

- ${debugInfo} -
- - -
- `); } - }; - reader.onerror = function() { - showToast('Failed to read file', 'error'); - }; + console.log('=== SAVED PAGE IMPORT RESPONSE ==='); + console.log('File:', file.name); + console.log('Full response:', result); + console.log('=================================='); - reader.readAsText(file); + if (!result.OK) { + throw new Error(result.MESSAGE || 'Failed to analyze saved page'); + } + + // Store extracted data + config.extractedData = result.DATA; + + // Store image mappings for matching uploaded images to items + config.imageMappings = result.DATA.imageMappings || []; + console.log('Image mappings from saved page:', config.imageMappings.length); + + // Remove loading message and start conversation flow + document.getElementById('conversation').innerHTML = ''; + + // Check if any items have imageUrl - if so, offer image upload matching + const itemsWithImages = (config.extractedData.items || []).filter(item => item.imageUrl).length; + if (itemsWithImages > 0) { + showImageMatchingStep(); + } else if (config.businessId && config.menuId) { + // In add-menu mode, skip business info and header + showCategoriesStep(); + } else { + showBusinessInfoStep(); + } + + } catch (error) { + console.error('Saved page analysis error:', error); + console.error('Full result object:', result); + document.getElementById('conversation').innerHTML = ''; + let debugInfo = ''; + if (result && result.debug) { + debugInfo = `

Debug: hasHtml=${result.debug.hasHtmlKey}, htmlLen=${result.debug.htmlLength}, url="${result.debug.urlValue}"

`; + } + addMessage('ai', ` +

Sorry, I encountered an error analyzing that file:

+

${error.message}

+ ${debugInfo} +
+ + +
+ `); + } // Reset the input so the same file can be selected again event.target.value = ''; } + // Legacy alias for backwards compatibility + function handleHtmlFileUpload(event) { + handleSavedPageUpload(event); + } + // Show step to upload images from saved webpage subfolder and match to items function showImageMatchingStep() { const itemCount = config.extractedData.items ? config.extractedData.items.length : 0;