Commit graph

550 commits

Author SHA1 Message Date
John Pinkyfloyd
4109e3dac4 Enable magic OTP on production for App Store review
Code 123456 will work for any phone number to facilitate
App Store review testing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-06 16:23:19 -08:00
John Mizerek
1568686ff1 Add BrandColorLight to menu API and setup wizard
- items.cfm: return BrandColorLight in menu response
- saveWizard.cfm: save BrandColorLight during business creation
- setup-wizard.html: second color picker for light brand color

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 19:24:45 -08:00
John Mizerek
771f70f2f3 Add BrandColorLight to portal settings page
Updated the portal settings brand color picker to show both dark
and light colors. Light swatch shown alongside dark swatch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 15:09:30 -08:00
John Mizerek
3bd7585383 Add BrandColorLight for subtle card tinting in menu builder
New BrandColorLight column on Businesses table. Menu builder cards
(categories, subcategories, items) get a very subtle tint from the
light brand color. White when no color is set. Brand color picker
now has both dark and light fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 14:48:11 -08:00
John Pinkyfloyd
b9755a1e72 Add role-aware cash routing and backend improvements
Staff cash goes to worker payout ledger, admin/manager cash deletes
pending payout and reverses withholding. Add RoleID to myBusinesses
response. Various order and webhook improvements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:04:46 -08:00
John Mizerek
9c09e18833 Add sidebar navigation to station-assignment page
Station assignment was a standalone page with no sidebar. Added the
same portal sidebar so users can navigate between pages. Also added
Stations link to menu-builder sidebar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 20:57:18 -08:00
John Mizerek
2d3634693b Add Show Modifiers toggle to menu builder outline view
Adds button in the outline modal to toggle modifier group display
under each item. Each group shows a + to expand into full options
with prices. Useful for debugging modifier assignments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 19:57:22 -08:00
John Mizerek
9000fc91fe Fix Show Modifiers button not rendering in template literal
Move the conditional out of the nested template literal to avoid
parsing issues with arrow functions inside backtick strings. Also
skip empty mod-groups divs on items without modifiers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 19:53:44 -08:00
John Mizerek
a67d225164 Add Show Modifiers toggle to items step in setup wizard
Outline button reveals modifier groups under each item with a +
expand button to drill into individual options and prices. Helps
debug imported menus before saving.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 19:50:46 -08:00
John Mizerek
a44dfd79ae Fix every-item-as-category pattern in menu import
Post-process Claude menu extraction to detect when >60% of categories
have exactly 1 item (a common misparse). Collapses pseudo-categories
into the nearest preceding real (0-item) category.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:51:09 -08:00
John Mizerek
84985d98d8 Improve Claude prompt to distinguish categories from items
Added explicit guidance that categories are broad section headings
(5-15 typical) and items are individual products (30-150 typical).
Prevents Claude from treating each menu item as its own category.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:45:33 -08:00
John Mizerek
0bb2707904 Skip header image step when no auto-detected header
For URL imports (Toast, Grubhub, etc.) there's no header image
to show, so skip straight from business info to categories
instead of showing an empty "Choose Image / Skip" prompt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:39:46 -08:00
John Mizerek
983ba7c2e4 Fix image media type detection from content, not extension
Claude API rejects images when the declared media type doesn't
match the actual content. Now detects JPEG/PNG/GIF/WebP from
base64 magic bytes instead of trusting file extensions or
Content-Type headers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 14:33:39 -08:00
John Mizerek
03c675336e fix: harden tab expiry cron against silent capture failures
- Check PI state on Stripe before capture/cancel (requires_capture,
  canceled, requires_payment_method, etc.)
- Add application_fee_amount for Stripe Connect (was missing — all
  money went to connected account on auto-close)
- Re-verify tab StatusID=1 before processing (prevents race with
  user-initiated close)
- Add WHERE StatusID=1 to closing UPDATE (prevents overwriting
  concurrent user close)
