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>
|
||||
<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>
|
||||
<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%;">
|
||||
<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"/>
|
||||
|
|
|
|||
|
|
@ -1049,7 +1049,9 @@
|
|||
items: []
|
||||
},
|
||||
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
|
||||
|
|
@ -1328,11 +1330,15 @@
|
|||
config.extractedData = result.DATA;
|
||||
config.sourceUrl = result.sourceUrl;
|
||||
|
||||
// Store image mappings for matching uploaded images to items
|
||||
config.imageMappings = result.DATA.imageMappings || [];
|
||||
|
||||
// Log debug info
|
||||
console.log('=== URL IMPORT RESPONSE ===');
|
||||
console.log('Source URL:', result.sourceUrl);
|
||||
console.log('Pages processed:', result.pagesProcessed);
|
||||
console.log('Images found:', result.imagesFound);
|
||||
console.log('Image mappings:', config.imageMappings.length);
|
||||
console.log('Extracted data:', result.DATA);
|
||||
if (result.steps) {
|
||||
console.log('Steps:', result.steps);
|
||||
|
|
@ -1342,8 +1348,11 @@
|
|||
// 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) {
|
||||
// If we have image mappings from the HTML, show image matching step first
|
||||
if (config.imageMappings.length > 0) {
|
||||
showImageMatchingStep();
|
||||
} else if (config.businessId && config.menuId) {
|
||||
// In add-menu mode, skip business info and header
|
||||
showCategoriesStep();
|
||||
} else {
|
||||
showBusinessInfoStep();
|
||||
|
|
@ -1396,14 +1405,34 @@
|
|||
<p style="font-size:13px;color:var(--gray-500);margin-top:8px;">Extracting menu data from HTML content.</p>
|
||||
`);
|
||||
|
||||
let result = null;
|
||||
try {
|
||||
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 })
|
||||
});
|
||||
|
||||
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) {
|
||||
throw new Error(result.MESSAGE || 'Failed to analyze HTML file');
|
||||
|
|
@ -1412,20 +1441,18 @@
|
|||
// 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('=================================');
|
||||
// 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 = '';
|
||||
|
||||
// In add-menu mode, skip business info and header
|
||||
if (config.businessId && config.menuId) {
|
||||
// If we have image mappings from the HTML, show image matching step first
|
||||
if (config.imageMappings.length > 0) {
|
||||
showImageMatchingStep();
|
||||
} else if (config.businessId && config.menuId) {
|
||||
// In add-menu mode, skip business info and header
|
||||
showCategoriesStep();
|
||||
} else {
|
||||
showBusinessInfoStep();
|
||||
|
|
@ -1433,10 +1460,16 @@
|
|||
|
||||
} 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 = `<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', `
|
||||
<p>Sorry, I encountered an error analyzing that file:</p>
|
||||
<p style="color: var(--danger);">${error.message}</p>
|
||||
${debugInfo}
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-primary" onclick="retryUrlAnalysis()">Try Again</button>
|
||||
<button class="btn btn-secondary" onclick="switchToFileUpload()">Upload Images Instead</button>
|
||||
|
|
@ -1455,6 +1488,107 @@
|
|||
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() {
|
||||
if (config.uploadedFiles.length === 0) {
|
||||
showToast('Please upload at least one menu image', 'error');
|
||||
|
|
|
|||
Reference in a new issue