From d34d88e46a5a7ad076eed5a0e8add485d391da58 Mon Sep 17 00:00:00 2001 From: John Mizerek Date: Thu, 15 Jan 2026 15:08:53 -0800 Subject: [PATCH] Fix wizard: remove CategoryID column ref, add analysis time message - Fixed Items table INSERT to remove CategoryID column (only exists in Categories table) - Templates now correctly use ItemCategoryID=0 instead of incorrect CategoryID - Added user message that analysis may take several minutes - Updated localStorage with new businessId after save to keep user logged in - Redirect to visual menu builder with new businessId in URL Co-Authored-By: Claude Sonnet 4.5 --- api/setup/saveWizard.cfm | 197 ++++++++++++++++------ portal/setup-wizard.html | 352 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 478 insertions(+), 71 deletions(-) diff --git a/api/setup/saveWizard.cfm b/api/setup/saveWizard.cfm index 429759e..121e17c 100644 --- a/api/setup/saveWizard.cfm +++ b/api/setup/saveWizard.cfm @@ -33,29 +33,120 @@ try { data = deserializeJSON(requestBody); businessId = structKeyExists(data, "businessId") ? val(data.businessId) : 0; + userId = structKeyExists(data, "userId") ? val(data.userId) : 0; wizardData = structKeyExists(data, "data") ? data.data : {}; - - if (businessId == 0) { - throw(message="businessId is required"); - } - - // Verify business exists - qBiz = queryExecute(" - SELECT BusinessID, BusinessName FROM Businesses WHERE BusinessID = :id - ", { id: businessId }, { datasource: "payfrit" }); - - if (qBiz.recordCount == 0) { - throw(message="Business not found: " & businessId); - } - - response.steps.append("Found business: " & qBiz.BusinessName); - - // Update business info if provided biz = structKeyExists(wizardData, "business") ? wizardData.business : {}; - if (structKeyExists(biz, "name") && len(biz.name)) { - // Optionally update business name and other info - // For now we'll skip updating existing business - just add menu - response.steps.append("Business info available (not updating existing)"); + + // If no businessId, create a new business + if (businessId == 0) { + response.steps.append("No businessId provided - creating new business"); + + if (!structKeyExists(biz, "name") || !len(biz.name)) { + throw(message="Business name is required to create new business"); + } + + if (userId == 0) { + throw(message="userId is required to create new business"); + } + + // Create address record first (use extracted address fields) + addressLine1 = structKeyExists(biz, "addressLine1") ? biz.addressLine1 : ""; + city = structKeyExists(biz, "city") ? biz.city : ""; + state = structKeyExists(biz, "state") ? biz.state : ""; + zip = structKeyExists(biz, "zip") ? biz.zip : ""; + + // Look up state ID from state abbreviation + stateID = 0; + if (len(state)) { + qState = queryExecute(" + SELECT tt_StateID FROM tt_States WHERE tt_StateAbbreviation = :abbr + ", { abbr: uCase(state) }, { datasource: "payfrit" }); + + if (qState.recordCount > 0) { + stateID = qState.tt_StateID; + } + } + + queryExecute(" + INSERT INTO Addresses (AddressLine1, AddressCity, AddressStateID, AddressZIPCode, AddressUserID, AddressTypeID, AddressAddedOn) + VALUES (:line1, :city, :stateID, :zip, :userID, :typeID, NOW()) + ", { + line1: len(addressLine1) ? addressLine1 : "Address pending", + city: len(city) ? city : "", + stateID: stateID, + zip: len(zip) ? zip : "", + userID: userId, + typeID: 2 + }, { datasource: "payfrit" }); + + qNewAddr = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" }); + addressId = qNewAddr.id; + response.steps.append("Created address record (ID: " & addressId & ")"); + + // Create new business with address link + queryExecute(" + INSERT INTO Businesses (BusinessName, BusinessUserID, BusinessAddressID, BusinessDeliveryZipCodes, BusinessAddedOn) + VALUES (:name, :userId, :addressId, :deliveryZips, NOW()) + ", { + name: biz.name, + userId: userId, + addressId: addressId, + deliveryZips: len(zip) ? zip : "" + }, { datasource: "payfrit" }); + + qNewBiz = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" }); + businessId = qNewBiz.id; + response.steps.append("Created new business: " & biz.name & " (ID: " & businessId & ")"); + + // Update address with business ID link + queryExecute(" + UPDATE Addresses SET AddressBusinessID = :businessID WHERE AddressID = :addressID + ", { + businessID: businessId, + addressID: addressId + }, { datasource: "payfrit" }); + response.steps.append("Linked address to business"); + + // Save business hours from structured schedule + if (structKeyExists(biz, "hoursSchedule") && isArray(biz.hoursSchedule)) { + hoursSchedule = biz.hoursSchedule; + response.steps.append("Processing " & arrayLen(hoursSchedule) & " days of hours"); + + for (i = 1; i <= arrayLen(hoursSchedule); i++) { + dayData = hoursSchedule[i]; + dayID = structKeyExists(dayData, "dayId") ? val(dayData.dayId) : 0; + openTime = structKeyExists(dayData, "open") ? dayData.open : "09:00"; + closeTime = structKeyExists(dayData, "close") ? dayData.close : "17:00"; + + // Convert HH:MM to HH:MM:SS if needed + if (len(openTime) == 5) openTime = openTime & ":00"; + if (len(closeTime) == 5) closeTime = closeTime & ":00"; + + // Insert hours record for this day + queryExecute(" + INSERT INTO Hours (HoursBusinessID, HoursDayID, HoursOpenTime, HoursClosingTime) + VALUES (:bizID, :dayID, :openTime, :closeTime) + ", { + bizID: businessId, + dayID: dayID, + openTime: openTime, + closeTime: closeTime + }, { datasource: "payfrit" }); + } + + response.steps.append("Created " & arrayLen(hoursSchedule) & " hours records"); + } + } else { + // Verify existing business exists + qBiz = queryExecute(" + SELECT BusinessID, BusinessName FROM Businesses WHERE BusinessID = :id + ", { id: businessId }, { datasource: "payfrit" }); + + if (qBiz.recordCount == 0) { + throw(message="Business not found: " & businessId); + } + + response.steps.append("Found existing business: " & qBiz.BusinessName); } // Build modifier template map @@ -77,21 +168,21 @@ try { WHERE i.ItemBusinessID = :bizID AND i.ItemName = :name AND i.ItemParentItemID = 0 - AND i.ItemIsCollapsible = 1 + AND i.ItemCategoryID = 0 ", { bizID: businessId, name: tmplName }, { datasource: "payfrit" }); if (qTmpl.recordCount > 0) { templateItemID = qTmpl.ItemID; response.steps.append("Template exists: " & tmplName & " (ID: " & templateItemID & ")"); } else { - // Create template as Item with ItemIsCollapsible=1 to mark as template + // Create template as Item with ItemCategoryID=0 to mark as template queryExecute(" INSERT INTO Items ( - ItemBusinessID, ItemName, ItemParentItemID, ItemPrice, + ItemBusinessID, ItemName, ItemParentItemID, ItemCategoryID, ItemPrice, ItemIsActive, ItemRequiresChildSelection, ItemMaxNumSelectionReq, - ItemSortOrder, ItemIsCollapsible + ItemSortOrder ) VALUES ( - :bizID, :name, 0, 0, 1, :required, 1, 0, 1 + :bizID, :name, 0, 0, 0, 1, :required, 1, 0 ) ", { bizID: businessId, @@ -109,6 +200,11 @@ try { // Create/update template options optionOrder = 1; for (opt in options) { + // Safety check: ensure opt is a struct with a name + if (!isStruct(opt) || !structKeyExists(opt, "name") || !len(opt.name)) { + continue; + } + optName = opt.name; optPrice = structKeyExists(opt, "price") ? val(opt.price) : 0; @@ -120,10 +216,10 @@ try { if (qOpt.recordCount == 0) { queryExecute(" INSERT INTO Items ( - ItemBusinessID, ItemName, ItemParentItemID, ItemPrice, - ItemIsActive, ItemSortOrder + ItemBusinessID, ItemName, ItemParentItemID, ItemCategoryID, + ItemPrice, ItemIsActive, ItemSortOrder ) VALUES ( - :bizID, :name, :parentID, :price, 1, :sortOrder + :bizID, :name, :parentID, 0, :price, 1, :sortOrder ) ", { bizID: businessId, @@ -139,7 +235,7 @@ try { // Build category map categories = structKeyExists(wizardData, "categories") ? wizardData.categories : []; - categoryMap = {}; // Maps category name to ItemID + categoryMap = {}; // Maps category name to CategoryID response.steps.append("Processing " & arrayLen(categories) & " categories..."); @@ -147,25 +243,22 @@ try { for (cat in categories) { catName = cat.name; - // Check if category exists (Item at ParentID=0, not a template) + // Check if category exists in Categories table qCat = queryExecute(" - SELECT ItemID FROM Items - WHERE ItemBusinessID = :bizID - AND ItemName = :name - AND ItemParentItemID = 0 - AND (ItemIsCollapsible IS NULL OR ItemIsCollapsible = 0) + SELECT CategoryID FROM Categories + WHERE CategoryBusinessID = :bizID AND CategoryName = :name ", { bizID: businessId, name: catName }, { datasource: "payfrit" }); if (qCat.recordCount > 0) { - categoryItemID = qCat.ItemID; - response.steps.append("Category exists: " & catName); + categoryID = qCat.CategoryID; + response.steps.append("Category exists: " & catName & " (ID: " & categoryID & ")"); } else { + // Create category in Categories table queryExecute(" - INSERT INTO Items ( - ItemBusinessID, ItemName, ItemParentItemID, ItemPrice, - ItemIsActive, ItemSortOrder, ItemIsCollapsible + INSERT INTO Categories ( + CategoryBusinessID, CategoryName, CategorySortOrder ) VALUES ( - :bizID, :name, 0, 0, 1, :sortOrder, 0 + :bizID, :name, :sortOrder ) ", { bizID: businessId, @@ -174,11 +267,11 @@ try { }, { datasource: "payfrit" }); qNewCat = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" }); - categoryItemID = qNewCat.id; - response.steps.append("Created category: " & catName & " (ID: " & categoryItemID & ")"); + categoryID = qNewCat.id; + response.steps.append("Created category: " & catName & " (ID: " & categoryID & ")"); } - categoryMap[catName] = categoryItemID; + categoryMap[catName] = categoryID; catOrder++; } @@ -206,7 +299,7 @@ try { continue; } - categoryItemID = categoryMap[itemCategory]; + categoryID = categoryMap[itemCategory]; // Track sort order within category if (!structKeyExists(categoryItemOrder, itemCategory)) { @@ -218,8 +311,10 @@ try { // Check if item exists qItem = queryExecute(" SELECT ItemID FROM Items - WHERE ItemBusinessID = :bizID AND ItemName = :name AND ItemParentItemID = :parentID - ", { bizID: businessId, name: itemName, parentID: categoryItemID }, { datasource: "payfrit" }); + WHERE ItemBusinessID = :bizID + AND ItemName = :name + AND ItemCategoryID = :catID + ", { bizID: businessId, name: itemName, catID: categoryID }, { datasource: "payfrit" }); if (qItem.recordCount > 0) { menuItemID = qItem.ItemID; @@ -240,15 +335,15 @@ try { queryExecute(" INSERT INTO Items ( ItemBusinessID, ItemName, ItemDescription, ItemParentItemID, - ItemPrice, ItemIsActive, ItemSortOrder + ItemCategoryID, ItemPrice, ItemIsActive, ItemSortOrder ) VALUES ( - :bizID, :name, :desc, :parentID, :price, 1, :sortOrder + :bizID, :name, :desc, 0, :catID, :price, 1, :sortOrder ) ", { bizID: businessId, name: itemName, desc: itemDesc, - parentID: categoryItemID, + catID: categoryID, price: itemPrice, sortOrder: itemOrder }, { datasource: "payfrit" }); diff --git a/portal/setup-wizard.html b/portal/setup-wizard.html index 845aaeb..3d8286e 100644 --- a/portal/setup-wizard.html +++ b/portal/setup-wizard.html @@ -651,8 +651,8 @@
-

Let's Set Up Your Menu

-

Upload your menu images and I'll extract all the information for you

+

Let's Setup Your Menu

+

Upload your menu images or PDFs and I'll extract then input all the information for you to preview!

@@ -747,9 +747,9 @@ }); function initializeConfig() { - // Get business ID from URL or localStorage + // Get business ID from URL or localStorage (optional for new business setup) const urlParams = new URLSearchParams(window.location.search); - config.businessId = urlParams.get('bid') || localStorage.getItem('payfrit_portal_business'); + config.businessId = urlParams.get('bid') || localStorage.getItem('payfrit_portal_business') || null; // Determine API base URL const basePath = window.location.pathname.includes('/biz.payfrit.com/') @@ -757,9 +757,16 @@ : ''; config.apiBaseUrl = basePath + '/api'; - if (!config.businessId) { + // Check if user is logged in + const userId = localStorage.getItem('payfrit_portal_userid'); + if (!userId) { window.location.href = 'login.html'; + return; } + config.userId = userId; + + // BusinessId is optional - will be created if not provided + console.log('Wizard initialized. BusinessId:', config.businessId, 'UserId:', config.userId); } function setupUploadZone() { @@ -899,7 +906,7 @@ addMessage('ai', `
- Analyzing ${config.uploadedFiles.length} menu image${config.uploadedFiles.length > 1 ? 's' : ''}... + Analyzing ${config.uploadedFiles.length} menu image${config.uploadedFiles.length > 1 ? 's' : ''}... This may take several minutes, please be patient.
`); @@ -925,6 +932,21 @@ // Store extracted data config.extractedData = result.DATA; + // Debug: Log raw API response + console.log('=== RAW API RESPONSE ==='); + console.log('Full result:', result); + if (result.DEBUG_RAW_RESULTS) { + console.log('DEBUG_RAW_RESULTS:', result.DEBUG_RAW_RESULTS); + result.DEBUG_RAW_RESULTS.forEach((imgResult, i) => { + console.log(`Image ${i} raw result:`, imgResult); + if (imgResult.modifiers) { + console.log(` Raw modifiers from image ${i}:`, imgResult.modifiers); + } + }); + } + console.log('Merged DATA:', result.DATA); + console.log('========================'); + // Remove loading message and start conversation flow document.getElementById('conversation').innerHTML = ''; @@ -949,28 +971,248 @@ document.getElementById('uploadSection').style.display = 'block'; } + // Helper function to parse hours string into 7-day schedule + function parseHoursString(hoursText) { + const schedule = []; + const dayMap = { + 'monday': 0, 'mon': 0, + 'tuesday': 1, 'tue': 1, 'tues': 1, + 'wednesday': 2, 'wed': 2, + 'thursday': 3, 'thu': 3, 'thur': 3, 'thurs': 3, + 'friday': 4, 'fri': 4, + 'saturday': 5, 'sat': 5, + 'sunday': 6, 'sun': 6 + }; + + // Initialize all days as closed + for (let i = 0; i < 7; i++) { + schedule.push({ open: '09:00', close: '17:00', closed: true }); + } + + if (!hoursText || !hoursText.trim()) { + return schedule; + } + + hoursText = hoursText.toLowerCase(); + + // Extract time range - find first time pattern + const timePattern = /(\d{1,2}):?(\d{2})?\s*(am|pm|a\.m\.|p\.m\.)?\s*(?:to|[-–])\s*(\d{1,2}):?(\d{2})?\s*(am|pm|a\.m\.|p\.m\.)?/i; + const timeMatch = hoursText.match(timePattern); + + let openTime = '09:00'; + let closeTime = '17:00'; + + if (timeMatch) { + const convertTo24Hour = (hour, minute, ampm) => { + let h = parseInt(hour); + const m = minute ? parseInt(minute) : 0; + + if (ampm) { + ampm = ampm.replace(/\./g, '').toLowerCase(); + if (ampm === 'pm' && h < 12) h += 12; + if (ampm === 'am' && h === 12) h = 0; + } + + return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`; + }; + + openTime = convertTo24Hour(timeMatch[1], timeMatch[2], timeMatch[3]); + closeTime = convertTo24Hour(timeMatch[4], timeMatch[5], timeMatch[6]); + } + + // Find which days are mentioned + const dayPattern = /(mon|monday|tue|tuesday|tues|wed|wednesday|thu|thursday|thur|thurs|fri|friday|sat|saturday|sun|sunday)/gi; + const dayMatches = hoursText.match(dayPattern) || []; + + if (dayMatches.length === 0) { + // No days mentioned, assume all days open + for (let i = 0; i < 7; i++) { + schedule[i] = { open: openTime, close: closeTime, closed: false }; + } + return schedule; + } + + // Process day ranges and individual days + const daysSet = new Set(); + let i = 0; + while (i < dayMatches.length) { + const currentDay = dayMatches[i].toLowerCase(); + const currentDayIdx = dayMap[currentDay]; + + // Check if next day forms a range + if (i < dayMatches.length - 1) { + const nextDay = dayMatches[i + 1].toLowerCase(); + const betweenPattern = new RegExp(currentDay + '\\s*[-–]\\s*' + nextDay, 'i'); + + if (betweenPattern.test(hoursText)) { + // It's a range + const nextDayIdx = dayMap[nextDay]; + for (let d = currentDayIdx; d <= nextDayIdx; d++) { + daysSet.add(d); + } + i += 2; + continue; + } + } + + // Individual day + daysSet.add(currentDayIdx); + i++; + } + + // Apply times to the days found + daysSet.forEach(dayIdx => { + schedule[dayIdx] = { open: openTime, close: closeTime, closed: false }; + }); + + return schedule; + } + + // Toggle day closed/open + function toggleDayClosed(dayIdx) { + const closedCheckbox = document.getElementById(`closed_${dayIdx}`); + const openInput = document.getElementById(`open_${dayIdx}`); + const closeInput = document.getElementById(`close_${dayIdx}`); + + if (closedCheckbox.checked) { + openInput.disabled = true; + closeInput.disabled = true; + } else { + openInput.disabled = false; + closeInput.disabled = false; + } + } + // Step 1: Business Info function showBusinessInfoStep() { updateProgress(2); const biz = config.extractedData.business || {}; + console.log('Business data:', biz); + + // Parse address into components if it's a single string + let addressLine1 = biz.addressLine1 || ''; + let city = biz.city || ''; + let state = biz.state || ''; + let zip = biz.zip || ''; + + if (biz.address && !addressLine1) { + console.log('Parsing address:', biz.address); + + // Parse from the end forward: ZIP (5 digits), then State (2 letters), then City + AddressLine1 + let remaining = biz.address.trim(); + + // Extract ZIP (last 5 digits, optionally with -4) + const zipMatch = remaining.match(/\b(\d{5})(?:-\d{4})?\s*$/); + if (zipMatch) { + zip = zipMatch[1]; + remaining = remaining.substring(0, zipMatch.index).trim(); + console.log('Found ZIP:', zip, 'Remaining:', remaining); + } + + // Extract State (2 letters before ZIP) + const stateMatch = remaining.match(/\b([A-Z]{2})\s*$/i); + if (stateMatch) { + state = stateMatch[1].toUpperCase(); + remaining = remaining.substring(0, stateMatch.index).trim(); + console.log('Found State:', state, 'Remaining:', remaining); + } + + // What's left is AddressLine1 + City + // Try to split by comma first + if (remaining.includes(',')) { + const parts = remaining.split(',').map(p => p.trim()); + addressLine1 = parts[0]; + city = parts.slice(1).join(', '); + } else { + // No comma - try to find last sequence of words as city + // Cities are usually 1-3 words at the end + const words = remaining.split(/\s+/); + if (words.length > 3) { + // Assume last 2 words are city, rest is address + city = words.slice(-2).join(' '); + addressLine1 = words.slice(0, -2).join(' '); + } else if (words.length > 1) { + // Assume last word is city + city = words[words.length - 1]; + addressLine1 = words.slice(0, -1).join(' '); + } else { + // All in address line 1 + addressLine1 = remaining; + } + } + } + + console.log('Parsed address:', { addressLine1, city, state, zip }); + + // Parse hours into a 7-day schedule + const hoursSchedule = parseHoursString(biz.hours || ''); + addMessage('ai', `