- Log full Stripe response status and HTTP code for debugging
- Handle already-cancelled PIs gracefully (mark tab expired)
- Handle unconfirmed PIs (cancel and mark expired)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 13:00:39 -08:00
John Mizerek
496ef74c4c fix: prevent tab cancel when approved orders exist
cancel.cfm previously only blocked cancel if orders had StatusID >= 1
(submitted to kitchen). If submitOrder() failed after addOrderToTab()
succeeded, the order stayed at StatusID 0 but was linked to the tab
with ApprovalStatus='approved'. This allowed cancelling the tab free.

Now checks TabOrders directly — any approved orders with non-zero
subtotals block the cancel, regardless of kitchen submission status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 12:56:14 -08:00
John Mizerek
c40e5c0181 Add Grubhub menu import via API
Detect Grubhub URLs and fetch menu data directly via their
REST API instead of scraping HTML. Gets anonymous auth token,
then fetches full restaurant data including categories, items,
modifiers, prices, hours, lat/lng, tax rate, and item images.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 12:20:53 -08:00
John Mizerek
2f9bb2b869 Extract lat/lng from Toast and save directly to address
Toast provides latitude/longitude in the location object. Extract
in analyzeMenuUrl.cfm and pass through to saveWizard.cfm, which
now includes lat/lng in the address INSERT. Skips the background
Nominatim geocode when coordinates are already available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:47:32 -08:00
John Mizerek
9225a53eee Fix saveWizard.cfm Lucee 7 syntax error
Convert cfthread from tag-based to cfscript syntax. Lucee 7
no longer allows </cfscript> mid-file to switch to tag mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:41:30 -08:00
John Mizerek
2b441e166e Extract business hours from Toast schedule data
Parses upcomingSchedules from ROOT_QUERY.restaurantV2.schedule
and formats as "Mon 7:30am-6:30pm, Tue 7:30am-6:30pm" text string
that the setup wizard's parseHoursString() can consume.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:24:45 -08:00
John Mizerek
4351978c10 Fix Toast menu import for new URL format and prices array
- Add item.prices array support to first code path (was only checking
  item.price scalar, but Toast now uses prices: [4.50] array)
- Extract individual address fields (addressLine1, city, state, zip)
  from ROOT_QUERY restaurant data for saveWizard compatibility
- Update modifier extraction URL detection to match any toasttab.com
  domain (not just order.toasttab.com)
- Update slug-based URL construction to use www.toasttab.com/local/order/
  format instead of deprecated order.toasttab.com/online/ format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:45:28 -08:00
John Mizerek
d822fcad5a feat: per-station item completion on KDS
Each station can mark their items done independently. When all
stations are done, the order auto-promotes to Ready (status 3)
and delivery/pickup tasks are created automatically.

- New markStationDone.cfm endpoint for per-station completion
- Extract task creation into shared _createOrderTasks.cfm include
- Add line item StatusID to listForKDS.cfm response
- KDS shows per-station "Mark Station Done" button when filtered
- Done items display with strikethrough and checkmark
- Manager view retains full manual control (no station selected)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:19:25 -08:00
John Mizerek
f3a41bf01a fix: validate Stripe customer before using, handle mode mismatch
If a user has a live-mode StripeCustomerId but the API is running in
test mode (or vice versa), the PI creation fails. Now validates the
customer with Stripe first and creates a new one if invalid.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:06:09 -08:00
John Mizerek
5dd0884b8f feat: include SessionEnabled in menu items API response
Android app uses this to show/hide the tab icon per business.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:34:08 -08:00
John Mizerek
576c90fffd Fix DISTINCT ORDER BY error in KDS station-filtered query
MySQL rejects ORDER BY o.SubmittedOn when SELECT DISTINCT has
DATE_FORMAT(o.SubmittedOn) AS SubmittedOn. Use the alias instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:30:44 -08:00
John Mizerek
49e3c812c9 Fix field name mismatch in station-assignment page
API returns Name, Price, ParentItemID, CategoryID, StationID but JS
was referencing ItemName, ItemPrice, ItemParentItemID, etc. causing
all items to be filtered out and not displayed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:03:45 -08:00
John Mizerek
a2ed13981e Fix CFML hash escape in station save default color
## in CFML strings is required for literal # characters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 16:49:07 -08:00
John Mizerek
7205962873 Add Stations link to portal sidebar navigation
Links to station-assignment.html, placed after Menu link.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 16:48:00 -08:00
John Mizerek
f5ff9cdfeb Add station CRUD endpoints and wire into portal UI
New endpoints: save.cfm (combined add/update) and delete.cfm
(soft-delete with item unassignment). Registered in Application.cfm.

