Add "Start from Scratch" option to setup wizard for new businesses

New users without an existing menu to import can now click "Start from Scratch"
to enter their business info and get redirected to the Menu Builder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-03-14 20:35:47 -07:00
parent 2d1618dc1c
commit 6d157b44f7

View file

@ -813,7 +813,7 @@
<!-- Wizard Header --> <!-- Wizard Header -->
<div class="wizard-header"> <div class="wizard-header">
<h1>Let's Setup Your Menu</h1> <h1>Let's Setup Your Menu</h1>
<p>Import your menu from a website URL or upload images/PDFs</p> <p>Import from an existing source, or start from scratch</p>
</div> </div>
<!-- Upload Section --> <!-- Upload Section -->
@ -832,6 +832,12 @@
</svg> </svg>
Upload Files Upload Files
</button> </button>
<button class="import-tab" id="tabScratch" onclick="switchImportTab('scratch')" style="flex:1;padding:12px 16px;border:none;background:var(--gray-100);color:var(--gray-700);font-weight:500;cursor:pointer;transition:all 0.2s;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align:middle;margin-right:6px;">
<path d="M12 5v14M5 12h14"/>
</svg>
Start from Scratch
</button>
</div> </div>
<!-- URL Import Panel --> <!-- URL Import Panel -->
@ -905,6 +911,23 @@
</button> </button>
</div> </div>
</div> </div>
<!-- Start from Scratch Panel (hidden by default) -->
<div id="scratchPanel" style="display:none;">
<div style="background:var(--gray-50);border:2px dashed var(--gray-300);border-radius:12px;padding:40px;text-align:center;">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="var(--gray-400)" stroke-width="1.5" style="margin-bottom:16px;">
<path d="M12 5v14M5 12h14"/>
</svg>
<h3 style="margin:0 0 8px;color:var(--gray-700);">Build Your Menu from Scratch</h3>
<p style="margin:0 0 24px;color:var(--gray-500);font-size:14px;">Enter your business information and then use the Visual Menu Builder to add categories, items, and modifiers.</p>
<button class="btn btn-primary" onclick="startFromScratch()" style="min-width:200px;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right:6px;">
<path d="M12 5v14M5 12h14"/>
</svg>
Get Started
</button>
</div>
</div>
</div> </div>
<!-- Conversation Section --> <!-- Conversation Section -->
@ -1341,25 +1364,126 @@
// Switch between URL and file upload tabs // Switch between URL and file upload tabs
function switchImportTab(tab) { function switchImportTab(tab) {
const tabUrl = document.getElementById('tabUrl'); const tabs = {
const tabUpload = document.getElementById('tabUpload'); url: document.getElementById('tabUrl'),
const urlPanel = document.getElementById('urlImportPanel'); upload: document.getElementById('tabUpload'),
const filePanel = document.getElementById('fileUploadPanel'); scratch: document.getElementById('tabScratch')
};
const panels = {
url: document.getElementById('urlImportPanel'),
upload: document.getElementById('fileUploadPanel'),
scratch: document.getElementById('scratchPanel')
};
if (tab === 'url') { Object.keys(tabs).forEach(key => {
tabUrl.style.background = 'var(--primary)'; if (key === tab) {
tabUrl.style.color = 'white'; tabs[key].style.background = 'var(--primary)';
tabUpload.style.background = 'var(--gray-100)'; tabs[key].style.color = 'white';
tabUpload.style.color = 'var(--gray-700)'; panels[key].style.display = 'block';
urlPanel.style.display = 'block'; } else {
filePanel.style.display = 'none'; tabs[key].style.background = 'var(--gray-100)';
} else { tabs[key].style.color = 'var(--gray-700)';
tabUpload.style.background = 'var(--primary)'; panels[key].style.display = 'none';
tabUpload.style.color = 'white'; }
tabUrl.style.background = 'var(--gray-100)'; });
tabUrl.style.color = 'var(--gray-700)'; }
urlPanel.style.display = 'none';
filePanel.style.display = 'block'; // Start from scratch — skip import, go straight to business info
function startFromScratch() {
config.scratchMode = true;
config.extractedData = {
business: {},
categories: [],
modifiers: [],
items: []
};
// Hide upload section, show conversation
document.getElementById('uploadSection').style.display = 'none';
showBusinessInfoStep();
}
// Save business only (scratch mode) then redirect to Menu Builder
async function saveScratchBusiness() {
// Add a default menu name and community meal type
config.extractedData.menuName = 'Main Menu';
config.extractedData.communityMealType = 1;
addMessage('ai', `
<p>Saving your business information...</p>
<div class="loading-spinner" style="width:24px;height:24px;border-width:3px;margin:16px auto;"></div>
`);
try {
const response = await fetch(`${config.apiBaseUrl}/setup/saveWizard.php`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
businessId: config.businessId || 0,
menuId: 0,
userId: config.userId,
data: config.extractedData,
tempFolder: null
})
});
const responseText = await response.text();
let result;
try {
result = JSON.parse(responseText);
} catch (e) {
throw new Error('Invalid response from server');
}
if (!result.OK) {
const errorMsg = result.errors && result.errors.length > 0
? result.errors.join('; ')
: (result.MESSAGE || 'Save failed');
throw new Error(errorMsg);
}
const summary = result.summary || result.SUMMARY || {};
const finalBusinessId = summary.businessId || summary.BUSINESSID || summary.businessid || config.businessId;
localStorage.setItem('payfrit_portal_business', finalBusinessId);
// Upload header image if one was selected
if (config.headerImageFile && finalBusinessId) {
try {
const formData = new FormData();
formData.append('BusinessID', finalBusinessId);
formData.append('header', config.headerImageFile);
const headerResp = await fetch(`${config.apiBaseUrl}/menu/uploadHeader.php`, {
method: 'POST',
body: formData
});
const headerResult = await headerResp.json();
if (!headerResult.OK) {
console.error('Header upload failed:', headerResult.MESSAGE);
}
} catch (headerErr) {
console.error('Header upload error:', headerErr);
}
}
showToast('Business created! Redirecting to Menu Builder...', 'success');
setTimeout(() => {
window.location.href = '/portal/menu-builder.html';
}, 1500);
} catch (err) {
console.error('Save error:', err);
showToast('Error: ' + err.message, 'error');
// Remove the loading message and show retry
addMessage('ai', `
<p style="color:var(--error);">Something went wrong: ${err.message}</p>
<div class="action-buttons">
<button class="btn btn-primary" onclick="saveScratchBusiness()">Try Again</button>
</div>
`);
} }
} }
@ -2657,12 +2781,20 @@
} }
function confirmHeaderImage() { function confirmHeaderImage() {
showCategoriesStep(); if (config.scratchMode) {
saveScratchBusiness();
} else {
showCategoriesStep();
}
} }
function skipHeaderImage() { function skipHeaderImage() {
config.headerImageFile = null; config.headerImageFile = null;
showCategoriesStep(); if (config.scratchMode) {
saveScratchBusiness();
} else {
showCategoriesStep();
}
} }
// Step 2: Categories // Step 2: Categories