I found your restaurant information:

- +
- - + + +
+
+ + +
+
+
+ + +
+
+ + +
- - + + + + + + + + + + + + ${['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'].map((day, idx) => { + const dayData = hoursSchedule[idx]; + return ` + + + + + + + `; + }).join('')} + +
DayOpenCloseClosed
${day} + + + + + +

Is this information correct?

@@ -985,12 +1227,32 @@ } function confirmBusinessInfo() { + // Collect hours from the table + const hoursSchedule = []; + const dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; + + for (let i = 0; i < 7; i++) { + const openTime = document.getElementById(`open_${i}`).value; + const closeTime = document.getElementById(`close_${i}`).value; + const isClosed = document.getElementById(`closed_${i}`).checked; + + hoursSchedule.push({ + day: dayNames[i], + dayId: i + 1, // 1=Monday, 7=Sunday + open: isClosed ? openTime : openTime, // If closed, both times will be the same + close: isClosed ? openTime : closeTime // Set close = open when closed + }); + } + // Update stored data with any edits config.extractedData.business = { name: document.getElementById('bizName').value, - address: document.getElementById('bizAddress').value, + addressLine1: document.getElementById('bizAddressLine1').value, + city: document.getElementById('bizCity').value, + state: document.getElementById('bizState').value, + zip: document.getElementById('bizZip').value, phone: document.getElementById('bizPhone').value, - hours: document.getElementById('bizHours').value + hoursSchedule: hoursSchedule // Send the structured schedule instead of the raw hours string }; // Move to categories @@ -1115,6 +1377,22 @@ updateProgress(4); const modifiers = config.extractedData.modifiers || []; + // Debug: Log the raw modifier data + console.log('=== MODIFIERS DEBUG ==='); + console.log('Total modifiers:', modifiers.length); + modifiers.forEach((mod, i) => { + console.log(`Modifier ${i}:`, mod); + console.log(` - name:`, mod.name); + console.log(` - options type:`, typeof mod.options); + console.log(` - options:`, mod.options); + if (mod.options && mod.options.length > 0) { + mod.options.forEach((opt, j) => { + console.log(` Option ${j}:`, opt, `(type: ${typeof opt})`); + }); + } + }); + console.log('======================'); + if (modifiers.length === 0) { addMessage('ai', `

I didn't detect any modifier templates (like size options, add-ons, etc.).

@@ -1131,11 +1409,11 @@
- ${mod.name} + ${mod.name || 'Unnamed'} ${mod.required ? 'Required' : 'Optional'}
- ${(mod.options || []).map(opt => ` + ${(mod.options || []).filter(opt => opt && opt.name).map(opt => ` ${opt.name}${opt.price ? `+$${opt.price.toFixed(2)}` : ''} @@ -1299,6 +1577,9 @@ } async function saveMenu() { + console.log('=== SAVE MENU CALLED ==='); + console.log('Data to save:', config.extractedData); + const saveBtn = document.querySelector('#finalActions .btn-success'); const originalText = saveBtn.innerHTML; saveBtn.innerHTML = '
Saving...'; @@ -1311,22 +1592,53 @@ 'Content-Type': 'application/json' }, body: JSON.stringify({ - businessId: config.businessId, + businessId: config.businessId || 0, + userId: config.userId, data: config.extractedData }) }); - const result = await response.json(); + console.log('HTTP Status:', response.status, response.statusText); + + const responseText = await response.text(); + console.log('Raw response:', responseText); + + let result; + try { + result = JSON.parse(responseText); + } catch (e) { + console.error('Failed to parse JSON response:', e); + throw new Error('Invalid JSON response from server: ' + responseText.substring(0, 200)); + } + + console.log('=== SAVE RESPONSE ==='); + console.log('Full result:', result); + if (result.errors && result.errors.length > 0) { + console.error('Backend errors:', result.errors); + } + if (result.steps && result.steps.length > 0) { + console.log('Backend steps:', result.steps); + } + console.log('===================='); if (!result.OK) { - throw new Error(result.MESSAGE || 'Save failed'); + const errorMsg = result.errors && result.errors.length > 0 + ? result.errors.join('; ') + : (result.MESSAGE || 'Save failed'); + throw new Error(errorMsg); } showToast('Menu saved successfully!', 'success'); - // Redirect to menu page after a moment + // Use the businessId from the response (in case it was newly created) + const finalBusinessId = result.summary?.businessId || config.businessId; + + // Update localStorage with the new business ID to keep user logged in + localStorage.setItem('payfrit_portal_business', finalBusinessId); + + // Redirect to visual menu builder after a moment setTimeout(() => { - window.location.href = `index.html?bid=${config.businessId}#menu`; + window.location.href = `index.html?bid=${finalBusinessId}#menu`; }, 1500); } catch (error) {