Portal station-assignment page now uses real API calls for add,
edit, and delete instead of client-side-only prompt(). Fixed key
naming mismatch (StationName/StationColor → Name/Color) so the
page works with real API data. Removed hardcoded demo fallbacks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 16:26:45 -08:00
John Mizerek
94b5bbbce1 KDS: per-station line item filtering with expand toggle
Backend returns all line items for every order (removes station
filter from sub-query). Frontend filters by station, showing only
relevant items by default. An expand toggle reveals other stations'
items dimmed at 35% opacity for full order context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:30:59 -08:00
John Mizerek
c65cd8242b Round balance amounts to cents before applying
Prevents sub-cent precision (e.g. $0.883125) from accumulating in
BalanceApplied and payment records. All balance math now rounds to
nearest cent first.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:32:54 -08:00
John Mizerek
c580e6ec78 Auto-apply user balance on cash and card orders
Balance from cash change now silently reduces the amount owed on the
next order. For cash: deducted immediately in submitCash, reduces cash
the worker needs to collect (or skips cash task entirely if fully
covered). For card: reduces the Stripe PaymentIntent amount, deducted
in webhook on successful payment. Receipt shows "Balance applied" line.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:16:21 -08:00
John Mizerek
96c2ed3fc1 Fix cash payment fee: use real Payfrit platform fee, not 2.25% cash handling fee
submitCash.cfm: Calculate platform fee from subtotal * PayfritFee,
store in Orders.PlatformFee and Payments.PaymentPayfritsCut on submission.

complete.cfm: Replace bogus 2.25% cash transaction fee with the real
platform fee (customer fee + business fee = 2 × PayfritFee × subtotal).
Credit full Payfrit revenue to User 0. Record business fee in
PaymentPayfritNetworkFees.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 12:59:22 -08:00
John Mizerek
cb7e3b7fc6 fix: include PaymentFromCreditCard in cash payment INSERT
PaymentFromCreditCard column is NOT NULL with no default value,
causing INSERT to fail silently. Set to 0 for cash payments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 12:44:53 -08:00
John Mizerek
eda8010927 Fix restaurant distance sorting to use numeric comparison
CFML compare() does string comparison, causing distances like 1.9 to
sort after 12.3. Switched to numeric < > operators.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 11:57:45 -08:00
John Mizerek
8adac1a242 Fix submitCash.cfm for legacy Payments table columns
Payments table still uses old prefixed names (PaymentID as PK, no UUID
column, no PaymentTip column, required PaymentReceivedByUserID). Updated
INSERT to match actual production schema.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 11:29:40 -08:00
John Mizerek
1956dd08e9 Add noon/midnight clarification note above hours table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 10:38:46 -08:00
John Mizerek
ff24cfa79b Fix column names: Latitude/Longitude not Lat/Lng
The Addresses table uses Latitude and Longitude column names,
not Lat and Lng.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 01:13:47 -08:00
John Mizerek
4e0cc65ba2 Auto-geocode addresses on create/update
Extract geocoding functions into shared api/inc/geocode.cfm and call
geocodeAddressById() via cfthread after every address INSERT or UPDATE.
Uses OpenStreetMap Nominatim (free, no API key). Non-blocking — the
HTTP call runs in a background thread so responses aren't delayed.

