diff --git a/api/auth/loginOTP.cfm b/api/auth/loginOTP.cfm index 35cc2fc..94ded05 100644 --- a/api/auth/loginOTP.cfm +++ b/api/auth/loginOTP.cfm @@ -108,12 +108,15 @@ try { } try{logPerf(0);}catch(any e){} - writeOutput(serializeJSON({ + resp = { "OK": true, "UUID": userUUID, - "MESSAGE": smsMessage, - "DEV_OTP": otp - })); + "MESSAGE": smsMessage + }; + if (isDev) { + resp["DEV_OTP"] = otp; + } + writeOutput(serializeJSON(resp)); } catch (any e) { apiAbort({ diff --git a/api/auth/sendOTP.cfm b/api/auth/sendOTP.cfm index d4a97a7..46d55bc 100644 --- a/api/auth/sendOTP.cfm +++ b/api/auth/sendOTP.cfm @@ -150,12 +150,15 @@ try { } } - writeOutput(serializeJSON({ + resp = { "OK": true, "UUID": userUUID, - "MESSAGE": smsMessage, - "DEV_OTP": otp - })); + "MESSAGE": smsMessage + }; + if (isDev) { + resp["DEV_OTP"] = otp; + } + writeOutput(serializeJSON(resp)); } catch (any e) { apiAbort({ diff --git a/api/menu/saveFromBuilder.cfm b/api/menu/saveFromBuilder.cfm index 653e2b2..59d9b8e 100644 --- a/api/menu/saveFromBuilder.cfm +++ b/api/menu/saveFromBuilder.cfm @@ -2,8 +2,9 @@ // Save menu data from the builder UI (OPTIMIZED) // Input: BusinessID, Menu (JSON structure) // Output: { OK: true } +// VERSION: 2026-02-08-fix1 -response = { "OK": false }; +response = { "OK": false, "VERSION": "2026-02-08-fix2", "DEBUG": [] }; // Track which templates we've already saved options for (to avoid duplicate saves) savedTemplates = {}; @@ -95,18 +96,21 @@ try { throw("Menu categories are required"); } - // Check if new schema is active - newSchemaActive = false; + // Check if Categories table has data (must match getForBuilder logic!) + // If Categories table has data, use legacy schema (CategoryID in Items) + // Otherwise use unified schema (ParentItemID in Items) + hasCategoriesData = false; try { - qCheck = queryTimed(" - SELECT 1 FROM Items - WHERE BusinessID = :businessID AND BusinessID > 0 + qCatCheck = queryTimed(" + SELECT 1 FROM Categories + WHERE BusinessID = :businessID LIMIT 1 - ", { businessID: businessID }); - newSchemaActive = (qCheck.recordCount > 0); + ", { businessID: businessID }, { datasource: "payfrit" }); + hasCategoriesData = (qCatCheck.recordCount > 0); } catch (any e) { - newSchemaActive = false; + hasCategoriesData = false; } + newSchemaActive = !hasCategoriesData; // Wrap everything in a transaction for speed and consistency transaction { @@ -114,6 +118,13 @@ try { for (cat in menu.categories) { categoryID = 0; categoryDbId = structKeyExists(cat, "dbId") ? val(cat.dbId) : 0; + catItemCount = structKeyExists(cat, "items") && isArray(cat.items) ? arrayLen(cat.items) : 0; + + // Debug: log each category being processed + arrayAppend(response.DEBUG, "Category: " & cat.name & " (dbId=" & categoryDbId & ") with " & catItemCount & " items"); + + // Initialize menuId param - use 0 for "no menu" (nullable in DB) + categoryMenuId = structKeyExists(cat, "menuId") ? val(cat.menuId) : 0; if (newSchemaActive) { if (categoryDbId > 0) { @@ -151,31 +162,27 @@ try { categoryID = result.newID; } } else { - // Get menu ID from category if provided - categoryMenuId = structKeyExists(cat, "menuId") ? val(cat.menuId) : 0; - categoryMenuIdParam = categoryMenuId > 0 ? categoryMenuId : javaCast("null", ""); - if (categoryDbId > 0) { categoryID = categoryDbId; queryTimed(" UPDATE Categories SET Name = :name, SortOrder = :sortOrder, - MenuID = :menuId - WHERE CategoryID = :categoryID + MenuID = NULLIF(:menuId, 0) + WHERE ID = :categoryID ", { categoryID: categoryID, name: cat.name, sortOrder: catSortOrder, - menuId: categoryMenuIdParam + menuId: categoryMenuId }); } else { queryTimed(" INSERT INTO Categories (BusinessID, MenuID, Name, SortOrder, AddedOn) - VALUES (:businessID, :menuId, :name, :sortOrder, NOW()) + VALUES (:businessID, NULLIF(:menuId, 0), :name, :sortOrder, NOW()) ", { businessID: businessID, - menuId: categoryMenuIdParam, + menuId: categoryMenuId, name: cat.name, sortOrder: catSortOrder }); @@ -185,6 +192,9 @@ try { } } + // Debug: log final categoryID for this category + arrayAppend(response.DEBUG, " -> CategoryID resolved to: " & categoryID); + // Process items if (structKeyExists(cat, "items") && isArray(cat.items)) { itemSortOrder = 0; @@ -192,6 +202,9 @@ try { itemID = 0; itemDbId = structKeyExists(item, "dbId") ? val(item.dbId) : 0; + // Debug: log each item being processed + arrayAppend(response.DEBUG, " Item: " & item.name & " (dbId=" & itemDbId & ") -> CategoryID=" & categoryID); + if (itemDbId > 0) { itemID = itemDbId; diff --git a/api/menu/uploadHeader.cfm b/api/menu/uploadHeader.cfm index 980f259..eab9fd8 100644 --- a/api/menu/uploadHeader.cfm +++ b/api/menu/uploadHeader.cfm @@ -4,6 +4,13 @@ + + + + #serializeJSON(arguments.payload)# + + + @@ -35,20 +42,29 @@ if (bizId LTE 0) { - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + @@ -105,8 +121,15 @@ if (bizId LTE 0) { + + + + #serializeJSON({ "OK": false, "ERROR": "upload_failed", "MESSAGE": "Uploaded file not found - upload may have failed", "DEBUG_SOURCE": sourceFile })# + + + - + @@ -116,13 +139,15 @@ if (bizId LTE 0) { + + #serializeJSON({ "OK": true, "ERROR": "", "MESSAGE": "Header uploaded successfully", "HEADERURL": "/uploads/headers/#bizId#.#actualExt#", - "WIDTH": imageInfo.width, - "HEIGHT": imageInfo.height + "WIDTH": imgWidth, + "HEIGHT": imgHeight })# diff --git a/api/menu/uploadItemPhoto.cfm b/api/menu/uploadItemPhoto.cfm index a20a4a7..3fc4342 100644 --- a/api/menu/uploadItemPhoto.cfm +++ b/api/menu/uploadItemPhoto.cfm @@ -122,16 +122,22 @@ img = fixOrientation(img); // Create thumbnail (64x64 square for list view - 2x for retina) thumb = createSquareThumb(img, 128); -imageWrite(thumb, "#itemsDir#/#itemId#_thumb.jpg", 0.85); +thumbPath = "#itemsDir#/#itemId#_thumb.jpg"; +imageWrite(thumb, thumbPath, 0.85); +fileSetAccessMode(thumbPath, "644"); // Create medium size (400px max for detail view) medium = imageCopy(img, 0, 0, imageGetWidth(img), imageGetHeight(img)); medium = resizeToFit(medium, 400); -imageWrite(medium, "#itemsDir#/#itemId#_medium.jpg", 0.85); +mediumPath = "#itemsDir#/#itemId#_medium.jpg"; +imageWrite(medium, mediumPath, 0.85); +fileSetAccessMode(mediumPath, "644"); // Save full size (max 1200px to keep file sizes reasonable) img = resizeToFit(img, 1200); -imageWrite(img, "#itemsDir#/#itemId#.jpg", 0.90); +fullPath = "#itemsDir#/#itemId#.jpg"; +imageWrite(img, fullPath, 0.90); +fileSetAccessMode(fullPath, "644"); // Delete the original uploaded file try { fileDelete(uploadedFile); } catch (any e) {} diff --git a/api/tasks/callServer.cfm b/api/tasks/callServer.cfm index d74e295..f29fb04 100644 --- a/api/tasks/callServer.cfm +++ b/api/tasks/callServer.cfm @@ -58,20 +58,14 @@ try { } } - // Get task type info if TaskTypeID provided (name + category) + // Get task type name if TaskTypeID provided taskTypeName = ""; - taskTypeCategoryID = 0; if (taskTypeID > 0) { typeQuery = queryExecute(" - SELECT Name, TaskCategoryID FROM tt_TaskTypes WHERE ID = :typeID + SELECT Name FROM tt_TaskTypes WHERE ID = :typeID ", { typeID: { value: taskTypeID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" }); - if (typeQuery.recordCount) { - if (len(trim(typeQuery.Name))) { - taskTypeName = typeQuery.Name; - } - if (!isNull(typeQuery.TaskCategoryID) && isNumeric(typeQuery.TaskCategoryID) && typeQuery.TaskCategoryID > 0) { - taskTypeCategoryID = typeQuery.TaskCategoryID; - } + if (typeQuery.recordCount && len(trim(typeQuery.Name))) { + taskTypeName = typeQuery.Name; } } @@ -96,40 +90,12 @@ try { taskDetails &= "Customer is requesting assistance"; } - // Determine category: use task type's category if set, otherwise fallback to "Service" category - categoryID = 0; - - if (taskTypeCategoryID > 0) { - // Use the task type's assigned category - categoryID = taskTypeCategoryID; - } else { - // Fallback: look up or create a "Service" category for this business - catQuery = queryExecute(" - SELECT ID FROM TaskCategories - WHERE BusinessID = :businessID AND Name = 'Service' - LIMIT 1 - ", { businessID: { value: businessID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" }); - - if (catQuery.recordCount == 0) { - // Create the category - queryExecute(" - INSERT INTO TaskCategories (BusinessID, Name, Color) - VALUES (:businessID, 'Service', '##FF9800') - ", { businessID: { value: businessID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" }); - - catResult = queryExecute("SELECT LAST_INSERT_ID() as newID", [], { datasource: "payfrit" }); - categoryID = catResult.newID; - } else { - categoryID = catQuery.ID; - } - } - - // Insert task + // Insert task (no CategoryID - using TaskTypeID only) queryExecute(" INSERT INTO Tasks ( BusinessID, + ServicePointID, UserID, - CategoryID, OrderID, TaskTypeID, Title, @@ -138,8 +104,8 @@ try { CreatedOn ) VALUES ( :businessID, + :servicePointID, :userID, - :categoryID, :orderID, :taskTypeID, :title, @@ -149,8 +115,8 @@ try { ) ", { businessID: { value: businessID, cfsqltype: "cf_sql_integer" }, + servicePointID: { value: servicePointID, cfsqltype: "cf_sql_integer" }, userID: { value: userID > 0 ? userID : javaCast("null", ""), cfsqltype: "cf_sql_integer", null: userID == 0 }, - categoryID: { value: categoryID, cfsqltype: "cf_sql_integer" }, orderID: { value: orderID > 0 ? orderID : javaCast("null", ""), cfsqltype: "cf_sql_integer", null: orderID == 0 }, taskTypeID: { value: taskTypeID, cfsqltype: "cf_sql_integer" }, title: { value: taskTitle, cfsqltype: "cf_sql_varchar" }, diff --git a/api/tasks/create.cfm b/api/tasks/create.cfm index cf31041..95ac20c 100644 --- a/api/tasks/create.cfm +++ b/api/tasks/create.cfm @@ -29,55 +29,25 @@ try { userID = val(jsonData.UserID ?: 0); message = jsonData.Message ?: ""; - // Get task type info for display (including category) + // Get task type name for display taskTypeQuery = queryTimed(" - SELECT Name, Color, Icon, TaskCategoryID - FROM tt_TaskTypes - WHERE ID = :taskTypeID + SELECT Name FROM tt_TaskTypes WHERE ID = :taskTypeID ", { taskTypeID: taskTypeID }, { datasource: "payfrit" }); taskTitle = message; - categoryID = 0; - if (taskTypeQuery.recordCount) { - if (len(trim(taskTypeQuery.Name))) { - taskTitle = taskTypeQuery.Name; - } - if (!isNull(taskTypeQuery.TaskCategoryID) && isNumeric(taskTypeQuery.TaskCategoryID) && taskTypeQuery.TaskCategoryID > 0) { - categoryID = taskTypeQuery.TaskCategoryID; - } - } - - // If no category from task type, look up or create default "Service" category - if (categoryID == 0) { - catQuery = queryTimed(" - SELECT ID FROM TaskCategories - WHERE BusinessID = :businessID AND Name = 'Service' - LIMIT 1 - ", { businessID: businessID }, { datasource: "payfrit" }); - - if (catQuery.recordCount > 0) { - categoryID = catQuery.ID; - } else { - // Create the Service category - queryTimed(" - INSERT INTO TaskCategories (BusinessID, Name, Color) - VALUES (:businessID, 'Service', '##FF9800') - ", { businessID: businessID }, { datasource: "payfrit" }); - catResult = queryTimed("SELECT LAST_INSERT_ID() as newID", {}, { datasource: "payfrit" }); - categoryID = catResult.newID; - } + if (taskTypeQuery.recordCount && len(trim(taskTypeQuery.Name))) { + taskTitle = taskTypeQuery.Name; } // Use message as details taskDetails = message; - // Insert service bell task with ServicePointID and UserID + // Insert service bell task (no CategoryID - using TaskTypeID only) queryTimed(" INSERT INTO Tasks ( BusinessID, ServicePointID, TaskTypeID, - CategoryID, OrderID, UserID, Title, @@ -88,7 +58,6 @@ try { :businessID, :servicePointID, :taskTypeID, - :categoryID, :orderID, :userID, :taskTitle, @@ -100,7 +69,6 @@ try { businessID: businessID, servicePointID: servicePointID, taskTypeID: taskTypeID, - categoryID: categoryID, orderID: orderID, userID: userID, taskTitle: taskTitle, diff --git a/api/tasks/getDetails.cfm b/api/tasks/getDetails.cfm index 557663b..dfd723f 100644 --- a/api/tasks/getDetails.cfm +++ b/api/tasks/getDetails.cfm @@ -34,20 +34,18 @@ - + - + @@ -46,10 +46,10 @@ - - - - + + + + @@ -58,7 +58,6 @@ SELECT t.ID, t.BusinessID, - t.CategoryID, t.OrderID, t.TaskTypeID, t.Title, @@ -66,8 +65,6 @@ t.CreatedOn, t.ClaimedByUserID, t.UserID, - tc.Name AS CategoryName, - tc.Color AS CategoryColor, tt.Name AS TaskTypeName, tt.Icon AS TaskTypeIcon, tt.Color AS TaskTypeColor, @@ -76,7 +73,6 @@ u.FirstName AS CustomerFirstName, u.LastName AS CustomerLastName FROM Tasks t - LEFT JOIN TaskCategories tc ON tc.ID = t.CategoryID LEFT JOIN tt_TaskTypes tt ON tt.ID = t.TaskTypeID LEFT JOIN Orders o ON o.ID = t.OrderID LEFT JOIN ServicePoints sp ON sp.ID = COALESCE(t.ServicePointID, o.ServicePointID) @@ -111,7 +107,6 @@ + ", [ { value = UserID, cfsqltype = "cf_sql_integer" }, { value = UserID, cfsqltype = "cf_sql_integer" } ], { datasource = "payfrit" })> @@ -59,7 +60,8 @@ "Address": "", "City": "", "StatusID": qBusinesses.StatusID, - "PendingTaskCount": qBusinesses.PendingTaskCount + "PendingTaskCount": qBusinesses.PendingTaskCount, + "ActiveTaskCount": qBusinesses.ActiveTaskCount })> diff --git a/hud/hud.js b/hud/hud.js index 3059e5f..6e6c948 100644 --- a/hud/hud.js +++ b/hud/hud.js @@ -225,13 +225,16 @@ const HUD = { // Get elapsed seconds since task creation getElapsedSeconds(createdOn) { + if (!createdOn) return 0; const created = new Date(createdOn); + if (isNaN(created.getTime())) return 0; const now = new Date(); - return Math.floor((now - created) / 1000); + return Math.max(0, Math.floor((now - created) / 1000)); }, // Format elapsed time as mm:ss formatElapsed(seconds) { + if (isNaN(seconds) || seconds < 0) seconds = 0; const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${String(secs).padStart(2, '0')}`; diff --git a/portal/login.html b/portal/login.html index 68d3a84..5715cf7 100644 --- a/portal/login.html +++ b/portal/login.html @@ -4,772 +4,14 @@ Login - Payfrit Business Portal - - + - - - +

Redirecting...

diff --git a/portal/menu-builder.html b/portal/menu-builder.html index 441b486..2bc0159 100644 --- a/portal/menu-builder.html +++ b/portal/menu-builder.html @@ -1,4376 +1,4442 @@ - - - - - - Menu Builder - Payfrit - - - - - -
- - - - -
-
- -
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
- -
-

Components

-
-
-
๐Ÿ“
-
-
Category
-
Group of menu items
-
-
-
-
๐Ÿฝ๏ธ
-
-
Menu Item
-
Orderable product
-
-
-
-
โš™๏ธ
-
-
Modifier
-
Add-on or option
-
-
-
-
๐Ÿ“‹
-
-
Modifier Group
-
Group of options
-
-
-
- -

Modifier Templates

-
-
Loading templates...
-
- -

Quick Stats

-
-
- Categories: - 0 -
-
- Items: - 0 -
-
- Templates: - 0 -
-
- Photos Missing: - 0 -
-
- -

Branding

-
- - -

- Header: 1200x400px recommended
- Color: Used for category bars in the app -

-
-
- - -
-
-

- Menu Builder -

-
- -
-
- - -
- - -
-
-

Properties

-
- -
-

- Select an item to edit its properties -

-
-
-
-
-
-
- - -
-
- - - - - Edit - Enter -
-
- - - - - Clone - Ctrl+D -
-
- - - - - - Create Photo Task -
-
-
- - - - Move Up - Alt+Up -
-
- - - - Move Down - Alt+Down -
-
-
- - - - Delete - Del -
-
- - - - - -
- - - - + + + + + + Menu Builder - Payfrit + + + + + +
+ + + + +
+
+
+

Menu Builder

+
+ +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+ +
+

Components

+
+
+
๐Ÿ“
+
+
Category
+
Group of menu items
+
+
+
+
๐Ÿฝ๏ธ
+
+
Menu Item
+
Orderable product
+
+
+
+
โš™๏ธ
+
+
Modifier
+
Add-on or option
+
+
+
+
๐Ÿ“‹
+
+
Modifier Group
+
Group of options
+
+
+
+ +

Modifier Templates

+
+
Loading templates...
+
+ +

Quick Stats

+
+
+ Categories: + 0 +
+
+ Items: + 0 +
+
+ Templates: + 0 +
+
+ Photos Missing: + 0 +
+
+ +

Branding

+
+ + +

+ Header: 1200x400px recommended
+ Color: Used for category bars in the app +

+
+
+ + +
+
+

+ Menu Builder +

+
+ +
+
+ + +
+ + +
+
+

Properties

+
+ +
+

+ Select an item to edit its properties +

+
+
+
+
+
+
+ + +
+
+ + + + + Edit + Enter +
+
+ + + + + Clone + Ctrl+D +
+
+ + + + + + Create Photo Task +
+
+
+ + + + Move Up + Alt+Up +
+
+ + + + Move Down + Alt+Down +
+
+
+ + + + Delete + Del +
+
+ + + + + +
+ + + + diff --git a/portal/portal.js b/portal/portal.js index 468ffd3..fa3c796 100644 --- a/portal/portal.js +++ b/portal/portal.js @@ -56,21 +56,21 @@ const Portal = { const savedBusiness = localStorage.getItem('payfrit_portal_business'); const userId = localStorage.getItem('payfrit_portal_userid'); - if (!token || !savedBusiness) { - // Not logged in - redirect to login + if (!token) { + // No token - redirect to signup localStorage.removeItem('payfrit_portal_token'); localStorage.removeItem('payfrit_portal_userid'); localStorage.removeItem('payfrit_portal_business'); - window.location.href = BASE_PATH + '/portal/login.html'; + window.location.href = BASE_PATH + '/portal/signup.html'; return; } - // Use saved business ID from localStorage - this.config.businessId = parseInt(savedBusiness) || null; + // Use saved business ID from localStorage (might be null) + this.config.businessId = savedBusiness ? parseInt(savedBusiness) : null; this.config.userId = parseInt(userId) || 1; this.config.token = token; - // Verify user has access to this business + // Verify user has access to this business (or get their businesses if none selected) try { const response = await fetch(`${this.config.apiBaseUrl}/portal/myBusinesses.cfm`, { method: 'POST', @@ -82,18 +82,30 @@ const Portal = { }); const data = await response.json(); - if (data.OK && data.BUSINESSES) { - const hasAccess = data.BUSINESSES.some(b => b.BusinessID === this.config.businessId); - if (!hasAccess && data.BUSINESSES.length > 0) { - // User doesn't have access to requested business, use their first business - this.config.businessId = data.BUSINESSES[0].BusinessID; - localStorage.setItem('payfrit_portal_business', this.config.businessId); - } else if (!hasAccess) { - // User has no businesses - this.toast('No businesses associated with your account', 'error'); - this.logout(); - return; + if (data.OK && data.BUSINESSES && data.BUSINESSES.length > 0) { + if (!this.config.businessId) { + // No business selected + if (data.BUSINESSES.length === 1) { + // Only one business - auto-select it + this.config.businessId = data.BUSINESSES[0].BusinessID; + localStorage.setItem('payfrit_portal_business', this.config.businessId); + } else { + // Multiple businesses - show chooser + this.showBusinessChooser(data.BUSINESSES); + return; + } + } else { + const hasAccess = data.BUSINESSES.some(b => b.BusinessID === this.config.businessId); + if (!hasAccess) { + // User doesn't have access to requested business - show chooser + this.showBusinessChooser(data.BUSINESSES); + return; + } } + } else { + // User has no businesses - go to setup wizard + window.location.href = BASE_PATH + '/portal/setup-wizard.html'; + return; } } catch (err) { console.error('[Portal] Auth verification error:', err); @@ -133,14 +145,69 @@ const Portal = { localStorage.removeItem('payfrit_portal_token'); localStorage.removeItem('payfrit_portal_userid'); localStorage.removeItem('payfrit_portal_business'); - window.location.href = BASE_PATH + '/portal/login.html'; + window.location.href = BASE_PATH + '/portal/signup.html'; }, - // Switch to a different business (go back to login to select) - switchBusiness() { - // Clear current business selection but keep token - localStorage.removeItem('payfrit_portal_business'); - window.location.href = BASE_PATH + '/portal/login.html'; + // Switch to a different business - show chooser modal + async switchBusiness() { + const token = localStorage.getItem('payfrit_portal_token'); + const userId = localStorage.getItem('payfrit_portal_userid'); + + try { + const response = await fetch(`${this.config.apiBaseUrl}/portal/myBusinesses.cfm`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-User-Token': token }, + body: JSON.stringify({ UserID: userId }) + }); + const data = await response.json(); + + if (data.OK && data.BUSINESSES && data.BUSINESSES.length > 0) { + this.showBusinessChooser(data.BUSINESSES); + } else { + window.location.href = BASE_PATH + '/portal/setup-wizard.html'; + } + } catch (err) { + console.error('[Portal] Failed to load businesses:', err); + } + }, + + showBusinessChooser(businesses) { + // Remove existing modal if any + const existing = document.getElementById('businessChooserModal'); + if (existing) existing.remove(); + + const modal = document.createElement('div'); + modal.id = 'businessChooserModal'; + modal.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:9999'; + + const content = document.createElement('div'); + content.style.cssText = 'background:white;color:#333;border-radius:12px;padding:24px;max-width:400px;width:90%;max-height:80vh;overflow-y:auto'; + + content.innerHTML = ` +

Select Business

+
+ + `; + + const list = content.querySelector('#businessList'); + businesses.forEach(biz => { + const item = document.createElement('div'); + item.style.cssText = 'padding:12px;border:1px solid #e0e0e0;border-radius:8px;margin-bottom:8px;cursor:pointer;transition:background 0.2s;color:#333;background:white'; + item.innerHTML = `${biz.BusinessName || biz.Name}`; + item.onmouseover = () => item.style.background = '#f5f5f5'; + item.onmouseout = () => item.style.background = 'white'; + item.onclick = () => { + localStorage.setItem('payfrit_portal_business', biz.BusinessID); + window.location.reload(); + }; + list.appendChild(item); + }); + + content.querySelector('#closeChooser').onclick = () => modal.remove(); + modal.onclick = (e) => { if (e.target === modal) modal.remove(); }; + + modal.appendChild(content); + document.body.appendChild(modal); }, // Add a new business (go to setup wizard) diff --git a/portal/setup-wizard.html b/portal/setup-wizard.html index 4ccc430..c3be3eb 100644 --- a/portal/setup-wizard.html +++ b/portal/setup-wizard.html @@ -718,7 +718,7 @@
Online
- + @@ -1077,7 +1077,7 @@ // Check if user is logged in const userId = localStorage.getItem('payfrit_portal_userid'); if (!userId) { - window.location.href = 'login.html'; + window.location.href = 'signup.html'; return; } config.userId = userId; @@ -2525,7 +2525,7 @@ localStorage.removeItem('payfrit_portal_token'); localStorage.removeItem('payfrit_portal_userid'); localStorage.removeItem('payfrit_portal_business'); - window.location.href = 'login.html'; + window.location.href = 'signup.html'; } diff --git a/portal/signup.html b/portal/signup.html index 3420b39..a31baab 100644 --- a/portal/signup.html +++ b/portal/signup.html @@ -314,8 +314,7 @@ @@ -806,8 +805,11 @@ console.log('[Signup] myBusinesses response:', data); if (data.OK && data.BUSINESSES && data.BUSINESSES.length > 0) { - // Has businesses - go to login for business selection - window.location.href = BASE_PATH + '/portal/login.html'; + // Has businesses - go to portal (chooser will show if multiple) + if (data.BUSINESSES.length === 1) { + localStorage.setItem('payfrit_portal_business', data.BUSINESSES[0].BusinessID); + } + window.location.href = BASE_PATH + '/portal/index.html'; } else { // No businesses - go to wizard this.redirectToWizard(); diff --git a/portal/station-assignment.html b/portal/station-assignment.html index be3c1c9..ae10d1b 100644 --- a/portal/station-assignment.html +++ b/portal/station-assignment.html @@ -437,7 +437,7 @@ const savedBusiness = localStorage.getItem('payfrit_portal_business'); if (!token || !savedBusiness) { - window.location.href = BASE_PATH + '/portal/login.html'; + window.location.href = BASE_PATH + '/portal/signup.html'; return; }