Fix wizard: header preview uses cover, add image matching step after HTML import
This commit is contained in:
parent
a1b557cdc7
commit
112f343ecf
2 changed files with 149 additions and 15 deletions
|
|
@ -633,7 +633,7 @@
|
||||||
<div>
|
<div>
|
||||||
<label style="display: block; margin-bottom: 8px; font-weight: 500;">Header Image</label>
|
<label style="display: block; margin-bottom: 8px; font-weight: 500;">Header Image</label>
|
||||||
<p style="color: #666; font-size: 13px; margin-bottom: 8px;">Displayed at the top of your menu. Recommended: 1200x400px</p>
|
<p style="color: #666; font-size: 13px; margin-bottom: 8px;">Displayed at the top of your menu. Recommended: 1200x400px</p>
|
||||||
<div id="headerPreview" style="width: 100%; height: 120px; background: #fff; border-radius: 8px; margin-bottom: 8px; background-size: contain; background-position: center; background-repeat: no-repeat; border: 1px solid #ddd;"></div>
|
<div id="headerPreview" style="width: 100%; height: 120px; background: #fff; border-radius: 8px; margin-bottom: 8px; background-size: cover; background-position: center; background-repeat: no-repeat; border: 1px solid #ddd;"></div>
|
||||||
<button class="btn btn-secondary" onclick="Portal.uploadHeader()" style="width: 100%;">
|
<button class="btn btn-secondary" onclick="Portal.uploadHeader()" style="width: 100%;">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 8px;">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 8px;">
|
||||||
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/>
|
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/>
|
||||||
|
|
|
||||||
|
|
@ -1049,7 +1049,9 @@
|
||||||
items: []
|
items: []
|
||||||
},
|
},
|
||||||
currentStep: 1,
|
currentStep: 1,
|
||||||
imageObjectUrls: [] // Store object URLs for uploaded images
|
imageObjectUrls: [], // Store object URLs for uploaded images
|
||||||
|
imageMappings: [], // For matching uploaded images to items (from HTML import)
|
||||||
|
itemImages: {} // item ID -> File object for matched images
|
||||||
};
|
};
|
||||||
|
|
||||||
// Image preview functions
|
// Image preview functions
|
||||||
|
|
@ -1328,11 +1330,15 @@
|
||||||
config.extractedData = result.DATA;
|
config.extractedData = result.DATA;
|
||||||
config.sourceUrl = result.sourceUrl;
|
config.sourceUrl = result.sourceUrl;
|
||||||
|
|
||||||
|
// Store image mappings for matching uploaded images to items
|
||||||
|
config.imageMappings = result.DATA.imageMappings || [];
|
||||||
|
|
||||||
// Log debug info
|
// Log debug info
|
||||||
console.log('=== URL IMPORT RESPONSE ===');
|
console.log('=== URL IMPORT RESPONSE ===');
|
||||||
console.log('Source URL:', result.sourceUrl);
|
console.log('Source URL:', result.sourceUrl);
|
||||||
console.log('Pages processed:', result.pagesProcessed);
|
console.log('Pages processed:', result.pagesProcessed);
|
||||||
console.log('Images found:', result.imagesFound);
|
console.log('Images found:', result.imagesFound);
|
||||||
|
console.log('Image mappings:', config.imageMappings.length);
|
||||||
console.log('Extracted data:', result.DATA);
|
console.log('Extracted data:', result.DATA);
|
||||||
if (result.steps) {
|
if (result.steps) {
|
||||||
console.log('Steps:', result.steps);
|
console.log('Steps:', result.steps);
|
||||||
|
|
@ -1342,8 +1348,11 @@
|
||||||
// Remove loading message and start conversation flow
|
// Remove loading message and start conversation flow
|
||||||
document.getElementById('conversation').innerHTML = '';
|
document.getElementById('conversation').innerHTML = '';
|
||||||
|
|
||||||
// In add-menu mode, skip business info and header
|
// If we have image mappings from the HTML, show image matching step first
|
||||||
if (config.businessId && config.menuId) {
|
if (config.imageMappings.length > 0) {
|
||||||
|
showImageMatchingStep();
|
||||||
|
} else if (config.businessId && config.menuId) {
|
||||||
|
// In add-menu mode, skip business info and header
|
||||||
showCategoriesStep();
|
showCategoriesStep();
|
||||||
} else {
|
} else {
|
||||||
showBusinessInfoStep();
|
showBusinessInfoStep();
|
||||||
|
|
@ -1396,14 +1405,34 @@
|
||||||
<p style="font-size:13px;color:var(--gray-500);margin-top:8px;">Extracting menu data from HTML content.</p>
|
<p style="font-size:13px;color:var(--gray-500);margin-top:8px;">Extracting menu data from HTML content.</p>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
let result = null;
|
||||||
try {
|
try {
|
||||||
|
console.log('Sending HTML content, length:', htmlContent.length);
|
||||||
const response = await fetch(`${config.apiBaseUrl}/setup/analyzeMenuUrl.cfm`, {
|
const response = await fetch(`${config.apiBaseUrl}/setup/analyzeMenuUrl.cfm`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ html: htmlContent })
|
body: JSON.stringify({ html: htmlContent })
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
console.log('Response status:', response.status, response.statusText);
|
||||||
|
const responseText = await response.text();
|
||||||
|
console.log('Raw response (first 500 chars):', responseText.substring(0, 500));
|
||||||
|
|
||||||
|
if (!responseText || responseText.trim().length === 0) {
|
||||||
|
throw new Error('Server returned empty response');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = JSON.parse(responseText);
|
||||||
|
} catch (parseErr) {
|
||||||
|
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) {
|
if (!result.OK) {
|
||||||
throw new Error(result.MESSAGE || 'Failed to analyze HTML file');
|
throw new Error(result.MESSAGE || 'Failed to analyze HTML file');
|
||||||
|
|
@ -1412,20 +1441,18 @@
|
||||||
// Store extracted data
|
// Store extracted data
|
||||||
config.extractedData = result.DATA;
|
config.extractedData = result.DATA;
|
||||||
|
|
||||||
// Log debug info
|
// Store image mappings for matching uploaded images to items
|
||||||
console.log('=== HTML FILE IMPORT RESPONSE ===');
|
config.imageMappings = result.DATA.imageMappings || [];
|
||||||
console.log('File:', file.name);
|
console.log('Image mappings from HTML:', config.imageMappings.length);
|
||||||
console.log('Extracted data:', result.DATA);
|
|
||||||
if (result.steps) {
|
|
||||||
console.log('Steps:', result.steps);
|
|
||||||
}
|
|
||||||
console.log('=================================');
|
|
||||||
|
|
||||||
// Remove loading message and start conversation flow
|
// Remove loading message and start conversation flow
|
||||||
document.getElementById('conversation').innerHTML = '';
|
document.getElementById('conversation').innerHTML = '';
|
||||||
|
|
||||||
// In add-menu mode, skip business info and header
|
// If we have image mappings from the HTML, show image matching step first
|
||||||
if (config.businessId && config.menuId) {
|
if (config.imageMappings.length > 0) {
|
||||||
|
showImageMatchingStep();
|
||||||
|
} else if (config.businessId && config.menuId) {
|
||||||
|
// In add-menu mode, skip business info and header
|
||||||
showCategoriesStep();
|
showCategoriesStep();
|
||||||
} else {
|
} else {
|
||||||
showBusinessInfoStep();
|
showBusinessInfoStep();
|
||||||
|
|
@ -1433,10 +1460,16 @@
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('HTML analysis error:', error);
|
console.error('HTML analysis error:', error);
|
||||||
|
console.error('Full result object:', result);
|
||||||
document.getElementById('conversation').innerHTML = '';
|
document.getElementById('conversation').innerHTML = '';
|
||||||
|
let debugInfo = '';
|
||||||
|
if (result && result.debug) {
|
||||||
|
debugInfo = `<p style="font-size:12px;color:var(--gray-500);margin-top:8px;">Debug: hasHtml=${result.debug.hasHtmlKey}, htmlLen=${result.debug.htmlLength}, url="${result.debug.urlValue}"</p>`;
|
||||||
|
}
|
||||||
addMessage('ai', `
|
addMessage('ai', `
|
||||||
<p>Sorry, I encountered an error analyzing that file:</p>
|
<p>Sorry, I encountered an error analyzing that file:</p>
|
||||||
<p style="color: var(--danger);">${error.message}</p>
|
<p style="color: var(--danger);">${error.message}</p>
|
||||||
|
${debugInfo}
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<button class="btn btn-primary" onclick="retryUrlAnalysis()">Try Again</button>
|
<button class="btn btn-primary" onclick="retryUrlAnalysis()">Try Again</button>
|
||||||
<button class="btn btn-secondary" onclick="switchToFileUpload()">Upload Images Instead</button>
|
<button class="btn btn-secondary" onclick="switchToFileUpload()">Upload Images Instead</button>
|
||||||
|
|
@ -1455,6 +1488,107 @@
|
||||||
event.target.value = '';
|
event.target.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
const mappingCount = config.imageMappings.length;
|
||||||
|
|
||||||
|
addMessage('ai', `
|
||||||
|
<p>I found <strong>${itemCount} menu items</strong> and detected <strong>${mappingCount} image references</strong> in the HTML.</p>
|
||||||
|
<p>To add images to your menu items, upload the images from the saved webpage folder (usually named something like <code>pagename_files</code>).</p>
|
||||||
|
<p style="font-size:13px;color:var(--gray-500);">I'll automatically match images to items based on filenames.</p>
|
||||||
|
<div style="margin-top:16px;">
|
||||||
|
<input type="file" id="matchImagesInput" accept="image/*" multiple style="display:none;" onchange="handleImageMatching(event)">
|
||||||
|
<button class="btn btn-primary" onclick="document.getElementById('matchImagesInput').click()">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right:6px;">
|
||||||
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/>
|
||||||
|
</svg>
|
||||||
|
Upload Images from Saved Folder
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary" onclick="skipImageMatching()" style="margin-left:8px;">
|
||||||
|
Skip - No Images
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle image matching from uploaded files
|
||||||
|
function handleImageMatching(event) {
|
||||||
|
const files = event.target.files;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
let matchedCount = 0;
|
||||||
|
const matchResults = [];
|
||||||
|
|
||||||
|
Array.from(files).forEach(file => {
|
||||||
|
const filename = file.name.toLowerCase();
|
||||||
|
|
||||||
|
// Try to find a matching mapping
|
||||||
|
for (const mapping of config.imageMappings) {
|
||||||
|
const mappingFilename = mapping.filename.toLowerCase();
|
||||||
|
|
||||||
|
// Check for exact filename match or close match
|
||||||
|
if (filename === mappingFilename ||
|
||||||
|
filename.includes(mappingFilename.replace(/\.[^.]+$/, '')) ||
|
||||||
|
mappingFilename.includes(filename.replace(/\.[^.]+$/, ''))) {
|
||||||
|
|
||||||
|
// Find matching item by alt text or name
|
||||||
|
const altText = (mapping.alt || '').toLowerCase();
|
||||||
|
const matchedItem = config.extractedData.items.find(item => {
|
||||||
|
const itemName = (item.name || '').toLowerCase();
|
||||||
|
const itemAlt = (item.imageAlt || '').toLowerCase();
|
||||||
|
return itemName === altText ||
|
||||||
|
itemAlt === altText ||
|
||||||
|
itemName.includes(altText) ||
|
||||||
|
altText.includes(itemName) ||
|
||||||
|
(itemAlt && (itemAlt.includes(altText) || altText.includes(itemAlt)));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchedItem) {
|
||||||
|
config.itemImages[matchedItem.id] = file;
|
||||||
|
matchedCount++;
|
||||||
|
matchResults.push({ item: matchedItem.name, file: file.name });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show results
|
||||||
|
const totalFiles = files.length;
|
||||||
|
const resultHtml = matchResults.length > 0
|
||||||
|
? `<ul style="font-size:13px;margin:8px 0;max-height:200px;overflow-y:auto;">${matchResults.map(r => `<li>${r.item} ← ${r.file}</li>`).join('')}</ul>`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
addMessage('ai', `
|
||||||
|
<p>Matched <strong>${matchedCount}</strong> of ${totalFiles} uploaded images to menu items.</p>
|
||||||
|
${resultHtml}
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button class="btn btn-primary" onclick="proceedAfterImageMatching()">Continue</button>
|
||||||
|
<button class="btn btn-secondary" onclick="document.getElementById('matchImagesInput').click()">Upload More Images</button>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
console.log('Image matching results:', matchResults);
|
||||||
|
console.log('Item images map:', config.itemImages);
|
||||||
|
|
||||||
|
// Reset input
|
||||||
|
event.target.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function skipImageMatching() {
|
||||||
|
proceedAfterImageMatching();
|
||||||
|
}
|
||||||
|
|
||||||
|
function proceedAfterImageMatching() {
|
||||||
|
// In add-menu mode, skip business info and header
|
||||||
|
if (config.businessId && config.menuId) {
|
||||||
|
showCategoriesStep();
|
||||||
|
} else {
|
||||||
|
showBusinessInfoStep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function startAnalysis() {
|
async function startAnalysis() {
|
||||||
if (config.uploadedFiles.length === 0) {
|
if (config.uploadedFiles.length === 0) {
|
||||||
showToast('Please upload at least one menu image', 'error');
|
showToast('Please upload at least one menu image', 'error');
|
||||||
|
|
|
||||||
Reference in a new issue