Affected endpoints:
- setup/saveWizard.cfm (new business creation)
- businesses/update.cfm (business address update)
- portal/updateSettings.cfm (portal settings save)
- addresses/add.cfm (customer delivery address)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 01:00:07 -08:00
John Mizerek
dc5148d1b8 Fix properties panel viewport overflow
Constrain main-content to 100vh and builder-wrapper to flex: 1
so the height chain propagates correctly and the properties panel
scrolls within the viewport instead of extending off screen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:25:08 -08:00
John Mizerek
195c7d8b3b Fix properties panel overflow and make header sticky
Properties panel was extending off screen due to missing min-height: 0
on flex containers. Made Properties header sticky so it floats as you
scroll through long modifier lists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:22:53 -08:00
John Mizerek
2bcdcbbed7 Fix subcategory expand/collapse in visual menu editor
Subcategories shared expandedCategoryId with parent categories,
so clicking a subcategory collapsed the parent. Added separate
expandedSubCategoryId state so subcategories expand independently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:58:20 -08:00
John Mizerek
3a9f952d8b Fix Toast __OO_STATE__ extraction: remove tag stripping and var keywords
Tag stripping via reReplace on 1.7M HTML was likely causing the silent
failure on biz server. Brace-counting doesn't need tag stripping since
HTML tags don't contain { or } and attribute quotes come in balanced
pairs. Also removed var keywords from page-level cfscript (may not be
supported in Lucee at template level) and added detailed error output
to the cfcatch for debugging.

Also auto-detect dev/prod environment from hostname instead of
hardcoded flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:28:02 -08:00
John Mizerek
e7aaae58b7 Replace regex extraction with brace-counting for __OO_STATE__
The regex .*? (non-greedy) fails on 500K+ JSON due to Java regex
backtracking limits, causing truncated data (only 3 of 6 menus
extracted). Replace all 3 extraction points with cfscript
brace-counting that reliably handles any JSON size.

Also decode HTML entities (&amp; -> &, &lt; -> <, etc.) from
Chrome View Source saves.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 19:18:17 -08:00
John Mizerek
22e89b2dd3 Fix __OO_STATE__ extraction for Chrome View Source HTML
Chrome's Ctrl+U (View Source) saves wraps content in <span> tags
which breaks the regex termination pattern ;\s*window\. because
HTML tags appear between ; and the next window. variable.

Strip HTML tags from a working copy before regex extraction when
View Source format is detected (presence of <span id="line" tags).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 18:45:34 -08:00
John Mizerek
4684936595 Add parent/child category hierarchy for Toast menus
Toast pages with multiple menus (e.g. "Food", "Beverages", "Merchandise")
now produce parent categories from the menu names with subcategories from
the groups within each menu, using the parentCategoryName field the wizard
already supports.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 18:18:13 -08:00
John Mizerek
aca3ba18a1 Add Toast modifier extraction via Playwright
When analyzing Toast menu pages, items with modifiers now have their
modifier groups extracted by clicking each item in a headless browser
and intercepting the GraphQL MenuItemDetails responses. Extracted
modifiers include group name, required/optional flag, min/max selections,
and option names with prices. Items sharing the same itemGroupGuid
inherit modifiers from successfully mapped siblings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:48:48 -08:00
John Mizerek
30dd0997b9 Seed order-fulfillment task types on business creation
Add Deliver to Table, Order Ready for Pickup, and Deliver to Address
to the default task types created by the setup wizard. These are
required by updateStatus.cfm to auto-create tasks at status 3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 13:13:08 -08:00
John Mizerek
61949586f5 Fix mismatched cfif/cftry tags in updateStatus.cfm
Extra </cfif> at line 243 had no matching opener. Was hidden by
Lucee's template cache until the Tomcat restart exposed it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 13:01:49 -08:00
John Mizerek
4e0c2ee1bf Remove timezone conversion hacks — MySQL now runs in UTC
All servers (db, dev, biz) migrated to UTC:
- MySQL default-time-zone changed from America/Los_Angeles to +00:00
- All existing datetime data converted from Pacific to UTC
- JVM timezone set to UTC on dev and biz servers
- OS timezone set to UTC on all three servers

Removed CONVERT_TZ hack from listForKDS.cfm — data is natively UTC.
Simplified toISO8601() — no dateConvert needed since data is already UTC.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 12:37:30 -08:00
John Mizerek
4b35c27f75 Fix KDS timestamps: CONVERT_TZ from MySQL local time to UTC
MySQL is in America/Los_Angeles, not UTC. Use CONVERT_TZ to properly
convert to UTC before appending the Z suffix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 12:20:39 -08:00