Commit graph

413 commits

Author SHA1 Message Date
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
John Mizerek
4dfdd6ee8b Fix KDS UTC timestamps: format in SQL to bypass Lucee timezone shift
dateTimeFormat() converts to JVM local time before formatting, so
appending 'Z' was wrong. Use DATE_FORMAT in SQL instead since the DB
values are already UTC.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 12:18:37 -08:00
John Mizerek
453188d63f Fix KDS elapsed time by tagging SubmittedOn as UTC
DB stores timestamps in UTC but the API returned them without timezone
info, causing JS to parse them as local time. Append 'Z' suffix so
new Date() correctly interprets them as UTC.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 12:14:47 -08:00
John Mizerek
f09777eaa5 Add get_beacon_config endpoint — single call for all beacon config
Combines allocate_business_namespace + allocate_servicepoint_minor into one
endpoint that returns UUID, Major, Minor, MeasuredPower, AdvInterval, TxPower,
ServicePointName, and BusinessName. No more hardcoded defaults on the client.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:20:16 -08:00
John Mizerek
95dc4c49fc Strip address from business name when Toast embeds it in the name field
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:12:58 -08:00
John Mizerek
e403e49487 Fix Toast OO_STATE: restaurant from ROOT_QUERY, prices from prices[]
- Restaurant info is in ROOT_QUERY.restaurantV2(...) keys, not
  Restaurant:* top-level keys (Apollo cache format)
- Prices are in item.prices array [4.50], not item.price scalar
- Added null checks for imageUrls (can be null, not missing)
- Fallback to title tag for business name

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:11:44 -08:00
John Mizerek
a0d86d6e87 Add Toast __OO_STATE__ fast-path for URL-fetched menu pages
Instead of sending 450KB of HTML to Claude (which truncates to 100K
and only extracts ~60 items), parse the structured __OO_STATE__ data
directly on the server. This captures all menus, groups, items, prices,
and images from Toast pages - 169 items for Jus Family Cafe vs 60 before.

Falls back to Claude analysis if __OO_STATE__ parsing fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:08:07 -08:00
John Mizerek
ffedc26150 Add subcategories stat row to menu summary card in setup wizard
Shows subcategory count as a separate indented row beneath categories
when subcategories are present.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:37:27 -08:00
John Mizerek
3c0311c1d5 Fix header image preview aspect ratio in setup wizard
Changed from fixed 120px height to aspect-ratio 3:1 to match the
recommended 1200x400 dimensions, preventing heavy cropping.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:36:00 -08:00
John Mizerek
ced4082993 Fix JSON parsing when Claude returns text preamble before menu JSON
The Claude API sometimes returns explanatory text before the JSON
response even when instructed to return only JSON. Added extraction
logic to find the first { character and strip any leading text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:30:21 -08:00
John Mizerek
9acf4aa511 Add server-side h2/h3 hierarchy detection for subcategory discovery
- Parse HTML heading structure to detect h2 parents with h3 subcategories
- Append detected hierarchy to Claude prompt as explicit hint
- Post-process Claude response to enforce hierarchy even if Claude returns flat

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:36:36 -08:00
John Mizerek
495b03c76d Add subcategory detection to wizard URL analyzer and display
- analyzeMenuUrl.cfm: Detect subcategories from Toast subgroups and
  Claude API responses, preserve hierarchy with parentCategoryName
- setup-wizard.html: Display subcategories indented under parents
  throughout wizard flow (categories step, items review, summary, preview)
- menu-builder.html: Show subcategories nested in outline modal view

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:08:59 -08:00
John Mizerek
3ccc82c9f2 Add subcategory support (2 levels deep)
- saveCategory.cfm: Accept ParentCategoryID, enforce max 2-level nesting
- items.cfm: Include ParentCategoryID on virtual category rows for Android
- getForBuilder.cfm: Return ParentCategoryID in builder API response
- saveFromBuilder.cfm: Persist ParentCategoryID on save, track JS-to-DB id mapping
- saveWizard.cfm: Two-pass category creation (parents first, then subcategories)
- menu-builder.html: Parent category dropdown in properties, visual nesting in canvas,
  add subcategory button, renderItemCard() extracted for reuse

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 21:29:40 -08:00
John Mizerek
e02e124610 Increase max_tokens to 16384 for menu URL analysis
Large menus (20+ categories) were getting truncated JSON responses
at 8192 tokens, causing parse failures.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 16:17:13 -08:00
John Mizerek
698d31f62c Fix cash debit: attribute to business owner for admin/manager roles
Staff cash_debit goes to the worker (they keep the cash).
Admin/Manager cash_debit goes to the business owner (restaurant has it).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 12:10:59 -08:00
John Mizerek
d1910a7d34 Add staff role system: Staff keeps cash, Manager/Admin collect for restaurant
- Create tt_StaffRoles lookup table (Staff, Manager, Admin)
- Add RoleID column to Employees table (default: Staff)
- Wire portal role dropdown to addTeamMember API
- Return RoleName in team list and RoleID to Android
- Skip worker payout ledger and cash_debit for Manager/Admin roles
  on cash task completion (they collect on behalf of the restaurant)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 11:19:22 -08:00
John Mizerek
476d7f9df1 Add HasChildren flag and filter child businesses from list
Return HasChildren boolean so Android can route food court parents
directly to business selector. Exclude child businesses from top-level
restaurant list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:10:51 -08:00
John Mizerek
ec3a15013b Add per-tab ApprovalMode override for member order approval
- open.cfm: accept optional ApprovalMode param (0=auto, 1=manual, NULL=business default)
- addOrder.cfm: check tab ApprovalMode first, fall back to business TabApprovalRequired
- getActive.cfm: return resolved ApprovalRequired in tab response

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:44:06 -08:00
John Mizerek
623b94cb3d Fix expireTabs cron: verify Stripe response before updating DB
The cron was marking tabs as 'captured' without checking if the Stripe
capture actually succeeded. Also, it never loaded the Stripe config
(api/config/stripe.cfm), so the API key was empty and all captures
were silently failing. Now includes the Stripe config, checks every
response status, and reverts the tab to open on capture failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 10:45:01 -08:00
John Mizerek
48fa6e4482 Fix missing UserID in submit.cfm order query
Tab-aware submit references qOrder.UserID but the SELECT didn't include it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 22:53:17 -08:00
John Mizerek
9ed200d7ea Fix open tab: accept AuthAmountCents from Android client
Android sends cents, server was reading dollars only, falling back to default $150.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 21:43:52 -08:00
John Mizerek
4c0479db5c Add Open Tabs feature: tab APIs, presence tracking, shared tabs, cron, portal settings
- New api/tabs/ directory with 13 endpoints: open, close, cancel, get, getActive,
  addOrder, increaseAuth, addMember, removeMember, getPresence, approveOrder,
  rejectOrder, pendingOrders
- New api/presence/heartbeat.cfm for beacon-based user presence tracking
- New cron/expireTabs.cfm for idle tab expiry and presence cleanup
- Modified submit.cfm for tab-aware order submission (skip payment, update running total)
- Modified getOrCreateCart.cfm to auto-detect active tab and set TabID on new carts
- Modified webhook.cfm to handle tab capture events (metadata type=tab_close)
- Modified businesses/get.cfm and updateTabs.cfm with new tab config columns
- Updated portal tab settings UI with auth amounts, max members, approval toggle
- Added tab and presence endpoints to Application.cfm public allowlist

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 20:56:07 -08:00
John Mizerek
0e603b6cc9 Fix refresh button: add inline onclick and center icon
The addEventListener wasn't firing (likely cached JS). Added inline
onclick as reliable fallback and flexbox centering for the SVG icon.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-24 12:51:02 -08:00
John Mizerek
194eb4d205 Fix beacon Minor 0 not displaying in service points page
b.Minor is 0 for the first beacon which is falsy in JS.
Changed to null check so Minor: 0 displays correctly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-24 12:47:20 -08:00
John Mizerek
57ef40f737 Add click handler for refresh button in portal
The refresh button in the top bar had no click handler attached.
Now it reloads the current page data when clicked.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-23 14:13:47 -08:00
John Mizerek
802d48f049 Fix tt_TaskTypes column names for dev database
Changed tt_TaskTypeID to ID in task type CRUD operations to match
the migrated dev database schema.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-23 14:13:19 -08:00
John Mizerek
d628f7eb50 Fix column name in listAllTypes: tt_TaskTypeID -> ID
The tt_TaskTypes table uses 'ID' not 'tt_TaskTypeID'.
This was causing "Failed to load services" in the portal.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-23 13:24:46 -08:00
John Mizerek
e8705287cc Fix JS syntax error: remove escaped backticks in addModifierOption
The escaped backticks (\`) were causing "invalid escape sequence" error,
breaking the entire script and making the Upload Files button non-functional.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-20 16:34:31 -08:00
John Mizerek
2c655a5963 Add business timezone support for menu scheduling
- Add getTimeInZone() and getDayInZone() helpers in Application.cfm
- Update items.cfm and getForBuilder.cfm to use business timezone
- Menu availability now correctly respects each business's timezone

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-20 12:09:36 -08:00
John Mizerek
9e5770f131 Add toISO8601() helper for UTC date formatting 2026-02-20 11:56:31 -08:00
John Mizerek
ea34f302ac Require TaxRate and PayfritFee to be configured - no fallbacks
Both values MUST be set per business. If not configured, the menu
API will return an error instead of silently using defaults.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-19 11:31:31 -08:00
John Mizerek
ed001fd0b0 Remove hardcoded 5% fee fallback, require Businesses.PayfritFee
If PayfritFee is not configured for a business, the payment intent
creation now errors instead of silently using 5%. This ensures fees
are always explicitly set per business.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-19 11:16:41 -08:00
John Mizerek
c17f624787 Fix Stripe webhook and add dev webhook secret
- Add SubmittedOn = COALESCE(SubmittedOn, NOW()) to webhook for KDS timer
- Add test mode webhook secret for dev.payfrit.com
- Keep live mode webhook secret for biz.payfrit.com

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-19 10:08:35 -08:00
John Mizerek
6a8ded9857 Add getPaymentConfig.cfm to public allowlist
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-18 20:25:30 -08:00
John Mizerek
bbfbbf1963 Update PaymentIntent amount if cart changed on retry
Moved fee calculation before PI check so we can compare amounts.
If existing PaymentIntent has different amount than current cart,
update it via Stripe API before returning.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-17 19:06:01 -08:00
John Mizerek
d0f0f86176 Reuse existing PaymentIntent instead of blocking on retry
When user abandons checkout and retries, retrieve the existing
PaymentIntent from Stripe. If still usable (requires_payment_method,
requires_confirmation, requires_action), return its client_secret.
If already succeeded, block with clear error. If terminal/canceled,
clear and create new one.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-17 19:01:17 -08:00
John Mizerek
29327c4a13 Add getPaymentConfig endpoint for Payment Sheet saved cards
Returns customer ID, ephemeral key, and publishable key needed for
Stripe Payment Sheet to display saved payment methods on iOS/Android.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-17 18:00:56 -08:00
John Mizerek
07c2f24d67 Add Stripe Customer creation to save payment methods
- Get user info (StripeCustomerId, email, name) when creating PaymentIntent
- Create Stripe Customer if user doesn't have one
- Add customer and setup_future_usage=off_session to PaymentIntent
- Cards are automatically saved after successful payment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-17 17:56:13 -08:00