(function() {
'use strict';
var BASE_URL = 'https://orderingwebsite.io';
var RESTAURANT_ID = '6936f5f2b75a14aac7c87e14';
var DISPLAY_MODE = 'button';
var EMBEDDED_RESTAURANT = {"name":"Bella Isabella","description":"","logo_url":"","cover_image_url":"","cuisine_type":"italian","address":"1 zone 1, 246 Barangay Palina E Rd, Urdaneta City, Pangasinan, Philippines","location":{"lat":15.9509355,"lng":120.5551862},"phone":" 09917049488","email":"jeffblyth1959@gmail.com","currency":"PHP","timezone":"UTC","delivery_hours":{"monday":{"closed":false,"shifts":[{"open":"21:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"tuesday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"wednesday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"thursday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"friday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"saturday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"sunday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]}},"pickup_hours":{"monday":{"closed":false,"shifts":[{"open":"21:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"tuesday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"wednesday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"thursday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"friday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"saturday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"sunday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]}},"dine_in_hours":{"monday":{"closed":false,"shifts":[{"open":"21:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"tuesday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"wednesday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"thursday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"friday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"saturday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]},"sunday":{"closed":false,"shifts":[{"open":"09:30","close":"12:30"},{"open":"03:30","close":"21:30"}]}},"delivery_enabled":true,"delivery_disabled_until":null,"pickup_enabled":true,"pickup_disabled_until":null,"dine_in_enabled":false,"dine_in_disabled_until":"2026-01-01T23:59:59.999Z","dine_in_ordering_mode":"online_menu_only","reservations_enabled":false,"delivery_minimum_order":0,"pickup_minimum_order":0,"dine_in_minimum_order":0,"max_delivery_distance":10,"delivery_fee":5,"delivery_fee_method":"distance_based","delivery_rate_per_mile":1,"delivery_base_fee":0,"delivery_tiers":[{"min_distance":0,"max_distance":1.8,"fee":60},{"min_distance":1.8,"max_distance":3.7,"fee":100}],"use_tiered_delivery":true,"delivery_zones":[],"estimated_delivery_time":30,"estimated_pickup_time":15,"estimated_dine_in_time":20,"other_fees":{"enabled":false,"items":[]},"tips_settings":{"enabled":true,"delivery_enabled":true,"pickup_enabled":true,"dine_in_enabled":true,"percentage_enabled":true,"flat_amount_enabled":true,"default_percentages":[10,15,20,25],"default_flat_amounts":[2,5,10]},"vat_settings":{"enabled":false,"rate":0,"price_inclusive":false,"applies_to_products":true,"applies_to_fees":false,"applies_to_delivery":false,"applies_to_tips":false},"customer_print_template":{"include_logo":false,"include_qr_code":true,"include_restaurant_info":true,"include_customer_info":true,"include_special_notes":true,"include_header":false,"include_footer":true,"include_order_id":true,"include_order_type":true,"include_requested_time":true,"include_accepted_time":true,"include_expected_time":true,"include_payment_status":true,"include_payment_method":true,"include_distance":true,"include_travel_time":true,"include_items":true,"include_totals":true,"include_placed_at":true,"include_not_paid_stamp":true,"include_scheduled_stamp":true,"not_paid_stamp_size":"medium","scheduled_stamp_size":"medium","paper_size":"80mm","header_font_size":"large","order_info_font_size":"medium","customer_info_font_size":"medium","special_notes_font_size":"medium","items_font_size":"large","item_options_font_size":"large","item_notes_font_size":"large","totals_font_size":"medium","footer_font_size":"small","order_id_font_size":"medium","order_type_font_size":"medium","requested_time_font_size":"medium","accepted_time_font_size":"medium","expected_time_font_size":"medium","payment_status_font_size":"medium","payment_method_font_size":"medium","distance_font_size":"medium","travel_time_font_size":"medium","restaurant_info_font_size":"medium","placed_at_font_size":"medium","header_text":"","footer_text":"Thank you for your order!","section_order":["logo","header","restaurant_info","order_info","times","payment","distance","customer_info","special_notes","items","totals","qr_code","footer"]},"kitchen_print_template":{"include_logo":false,"include_qr_code":true,"include_restaurant_info":true,"include_customer_info":true,"include_special_notes":true,"include_special_instructions":true,"include_header":false,"include_footer":true,"include_order_id":true,"include_order_type":true,"include_requested_time":true,"include_accepted_time":true,"include_expected_time":true,"include_payment_status":true,"include_payment_method":true,"include_distance":true,"include_travel_time":true,"include_items":true,"include_totals":true,"include_placed_at":true,"include_not_paid_stamp":true,"include_scheduled_stamp":true,"not_paid_stamp_size":"medium","scheduled_stamp_size":"medium","paper_size":"80mm","header_font_size":"large","order_info_font_size":"medium","customer_info_font_size":"medium","special_notes_font_size":"large","items_font_size":"xlarge","item_options_font_size":"large","item_notes_font_size":"xlarge","totals_font_size":"medium","footer_font_size":"small","order_id_font_size":"medium","order_type_font_size":"medium","requested_time_font_size":"medium","accepted_time_font_size":"medium","expected_time_font_size":"medium","payment_status_font_size":"medium","payment_method_font_size":"medium","distance_font_size":"medium","travel_time_font_size":"medium","restaurant_info_font_size":"medium","placed_at_font_size":"medium","header_text":"","footer_text":"","section_order":["logo","header","restaurant_info","order_info","times","payment","distance","customer_info","special_notes","items","totals","qr_code","footer"]},"dietary_options":[],"specialties":[],"amenities":[],"average_rating":0,"total_reviews":0,"price_range":"$$","payment_gateways":{"stripe":{"enabled":false,"publishable_key":"","secret_key":"","webhook_secret":"","mode":"test","reservations":true,"delivery":true,"pickup":true,"dine_in":true},"paypal":{"enabled":false,"client_id":"","client_secret":"","mode":"sandbox","reservations":true,"delivery":true,"pickup":true,"dine_in":true},"square":null,"gocardless":{"enabled":false,"access_token":"","environment":"sandbox","reservations":false,"delivery":true,"pickup":true,"dine_in":true},"worldpay":{"enabled":false,"access_token":"","environment":"sandbox","reservations":false,"delivery":true,"pickup":true,"dine_in":true},"revolut":{"enabled":false,"access_token":"","environment":"sandbox","reservations":false,"delivery":true,"pickup":true,"dine_in":true},"verifone":{"enabled":false,"access_token":"","environment":"sandbox","reservations":false,"delivery":true,"pickup":true,"dine_in":true},"wonderful":{"enabled":false,"access_token":"","environment":"sandbox","reservations":false,"delivery":true,"pickup":true,"dine_in":true},"razorpay":{"enabled":false,"key_id":"","key_secret":"","reservations":true,"delivery":true,"pickup":true,"dine_in":true},"alipay":{"enabled":false,"access_token":"","environment":"sandbox","reservations":false,"delivery":true,"pickup":true,"dine_in":true},"airwallex":{"enabled":false,"access_token":"","environment":"sandbox","reservations":false,"delivery":true,"pickup":true,"dine_in":true},"pay_cash_on_pickup":{"enabled":true,"pickup":true,"dine_in":true,"reservations":false},"pay_card_on_pickup":{"enabled":true,"pickup":true,"dine_in":true,"reservations":false},"pay_cash_on_delivery":{"enabled":true},"pay_card_on_delivery":{"enabled":true}},"max_party_size":10,"advance_booking_days":30,"reservation_deposit":5,"require_online_reservation_deposit":true,"booking_fee_enabled":true,"booking_fee_amount":5,"table_time_limit":90,"auto_confirm_reservations":false,"reservation_notes":"","reservation_slot_interval":30,"reservation_hours":{"monday":{"closed":false,"shifts":[{"open":"11:00","close":"22:00"}]},"tuesday":{"closed":false,"shifts":[{"open":"11:00","close":"22:00"}]},"wednesday":{"closed":false,"shifts":[{"open":"11:00","close":"22:00"}]},"thursday":{"closed":false,"shifts":[{"open":"11:00","close":"22:00"}]},"friday":{"closed":false,"shifts":[{"open":"11:00","close":"22:00"}]},"saturday":{"closed":false,"shifts":[{"open":"11:00","close":"22:00"}]},"sunday":{"closed":false,"shifts":[{"open":"11:00","close":"22:00"}]}},"floor_plan_design":null,"status":"active","white_label_partner_id":null,"managed_by_partner":false,"theme_header_background_color":"#FFFFFF","theme_header_text_color":"#d52a3b","theme_background_color":"#F8FAFC","theme_text_color":"#1E293B","theme_button_color":"#d52a3b","theme_button_text_color":"#FFFFFF","theme_category_section_background_color":"#FFFFFF","theme_category_background_color":"#ffffff","theme_category_text_color":"#1E293B","theme_item_card_background_color":"#FFFFFF","theme_cart_background_color":"#FFFFFF","reservation_theme_header_background_color":"#FFFFFF","reservation_theme_header_text_color":"#1E293B","reservation_theme_background_color":"#F8FAFC","reservation_theme_text_color":"#1E293B","reservation_theme_button_color":"#FF6B35","reservation_theme_button_text_color":"#FFFFFF","embedder_button_text":"Order Online","embedder_button_color":"#e04242","embedder_button_text_color":"#0f172a","embedder_button_border_radius":20,"reservation_button_text":"Book a Table","reservation_button_color":"#1e3a8a","reservation_button_text_color":"#0f172a","reservation_button_border_radius":20,"id":"6936f5f2b75a14aac7c87e14","created_date":"2025-12-08T15:59:46.499000","updated_date":"2026-01-20T00:32:15.777000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false};
var EMBEDDED_MENU_ITEMS = [{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Soft Drinks 237ml","description":"Small soft drink","image_url":null,"category":"drinks","original_category":null,"price":15,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":20,"options":[],"id":"693ed81723d3d0e0a65ff436","created_date":"2025-12-14T15:30:31.545000","updated_date":"2025-12-16T15:40:08.650000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Drinks Litre","description":"1 liter soft drink","image_url":null,"category":"drinks","original_category":null,"price":45,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":27,"options":[],"id":"693ed81723d3d0e0a65ff437","created_date":"2025-12-14T15:30:31.545000","updated_date":"2025-12-16T15:40:07.671000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Milkshake Cappuccino","description":"Coffee flavored milkshake","image_url":null,"category":"drinks","original_category":null,"price":140,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":13,"options":[],"id":"693ed81723d3d0e0a65ff435","created_date":"2025-12-14T15:30:31.545000","updated_date":"2025-12-16T15:40:07.766000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Milkshake Strawberry","description":"Creamy strawberry milkshake","image_url":null,"category":"drinks","original_category":null,"price":140,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":2,"options":[],"id":"693ed81723d3d0e0a65ff433","created_date":"2025-12-14T15:30:31.545000","updated_date":"2025-12-16T15:40:14.677000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Milkshake Chocolate","description":"Rich chocolate milkshake","image_url":null,"category":"drinks","original_category":null,"price":140,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":6,"options":[],"id":"693ed81723d3d0e0a65ff434","created_date":"2025-12-14T15:30:31.545000","updated_date":"2025-12-16T15:40:08.694000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Red Horse","description":"Red Horse beer","image_url":null,"category":"drinks","original_category":null,"price":65,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":40,"options":[],"id":"693ed81723d3d0e0a65ff43a","created_date":"2025-12-14T15:30:31.545000","updated_date":"2025-12-16T15:40:07.376000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Coffee","description":"Freshly brewed coffee","image_url":null,"category":"drinks","original_category":null,"price":60,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":32,"options":[],"id":"693ed81723d3d0e0a65ff438","created_date":"2025-12-14T15:30:31.545000","updated_date":"2025-12-16T15:40:07.784000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Cappuccino","description":"Italian style cappuccino","image_url":null,"category":"drinks","original_category":null,"price":100,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":37,"options":[],"id":"693ed81723d3d0e0a65ff439","created_date":"2025-12-14T15:30:31.545000","updated_date":"2025-12-16T15:40:07.775000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Garlic Bread Mexicana","description":"Served with pizza sauce, onions spicy","image_url":null,"category":"deleted_items","original_category":"starters","price":350,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":21,"options":[],"id":"693ed7dd54067ac0b3ede550","created_date":"2025-12-14T15:29:33.492000","updated_date":"2025-12-17T17:48:03.699000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Meatballs","description":"Oven baked with mozzarella","image_url":null,"category":"starters","original_category":null,"price":250,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":33,"options":[],"id":"693ed7dd54067ac0b3ede552","created_date":"2025-12-14T15:29:33.492000","updated_date":"2025-12-16T15:40:07.741000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Cheesy Fries","description":"Oven baked with mozzarella","image_url":null,"category":"starters","original_category":null,"price":140,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":7,"options":[],"id":"693ed7dd54067ac0b3ede54e","created_date":"2025-12-14T15:29:33.492000","updated_date":"2025-12-16T15:40:14.663000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"French Fries","description":"Crispy golden fries","image_url":null,"category":"starters","original_category":null,"price":100,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":0,"options":[],"id":"693ed7dd54067ac0b3ede54d","created_date":"2025-12-14T15:29:33.492000","updated_date":"2025-12-16T15:40:14.615000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Garlic Bread Cheese 14\"","description":"14 inch garlic bread with cheese","image_url":null,"category":"starters","original_category":null,"price":300,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":14,"options":[],"id":"693ed7dd54067ac0b3ede54f","created_date":"2025-12-14T15:29:33.492000","updated_date":"2025-12-16T15:40:07.772000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Chicken Parmigiana","description":"Breaded chicken filet served in tomato sauce, mozzarella and parmesan cheese oven baked with pasta or French fries as a side dish","image_url":null,"category":"deleted_items","original_category":"starters","price":440,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":28,"options":[],"id":"693ed7dd54067ac0b3ede551","created_date":"2025-12-14T15:29:33.492000","updated_date":"2025-12-17T17:48:21.924000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Hamburger","description":"Served with lettuce, tomatoes, French fries and a small soft drink","image_url":null,"category":"burgers","original_category":null,"price":170,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":1,"options":[],"id":"693ed7ba00cd9b6f7bcf360a","created_date":"2025-12-14T15:28:58.473000","updated_date":"2025-12-16T15:40:14.586000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Cheese Burger","description":"Served with lettuce, tomatoes, French fries and a small soft drink","image_url":null,"category":"deleted_items","original_category":"burgers","price":190,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":8,"options":[],"id":"693ed7ba00cd9b6f7bcf360b","created_date":"2025-12-14T15:28:58.473000","updated_date":"2025-12-17T17:45:29.269000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Double Cheese Burger","description":"Served with lettuce, tomatoes, French fries and a small soft drink","image_url":null,"category":"burgers","original_category":null,"price":250,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":15,"options":[],"id":"693ed7ba00cd9b6f7bcf360c","created_date":"2025-12-14T15:28:58.473000","updated_date":"2025-12-16T15:40:07.757000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Chicken Burger","description":"Home made chicken burger served with lettuce, tomatoes, French fries and a small soft drink","image_url":null,"category":"deleted_items","original_category":"burgers","price":270,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":22,"options":[],"id":"693ed7ba00cd9b6f7bcf360d","created_date":"2025-12-14T15:28:58.473000","updated_date":"2025-12-17T17:45:42.003000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Cannelloni","description":"Filled pasta tubes in a meaty sauce","image_url":null,"category":"pasta","original_category":null,"price":300,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":41,"options":[],"id":"693ed78df9645cee78c1837b","created_date":"2025-12-14T15:28:13.672000","updated_date":"2025-12-16T15:40:07.746000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"French Fries","description":"Crispy golden fries","image_url":null,"category":"deleted_items","original_category":"starters","price":100,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":6,"options":[],"id":"693ed7424217b05c7775af0a","created_date":"2025-12-14T15:26:58.069000","updated_date":"2025-12-17T17:47:52.110000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Cheesy Fries","description":"Oven baked with mozzarella","image_url":null,"category":"deleted_items","original_category":"starters","price":140,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":9,"options":[],"id":"693ed7424217b05c7775af0b","created_date":"2025-12-14T15:26:58.069000","updated_date":"2025-12-17T17:47:58.570000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Garlic Bread Cheese 14\"","description":"14 inch garlic bread with cheese","image_url":null,"category":"deleted_items","original_category":"starters","price":300,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":16,"options":[],"id":"693ed7424217b05c7775af0c","created_date":"2025-12-14T15:26:58.069000","updated_date":"2025-12-17T17:48:14.785000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Chicken Parmigiana","description":"Breaded chicken filet served in tomato sauce, mozzarella and parmesan cheese oven baked with pasta or French fries as a side dish","image_url":null,"category":"starters","original_category":null,"price":440,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":true,"sort_order":29,"options":[{"name":"Side Dish","type":"single_select","required":true,"min_selections":null,"max_selections":null,"choices":[{"name":"Pasta","price_modifier":0},{"name":"French Fries","price_modifier":0}]}],"id":"693ed7424217b05c7775af0e","created_date":"2025-12-14T15:26:58.069000","updated_date":"2025-12-16T15:40:07.708000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Garlic Bread Mexicana","description":"Served with pizza sauce, onions - spicy","image_url":null,"category":"starters","original_category":null,"price":350,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":23,"options":[],"id":"693ed7424217b05c7775af0d","created_date":"2025-12-14T15:26:58.069000","updated_date":"2025-12-16T15:40:07.842000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Meatballs","description":"Oven baked with mozzarella","image_url":null,"category":"deleted_items","original_category":"starters","price":250,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":34,"options":[],"id":"693ed7424217b05c7775af0f","created_date":"2025-12-14T15:26:58.069000","updated_date":"2025-12-17T17:48:35.904000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Double Cheese Burger","description":"Served with lettuce, tomatoes, French fries and a small soft drink","image_url":null,"category":"deleted_items","original_category":"burgers","price":250,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":17,"options":[],"id":"693ed7424217b05c7775af08","created_date":"2025-12-14T15:26:58.043000","updated_date":"2025-12-17T17:45:34.394000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Cheese Burger","description":"Served with lettuce, tomatoes, French fries and a small soft drink","image_url":null,"category":"burgers","original_category":null,"price":190,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":10,"options":[],"id":"693ed7424217b05c7775af07","created_date":"2025-12-14T15:26:58.043000","updated_date":"2025-12-16T15:40:07.751000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Chicken Burger","description":"Home made - Served with lettuce, tomatoes, French fries and a small soft drink","image_url":null,"category":"burgers","original_category":null,"price":270,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":24,"options":[],"id":"693ed7424217b05c7775af09","created_date":"2025-12-14T15:26:58.043000","updated_date":"2025-12-16T15:40:07.746000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Spaghetti Bolognese with Meat Balls","description":"Bolognese with meatballs","image_url":null,"category":"pasta","original_category":null,"price":350,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":18,"options":[],"id":"693ed7424217b05c7775af00","created_date":"2025-12-14T15:26:58.025000","updated_date":"2025-12-16T15:40:07.829000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Spaghetti Pollo","description":"Served with chicken and creamy sauce","image_url":null,"category":"pasta","original_category":null,"price":350,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":35,"options":[],"id":"693ed7424217b05c7775af03","created_date":"2025-12-14T15:26:58.025000","updated_date":"2025-12-16T15:40:07.822000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Spaghetti Bolognese","description":"Classic meat sauce","image_url":null,"category":"pasta","original_category":null,"price":250,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":11,"options":[],"id":"693ed7424217b05c7775aeff","created_date":"2025-12-14T15:26:58.025000","updated_date":"2025-12-16T15:40:08.771000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Spaghetti Carbonara","description":"Creamy carbonara sauce","image_url":null,"category":"pasta","original_category":null,"price":270,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":25,"options":[],"id":"693ed7424217b05c7775af01","created_date":"2025-12-14T15:26:58.025000","updated_date":"2025-12-16T15:40:07.881000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Lasagne","description":"Layers of pasta in a meaty sauce","image_url":null,"category":"pasta","original_category":null,"price":300,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":38,"options":[],"id":"693ed7424217b05c7775af04","created_date":"2025-12-14T15:26:58.025000","updated_date":"2025-12-16T15:40:07.429000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Spaghetti Napoli","description":"With tomato sauce","image_url":null,"category":"pasta","original_category":null,"price":230,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":3,"options":[],"id":"693ed7424217b05c7775aefe","created_date":"2025-12-14T15:26:58.025000","updated_date":"2025-12-16T15:40:14.697000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Spaghetti Amatriciana","description":"With bacon, onions and tomato sauce","image_url":null,"category":"pasta","original_category":null,"price":270,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":30,"options":[],"id":"693ed7424217b05c7775af02","created_date":"2025-12-14T15:26:58.025000","updated_date":"2025-12-16T15:40:07.840000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Pizza Isabella Special","description":"Double decker pizza in 14\" - Serves 2 to 3 people","image_url":null,"category":"pizza","original_category":null,"price":850,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":true,"sort_order":45,"options":[],"id":"693ed6afcc2395b7d2f42ad5","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-16T15:40:07.799000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Pizza Tonno","description":"Pizza with tuna and onions (Only available in 14\")","image_url":null,"category":"pizza","original_category":null,"price":450,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":42,"options":[],"id":"693ed6afcc2395b7d2f42ad2","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-16T15:40:07.796000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Margherita M","description":"Classic pizza with mozzarella and pizza sauce","image_url":null,"category":"pizza","original_category":null,"price":350,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":4,"options":[{"name":"Size","type":"single_select","required":true,"min_selections":null,"max_selections":null,"choices":[{"name":"12\"","price_modifier":0},{"name":"14\"","price_modifier":30}]}],"id":"693ed6afcc2395b7d2f42acb","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-31T13:26:25.476000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Pepperoni","description":"Classic pepperoni pizza","image_url":null,"category":"pizza","original_category":null,"price":380,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":12,"options":[{"name":"Size","type":"single_select","required":true,"min_selections":null,"max_selections":null,"choices":[{"name":"12\"","price_modifier":0},{"name":"14\"","price_modifier":30}]}],"id":"693ed6afcc2395b7d2f42acc","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-16T15:40:07.800000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Meat Feast","description":"Loaded with assorted meats (Only available in 14\")","image_url":null,"category":"pizza","original_category":null,"price":450,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":39,"options":[],"id":"693ed6afcc2395b7d2f42ad1","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-16T15:40:07.773000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Special Pizza","description":"Ham, Mushrooms, Pepperoni, Onions and Peppers (Only available in 14\")","image_url":null,"category":"pizza","original_category":null,"price":450,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":43,"options":[],"id":"693ed6afcc2395b7d2f42ad3","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-16T15:40:07.860000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Hawaiian","description":"Hawaiian pizza with ham and pineapple","image_url":null,"category":"pizza","original_category":null,"price":400,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":36,"options":[{"name":"Size","type":"single_select","required":true,"min_selections":null,"max_selections":null,"choices":[{"name":"12\"","price_modifier":0},{"name":"14\"","price_modifier":20}]}],"id":"693ed6afcc2395b7d2f42ad0","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-16T15:40:07.719000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Pepperoni & Mushrooms","description":"Pepperoni and mushrooms pizza","image_url":null,"category":"pizza","original_category":null,"price":400,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":26,"options":[{"name":"Size","type":"single_select","required":true,"min_selections":null,"max_selections":null,"choices":[{"name":"12\"","price_modifier":0},{"name":"14\"","price_modifier":20}]}],"id":"693ed6afcc2395b7d2f42ace","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-16T15:40:07.690000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Ham & Mushrooms","description":"Ham and mushrooms pizza","image_url":null,"category":"pizza","original_category":null,"price":400,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":31,"options":[{"name":"Size","type":"single_select","required":true,"min_selections":null,"max_selections":1,"choices":[{"name":"12\"","price_modifier":10},{"name":"14\"","price_modifier":20}]}],"id":"693ed6afcc2395b7d2f42acf","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-31T12:46:10.009000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Ham","description":"Pizza topped with ham","image_url":null,"category":"pizza","original_category":null,"price":380,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":19,"options":[{"name":"Size","type":"single_select","required":true,"min_selections":null,"max_selections":null,"choices":[{"name":"12\"","price_modifier":0},{"name":"14\"","price_modifier":30}]}],"id":"693ed6afcc2395b7d2f42acd","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-16T15:40:07.741000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false},{"restaurant_id":"6936f5f2b75a14aac7c87e14","name":"Panzarotti","description":"Folded fried pizza (Only available in 14\")","image_url":null,"category":"pizza","original_category":null,"price":480,"preparation_time":null,"available":true,"not_available_until":null,"dietary_tags":[],"popular":false,"sort_order":44,"options":[],"id":"693ed6afcc2395b7d2f42ad4","created_date":"2025-12-14T15:24:31.033000","updated_date":"2025-12-16T15:40:07.806000","created_by_id":"69112422010ec07981f799b8","created_by":"orderingwebsite@gmail.com","is_sample":false}];
var FETCH_ERROR = null;
var GOOGLE_MAPS_API_KEY = "AIzaSyAwMyNA5izay3wtbTrYpDPKSHhwUUHQPTk";
var STRIPE_PUBLISHABLE_KEY = null;
console.log('[Embedder] Initializing - Mode:', DISPLAY_MODE, 'Restaurant:', RESTAURANT_ID);
console.log('[Embedder] Restaurant data:', EMBEDDED_RESTAURANT ? 'loaded' : 'null');
console.log('[Embedder] Menu items:', EMBEDDED_MENU_ITEMS ? EMBEDDED_MENU_ITEMS.length : 0);
console.log('[Embedder] Fetch error:', FETCH_ERROR);
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? parseInt(result[1], 16) + ', ' + parseInt(result[2], 16) + ', ' + parseInt(result[3], 16) : '249, 115, 22';
}
var styles = `
.base44-menu-button {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
padding: 12px 24px !important;
background: linear-gradient(135deg, #f97316 0%, #fb923c 100%) !important;
color: white !important;
font-weight: 600 !important;
font-size: 16px !important;
border: none !important;
border-radius: 8px !important;
cursor: pointer !important;
text-decoration: none !important;
transition: all 0.3s ease !important;
box-shadow: 0 4px 6px rgba(249, 115, 22, 0.2) !important;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
line-height: 1.5 !important;
text-transform: none !important;
letter-spacing: normal !important;
}
.base44-menu-button:hover {
transform: translateY(-2px) !important;
box-shadow: 0 6px 12px rgba(249, 115, 22, 0.3) !important;
background: linear-gradient(135deg, #ea580c 0%, #f97316 100%) !important;
color: white !important;
}
.base44-menu-button:active {
transform: translateY(0) !important;
}
.base44-menu-button svg {
margin-right: 8px !important;
width: 20px !important;
height: 20px !important;
}
.base44-modal-overlay {
position: fixed !important;
top: 0 !important;
left: 0 !important;
right: 0 !important;
bottom: 0 !important;
background: rgba(0, 0, 0, 0.75) !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
z-index: 999999 !important;
padding: 0 !important;
animation: base44FadeIn 0.3s ease !important;
overflow: hidden !important;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
}
.address-autocomplete-suggestions {
position: absolute !important;
top: 100% !important;
left: 0 !important;
right: 0 !important;
background: #fff !important;
border: 2px solid #e2e8f0 !important;
border-top: none !important;
border-radius: 0 0 10px 10px !important;
max-height: 250px !important;
overflow-y: auto !important;
z-index: 9999999 !important;
box-shadow: 0 8px 20px rgba(0,0,0,0.25) !important;
margin-top: 2px !important;
}
.address-suggestion-item {
padding: 12px 16px !important;
cursor: pointer !important;
border-bottom: 1px solid #f1f5f9 !important;
transition: background 0.15s !important;
}
.address-suggestion-item:last-child {
border-bottom: none !important;
}
.address-suggestion-item:hover {
background: #f8fafc !important;
}
.address-suggestion-main {
font-weight: 600 !important;
color: #1e293b !important;
font-size: 14px !important;
margin-bottom: 2px !important;
}
.address-suggestion-secondary {
color: #64748b !important;
font-size: 12px !important;
}
.base44-modal-overlay * {
box-sizing: border-box !important;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
}
.base44-modal-overlay h1, .base44-modal-overlay h2, .base44-modal-overlay h3, .base44-modal-overlay h4, .base44-modal-overlay h5, .base44-modal-overlay h6 {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
line-height: 1.3 !important;
letter-spacing: normal !important;
text-transform: none !important;
}
.base44-modal-overlay p, .base44-modal-overlay span, .base44-modal-overlay div, .base44-modal-overlay label {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
line-height: 1.5 !important;
letter-spacing: normal !important;
}
.base44-modal-overlay button {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
cursor: pointer !important;
}
.base44-modal-overlay input, .base44-modal-overlay textarea, .base44-modal-overlay select {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
font-size: 16px !important;
color: #1e293b !important;
background-color: #fff !important;
}
.base44-modal-overlay input::placeholder, .base44-modal-overlay textarea::placeholder {
color: #64748b !important;
opacity: 1 !important;
}
@keyframes base44FadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.base44-modal-container {
position: relative !important;
width: 100% !important;
max-width: 100% !important;
height: 100vh !important;
background: white !important;
border-radius: 0 !important;
overflow: hidden !important;
box-shadow: none !important;
animation: base44SlideUp 0.3s ease !important;
display: flex !important;
flex-direction: column !important;
}
@keyframes base44SlideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes base44Pulse {
0%, 100% {
box-shadow: 0 0 0 0 rgba(213, 42, 59, 0.9);
transform: scale(1);
}
50% {
box-shadow: 0 0 0 16px rgba(213, 42, 59, 0);
transform: scale(1.005);
}
}
@keyframes base44NewItemPulse {
0%, 100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(213, 42, 59, 0.7);
}
50% {
transform: scale(1.02);
box-shadow: 0 0 20px rgba(213, 42, 59, 0.4);
}
}
.base44-newest-item {
animation: base44NewItemPulse 0.5s ease-in-out 8 !important;
}
.base44-pulse-field {
animation: base44Pulse 2s ease-in-out infinite !important;
}
.base44-modal-header {
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
padding: 16px 24px !important;
background: #FFFFFF !important;
color: #d52a3b !important;
flex-shrink: 0 !important;
}
.base44-modal-title {
font-size: 20px !important;
font-weight: 700 !important;
margin: 0 !important;
color: #d52a3b !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
flex: 1 !important;
min-width: 0 !important;
}
.base44-modal-close {
background: rgba(255, 255, 255, 0.2) !important;
border: none !important;
color: #d52a3b !important;
width: 36px !important;
height: 36px !important;
border-radius: 8px !important;
cursor: pointer !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
transition: background 0.2s !important;
font-size: 24px !important;
line-height: 1 !important;
font-weight: 300 !important;
}
.base44-modal-close:hover {
background: rgba(255, 255, 255, 0.3) !important;
color: #d52a3b !important;
}
.base44-modal-content {
flex: 1 !important;
overflow-y: auto !important;
overflow-x: hidden !important;
-webkit-overflow-scrolling: touch !important;
background: #f8fafc !important;
}
@media (max-width: 768px) {
.base44-modal-container {
max-width: 100% !important;
width: 100% !important;
height: 100vh !important;
border-radius: 0 !important;
}
.base44-modal-header {
padding-top: calc(env(safe-area-inset-top) + 16px) !important;
padding-top: max(calc(env(safe-area-inset-top) + 16px), 32px) !important;
padding-left: 12px !important;
padding-right: 12px !important;
gap: 8px !important;
}
.base44-modal-title {
font-size: 15px !important;
}
.base44-modal-track-btn {
font-size: 12px !important;
padding: 6px 10px !important;
}
.locate-btn .locate-text {
display: none !important;
}
.locate-btn {
padding: 12px !important;
}
}
`;
function injectStyles() {
if (document.getElementById('base44-embedder-styles')) return;
var styleEl = document.createElement('style');
styleEl.id = 'base44-embedder-styles';
styleEl.textContent = styles;
document.head.appendChild(styleEl);
}
// Store the current modal container for the inline menu
var currentModalContent = null;
var currentOverlay = null;
async function createModal(restaurantId, restaurantName) {
// Prevent body scroll
document.body.style.overflow = 'hidden';
var overlay = document.createElement('div');
overlay.className = 'base44-modal-overlay';
currentOverlay = overlay;
var container = document.createElement('div');
container.className = 'base44-modal-container';
var header = document.createElement('div');
header.className = 'base44-modal-header';
var title = document.createElement('h2');
title.className = 'base44-modal-title';
// Use placeholder name initially
title.textContent = restaurantName || EMBEDDED_RESTAURANT?.name || 'Order Online';
var headerActions = document.createElement('div');
headerActions.style.cssText = 'display: flex; align-items: center; gap: 12px;';
var trackOrdersBtn = document.createElement('button');
trackOrdersBtn.className = 'base44-modal-track-btn';
var headerTextColor = EMBEDDED_RESTAURANT?.theme_header_text_color || '#1E293B';
trackOrdersBtn.innerHTML = 'My Orders ';
trackOrdersBtn.style.cssText = 'background: rgba(255,255,255,0.2); border: none; color: ' + headerTextColor + '; padding: 8px 16px; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 14px; transition: background 0.2s; white-space: nowrap; flex-shrink: 0; display: flex; align-items: center;';
trackOrdersBtn.onmouseover = function() { this.style.background = 'rgba(255,255,255,0.3)'; };
trackOrdersBtn.onmouseout = function() { this.style.background = 'rgba(255,255,255,0.2)'; };
trackOrdersBtn.onclick = function() {
openAccount();
};
var closeBtn = document.createElement('button');
closeBtn.className = 'base44-modal-close';
closeBtn.innerHTML = '×';
closeBtn.onclick = function() { closeModal(); };
header.appendChild(title);
headerActions.appendChild(trackOrdersBtn);
headerActions.appendChild(closeBtn);
header.appendChild(headerActions);
var content = document.createElement('div');
content.className = 'base44-modal-content';
content.id = 'base44-modal-inline-menu';
content.innerHTML = '
⏳ Loading latest menu...
';
currentModalContent = content;
container.appendChild(header);
container.appendChild(content);
overlay.appendChild(container);
var escapeHandler = function(e) {
if (e.key === 'Escape') {
closeModal();
document.removeEventListener('keydown', escapeHandler);
}
};
document.addEventListener('keydown', escapeHandler);
overlay._escapeHandler = escapeHandler;
document.body.appendChild(overlay);
// Fetch fresh data
try {
var response = await fetch(BASE_URL + '/api/functions/getPublicMenuData?rid=' + restaurantId);
if (!response.ok) throw new Error('Network response was not ok');
var data = await response.json();
if (!data.success) throw new Error(data.error || 'Could not load data');
// Update global variables
restaurant = data.restaurant;
menuItems = (data.menuItems || []).filter(function(item) { return item.available !== false; });
EMBEDDED_RESTAURANT = data.restaurant; // Keep this consistent
EMBEDDED_MENU_ITEMS = menuItems;
// Update header with fresh data
title.textContent = restaurant.name || 'Order Online';
header.style.background = restaurant.theme_header_background_color || '#FFFFFF';
header.style.color = restaurant.theme_header_text_color || '#1E293B';
closeBtn.style.color = restaurant.theme_header_text_color || '#1E293B';
// Initialize menu with fresh data
initializeInlineMenu(content);
} catch (error) {
console.error('[Embedder] Failed to fetch fresh data on modal open:', error);
content.innerHTML = '⚠️ Error: Could not load menu. Please try again.
';
}
}
function closeModal() {
if (currentOverlay) {
if (currentOverlay._escapeHandler) {
document.removeEventListener('keydown', currentOverlay._escapeHandler);
}
if (document.body.contains(currentOverlay)) {
document.body.removeChild(currentOverlay);
}
currentOverlay = null;
currentModalContent = null;
document.body.style.overflow = '';
}
}
// =====================================================
// INLINE MENU LOGIC (from embedderInline)
// =====================================================
var cart = [];
var menuItems = EMBEDDED_MENU_ITEMS;
var restaurant = EMBEDDED_RESTAURANT;
var categories = [];
var selectedCategory = 'all';
var editingInstructions = {};
var searchQuery = '';
// Load cart from localStorage and merge duplicates
try {
var savedCart = localStorage.getItem('base44_embed_cart_' + RESTAURANT_ID);
if (savedCart) {
var loadedCart = JSON.parse(savedCart);
// Merge duplicate items
var mergedCart = [];
loadedCart.forEach(function(item) {
var itemOptions = item.selected_options;
var itemHasOptions = itemOptions && Object.keys(itemOptions).length > 0;
var optionsKey = itemHasOptions ? JSON.stringify(itemOptions) : '';
var existingIndex = -1;
for (var i = 0; i < mergedCart.length; i++) {
var mergedOptions = mergedCart[i].selected_options;
var mergedHasOptions = mergedOptions && Object.keys(mergedOptions).length > 0;
var mergedOptionsKey = mergedHasOptions ? JSON.stringify(mergedOptions) : '';
if (mergedCart[i].id === item.id && mergedOptionsKey === optionsKey) {
existingIndex = i;
break;
}
}
if (existingIndex !== -1) {
mergedCart[existingIndex].quantity += item.quantity;
} else {
mergedCart.push(item);
}
});
cart = mergedCart;
saveCart();
}
} catch (e) {
console.log('Failed to load cart from storage');
}
function saveCart() {
try {
if (cart.length > 0) {
localStorage.setItem('base44_embed_cart_' + RESTAURANT_ID, JSON.stringify(cart));
} else {
localStorage.removeItem('base44_embed_cart_' + RESTAURANT_ID);
}
} catch (e) {
console.log('Failed to save cart');
}
}
function getCurrencySymbol(code) {
var symbols = {
USD: '$', EUR: '€', GBP: '£', JPY: '¥', CNY: '¥', AUD: '$', CAD: '$', CHF: 'Fr',
INR: '₹', MXN: '$', BRL: 'R$', ZAR: 'R', RUB: '₽', KRW: '₩', SGD: '$', HKD: '$',
NOK: 'kr', SEK: 'kr', DKK: 'kr', PLN: 'zł', THB: '฿', IDR: 'Rp', MYR: 'RM',
PHP: '₱', TRY: '₺', AED: 'د.إ', SAR: '﷼', ILS: '₪', EGP: '£', NGN: '₦'
};
return symbols[code?.toUpperCase()] || code || '$';
}
function getUniqueCategories() {
var cats = {};
menuItems.forEach(function(item) {
// Only include category if the item is not in a deleted category
// Items in deleted categories should not appear
if (item.category && !item.original_category) {
cats[item.category] = true;
}
});
return ['all'].concat(Object.keys(cats));
}
function formatCategory(cat) {
if (cat === 'all') return 'All Items';
return cat.replace(/_/g, ' ').split(' ').map(function(word) { return word.charAt(0).toUpperCase() + word.slice(1); }).join(' ');
}
function getFilteredItems() {
// Filter out items that are in deleted categories (marked by original_category)
var items = menuItems.filter(function(item) {
return !item.original_category;
});
if (selectedCategory !== 'all') {
items = items.filter(function(item) { return item.category === selectedCategory; });
}
if (searchQuery && searchQuery.trim() !== '') {
var query = searchQuery.toLowerCase().trim();
items = items.filter(function(item) {
return (item.name && item.name.toLowerCase().indexOf(query) !== -1) ||
(item.description && item.description.toLowerCase().indexOf(query) !== -1);
});
}
return items;
}
var optionsModalOpen = false;
var selectedItemForOptions = null;
var selectedOptions = {};
var optionsSpecialInstructions = '';
var optionsQuantity = 1;
var itemQuantities = {};
var cartOpen = false;
var checkoutOpen = false;
var confirmationOpen = false;
var trackingOpen = false;
var currentOrderId = null;
var currentOrderData = null;
var previousOrderData = null;
var accountOpen = false;
var isUserLoggedIn = false;
var currentUser = null;
var checkoutData = {
name: '',
phone: '',
email: '',
orderType: '',
timingType: null, // 'asap' or 'scheduled'
address: '',
notes: '',
scheduledTime: '',
tipType: 'percentage',
tipPercentage: 0,
tipFlatAmount: 0,
paymentMethod: '',
deliveryLocation: null,
marketingOptIn: true,
partySize: null,
showDatePicker: false,
showTimePicker: false,
dineInInitialized: false,
promoCode: '',
appliedPromo: null,
openSection: null // Tracks which accordion section is manually open
};
// Check for logged-in user on load and auto-fill checkout
(async function() {
var token = null;
try { token = localStorage.getItem('base44_embedder_token'); } catch(e) {}
if (token) {
try {
var response = await fetch(BASE_URL + '/api/functions/embedderAuth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'verify', token: token })
});
var data = await response.json();
if (data.success && data.user) {
currentUser = data.user;
isUserLoggedIn = true;
// Auto-fill all forms with user data
checkoutData.name = data.user.full_name || '';
checkoutData.email = data.user.email || '';
checkoutData.phone = data.user.phone || '';
console.log('[Embedder] User auto-logged in and details autofilled:', data.user.email);
}
} catch(e) {
console.log('[Embedder] Token verification failed');
}
}
})();
var isSubmitting = false;
var calculatedDeliveryFee = 0;
var deliveryFeeData = null;
var deliveryFeeError = '';
var deliveryFeeLoading = false;
var stripeInstance = null;
var stripeElements = null;
var cardElement = null;
var stripePaymentProcessing = false;
var stripeError = '';
var googleMapsLoaded = false;
var autocompleteService = null;
var placesService = null;
var autocompleteSessionToken = null;
var addressSuggestions = [];
var showAddressSuggestions = false;
var gettingLocation = false;
var selectedAddressFromList = false;
function addToCart(item, options, specialInstructions) {
// Normalize options - treat null, undefined, and empty object as same
var normalizedOptions = (options && Object.keys(options).length > 0) ? options : null;
var optionsKey = normalizedOptions ? JSON.stringify(normalizedOptions) : '';
var instructions = specialInstructions || '';
// Check if same item with same options and instructions already exists in cart
var existingIndex = -1;
for (var i = 0; i < cart.length; i++) {
var cartItemOptions = cart[i].selected_options;
var cartItemHasOptions = cartItemOptions && Object.keys(cartItemOptions).length > 0;
var cartItemOptionsKey = cartItemHasOptions ? JSON.stringify(cartItemOptions) : '';
var cartItemInstructions = cart[i].special_instructions || '';
if (cart[i].id === item.id && cartItemOptionsKey === optionsKey && cartItemInstructions === instructions) {
existingIndex = i;
break;
}
}
if (existingIndex !== -1) {
// Increase quantity of existing item
cart[existingIndex].quantity += 1;
// Move the updated item to the end of the cart array to reflect it as the "last added"
var updatedItem = cart.splice(existingIndex, 1)[0];
cart.push(updatedItem);
} else {
// Find the original menu item to get base price
var originalMenuItem = menuItems.find(function(mi) { return mi.id === item.id; });
var basePrice = originalMenuItem ? originalMenuItem.price : item.price;
// Add new item to cart
var cartItem = Object.assign({}, item, {
quantity: 1,
cartId: Date.now() + Math.random(),
special_instructions: instructions,
selected_options: normalizedOptions || {},
price: basePrice // Start with base price
});
// Calculate price with options modifiers
if (normalizedOptions && item.options) {
item.options.forEach(function(optGroup) {
var selectedChoices = normalizedOptions[optGroup.name];
if (selectedChoices && optGroup.choices) {
// Normalize selected choices to array for uniform processing
var choicesArray = Array.isArray(selectedChoices) ? selectedChoices : [selectedChoices];
choicesArray.forEach(function(selectedChoice) {
var choice = optGroup.choices.find(function(c) {
return c.name === selectedChoice;
});
if (choice && choice.price_modifier) {
cartItem.price += choice.price_modifier;
}
});
}
});
}
cart.push(cartItem);
}
saveCart();
renderMenu();
}
function handleItemClick(item) {
if (item.options && item.options.length > 0) {
selectedItemForOptions = item;
selectedOptions = {};
optionsQuantity = 1;
item.options.forEach(function(opt) {
if (opt.type === 'single_select' && opt.choices && opt.choices.length > 0) {
selectedOptions[opt.name] = [opt.choices[0].name];
} else if (opt.type === 'multi_select') {
selectedOptions[opt.name] = [];
}
});
optionsModalOpen = true;
renderMenu();
} else {
addToCart(item);
}
}
function closeOptionsModal() {
optionsModalOpen = false;
selectedItemForOptions = null;
selectedOptions = {};
optionsSpecialInstructions = '';
optionsQuantity = 1;
renderMenu();
}
function addItemWithOptions() {
if (!selectedItemForOptions) return;
var valid = true;
if (selectedItemForOptions.options) {
selectedItemForOptions.options.forEach(function(opt) {
if (opt.required) {
var sel = selectedOptions[opt.name];
if (opt.type === 'single_select' && !sel) {
valid = false;
} else if (opt.type === 'multi_select') {
var minSel = opt.min_selections || 0;
if (!sel || sel.length < minSel) {
valid = false;
}
}
}
});
}
if (!valid) {
alert('Please select all required options');
return;
}
// Get the special instructions from the textarea
var instructionsTextarea = currentModalContent.querySelector('#options-special-instructions');
var instructions = instructionsTextarea ? instructionsTextarea.value : optionsSpecialInstructions;
// Convert selectedOptions to the format needed for addToCart
var optionsForCart = {};
Object.keys(selectedOptions).forEach(function(key) {
var val = selectedOptions[key];
if (Array.isArray(val) && val.length === 1) {
optionsForCart[key] = val[0]; // Convert single-item arrays to strings
} else {
optionsForCart[key] = val;
}
});
// Add to cart multiple times based on quantity
for (var i = 0; i < optionsQuantity; i++) {
addToCart(selectedItemForOptions, optionsForCart, instructions);
}
closeOptionsModal();
if (window.showCartPreviewBriefly) window.showCartPreviewBriefly();
}
function getCartTotal() {
return cart.reduce(function(sum, item) { return sum + (item.price * item.quantity); }, 0);
}
function getCartCount() {
return cart.reduce(function(sum, item) { return sum + item.quantity; }, 0);
}
function getTipAmount() {
if (!restaurant.tips_settings || !restaurant.tips_settings.enabled) return 0;
var orderTypeKey = checkoutData.orderType === 'delivery' ? 'delivery_enabled'
: checkoutData.orderType === 'pickup' ? 'pickup_enabled' : 'dine_in_enabled';
if (!restaurant.tips_settings[orderTypeKey]) return 0;
var subtotal = getCartTotal();
if (checkoutData.tipType === 'percentage') {
return subtotal * (checkoutData.tipPercentage / 100);
}
return checkoutData.tipFlatAmount || 0;
}
function getDeliveryFee() {
if (checkoutData.orderType !== 'delivery') return 0;
return calculatedDeliveryFee || 0;
}
function calculateDiscount() {
if (!checkoutData.appliedPromo) return 0;
var subtotal = getCartTotal();
var deliveryFee = getDeliveryFee();
if (checkoutData.appliedPromo.discount_type === 'percentage') {
return subtotal * (checkoutData.appliedPromo.discount_value / 100);
} else if (checkoutData.appliedPromo.discount_type === 'fixed_amount') {
return Math.min(checkoutData.appliedPromo.discount_value, subtotal);
} else if (checkoutData.appliedPromo.discount_type === 'free_delivery') {
return deliveryFee;
}
return 0;
}
function getFeeBreakdown() {
var otherFees = restaurant.other_fees || { enabled: false, items: [] };
// console.log('[Embedder Fees] otherFees:', otherFees);
// console.log('[Embedder Fees] orderType:', checkoutData.orderType);
if (!otherFees.enabled || !otherFees.items || !checkoutData.orderType) return [];
const onlinePaymentMethods = ['stripe', 'paypal', 'razorpay', 'square', 'gocardless', 'worldpay', 'revolut', 'verifone', 'wonderful', 'alipay', 'airwallex'];
const offlinePaymentMethods = ['cash_on_delivery', 'pay_cash_on_pickup', 'pay_card_on_pickup', 'pay_cash_on_delivery', 'pay_card_on_delivery', 'gcash', 'bkash'];
var subtotal = getCartTotal();
return otherFees.items.filter(function(fee) {
if (!fee.enabled) return false;
const orderTypes = fee.order_types || [];
// Treat empty array as "None" - strict check
if (orderTypes.length === 0) return false;
if (!orderTypes.includes('all') && !orderTypes.includes(checkoutData.orderType)) {
return false;
}
const paymentMethods = fee.payment_methods || [];
// Treat empty array as "None" - strict check
if (paymentMethods.length === 0) return false;
if (paymentMethods.includes('all')) {
return true;
}
// If specific payment methods are required but none selected yet, we can't show the fee
if (!checkoutData.paymentMethod) return false;
const isOnlinePayment = onlinePaymentMethods.includes(checkoutData.paymentMethod);
const isOfflinePayment = offlinePaymentMethods.includes(checkoutData.paymentMethod);
if (paymentMethods.includes('online') && isOnlinePayment) return true;
if (paymentMethods.includes('offline') && isOfflinePayment) return true;
if (paymentMethods.includes(checkoutData.paymentMethod)) return true;
return false;
}).map(function(fee){
var feeAmount = fee.fee_type === 'percentage'
? subtotal * (fee.fee_value / 100)
: fee.fee_value;
return { name: fee.fee_name || 'Fee', amount: feeAmount, type: fee.fee_type, value: fee.fee_value };
});
}
function calculateOtherFees() {
var feeBreakdown = getFeeBreakdown();
return feeBreakdown.reduce(function(total, fee) {
return total + fee.amount;
}, 0);
}
function formatOptionsForOrder(cartItem) {
var formattedOptions = [];
if (!cartItem.selected_options) return [];
var originalItem = menuItems.find(function(mi) { return mi.id === cartItem.id; });
Object.keys(cartItem.selected_options).forEach(function(optName) {
var selection = cartItem.selected_options[optName];
var choices = [];
// Handle both array and string inputs
var selectedNames = Array.isArray(selection) ? selection : [selection];
selectedNames.forEach(function(choiceName) {
var priceMod = 0;
// Try to find price modifier from original menu item
if (originalItem && originalItem.options) {
var optGroup = originalItem.options.find(function(og) { return og.name === optName; });
// Fallback if find fails or name slightly different
if (!optGroup) optGroup = originalItem.options.find(function(og) { return og.name === optName; });
if (optGroup && optGroup.choices) {
var choiceData = optGroup.choices.find(function(c) { return c.name === choiceName; });
if (choiceData) priceMod = choiceData.price_modifier || 0;
}
}
choices.push({
choice_name: choiceName,
price_modifier: priceMod
});
});
if (choices.length > 0) {
formattedOptions.push({
option_name: optName,
selected_choices: choices
});
}
});
return formattedOptions;
}
function calculateVAT() {
if (!restaurant.vat_settings || !restaurant.vat_settings.enabled || !restaurant.vat_settings.rate) {
return { vat_amount: 0, vat_rate: 0, vat_on_products: 0, vat_on_delivery: 0, vat_on_fees: 0, vat_on_tips: 0 };
}
var subtotal = getCartTotal();
var deliveryFee = getDeliveryFee();
var otherFees = calculateOtherFees();
var tipAmount = getTipAmount();
var vatRate = restaurant.vat_settings.rate / 100;
var vatOnProducts = 0;
var vatOnDelivery = 0;
var vatOnFees = 0;
var vatOnTips = 0;
if (restaurant.vat_settings.applies_to_products !== false) {
vatOnProducts = subtotal * vatRate;
}
if (restaurant.vat_settings.applies_to_delivery && deliveryFee > 0) {
vatOnDelivery = deliveryFee * vatRate;
}
if (restaurant.vat_settings.applies_to_fees && otherFees > 0) {
vatOnFees = otherFees * vatRate;
}
if (restaurant.vat_settings.applies_to_tips && tipAmount > 0) {
vatOnTips = tipAmount * vatRate;
}
var totalVat = vatOnProducts + vatOnDelivery + vatOnFees + vatOnTips;
return {
vat_amount: totalVat,
vat_rate: restaurant.vat_settings.rate,
vat_on_products: vatOnProducts,
vat_on_delivery: vatOnDelivery,
vat_on_fees: vatOnFees,
vat_on_tips: vatOnTips
};
}
function getOrderTotal() {
var subtotal = getCartTotal();
var deliveryFee = getDeliveryFee();
var tipAmount = getTipAmount();
var otherFees = calculateOtherFees();
var discount = calculateDiscount();
var vatCalc = calculateVAT();
if (restaurant.vat_settings && restaurant.vat_settings.price_inclusive) {
return subtotal + deliveryFee + tipAmount + otherFees - discount;
} else {
return subtotal + deliveryFee + tipAmount + otherFees - discount + vatCalc.vat_amount;
}
}
function getAvailablePaymentMethods() {
var methods = [];
var pg = restaurant.payment_gateways || {};
// Check Stripe
if (pg.stripe && pg.stripe.enabled) {
var isAvailableForOrderType =
(checkoutData.orderType === 'delivery' && pg.stripe.delivery !== false) ||
(checkoutData.orderType === 'pickup' && pg.stripe.pickup !== false) ||
(checkoutData.orderType === 'dine_in' && pg.stripe.dine_in !== false);
if (isAvailableForOrderType) {
methods.push({ id: 'stripe', name: 'Credit/Debit Card', icon: '💳', desc: 'Pay securely with Stripe' });
}
}
// Check PayPal
if (pg.paypal && pg.paypal.enabled) {
var isAvailableForOrderType =
(checkoutData.orderType === 'delivery' && pg.paypal.delivery !== false) ||
(checkoutData.orderType === 'pickup' && pg.paypal.pickup !== false) ||
(checkoutData.orderType === 'dine_in' && pg.paypal.dine_in !== false);
if (isAvailableForOrderType) {
methods.push({ id: 'paypal', name: 'PayPal', icon: '💰', desc: 'Pay with PayPal' });
}
}
// Check Razorpay
if (pg.razorpay && pg.razorpay.enabled) {
var isAvailableForOrderType =
(checkoutData.orderType === 'delivery' && pg.razorpay.delivery !== false) ||
(checkoutData.orderType === 'pickup' && pg.razorpay.pickup !== false) ||
(checkoutData.orderType === 'dine_in' && pg.razorpay.dine_in !== false);
if (isAvailableForOrderType) {
methods.push({ id: 'razorpay', name: 'Razorpay', icon: '💳', desc: 'UPI, Cards, Wallets' });
}
}
// Check offline payments for pickup and dine-in
if (checkoutData.orderType === 'pickup' || checkoutData.orderType === 'dine_in') {
if (pg.pay_cash_on_pickup && pg.pay_cash_on_pickup.enabled) {
var isAvailableForOrderType =
(checkoutData.orderType === 'pickup' && pg.pay_cash_on_pickup.pickup !== false) ||
(checkoutData.orderType === 'dine_in' && pg.pay_cash_on_pickup.dine_in !== false);
if (isAvailableForOrderType) {
methods.push({ id: 'cash_on_delivery', name: 'Pay Cash at Counter', icon: '💰', desc: 'Pay with cash at the counter' });
}
}
if (pg.pay_card_on_pickup && pg.pay_card_on_pickup.enabled) {
var isAvailableForOrderType =
(checkoutData.orderType === 'pickup' && pg.pay_card_on_pickup.pickup !== false) ||
(checkoutData.orderType === 'dine_in' && pg.pay_card_on_pickup.dine_in !== false);
if (isAvailableForOrderType) {
methods.push({ id: 'pay_card_on_pickup', name: 'Pay Card at Counter', icon: '💳', desc: 'Pay with card at the counter' });
}
}
}
// Check offline payments for delivery
if (checkoutData.orderType === 'delivery' && restaurant.delivery_enabled !== false) {
if (pg.pay_cash_on_delivery && pg.pay_cash_on_delivery.enabled) {
methods.push({ id: 'cash_on_delivery', name: 'Cash on Delivery', icon: '💰', desc: 'Pay with cash when delivered' });
}
if (pg.pay_card_on_delivery && pg.pay_card_on_delivery.enabled) {
methods.push({ id: 'pay_card_on_delivery', name: 'Card on Delivery', icon: '💳', desc: 'Pay with card on delivery' });
}
}
if (methods.length === 0) {
methods.push({ id: 'cash_on_delivery', name: checkoutData.orderType === 'delivery' ? 'Cash on Delivery' : 'Pay at Counter', icon: '💰', desc: checkoutData.orderType === 'delivery' ? 'Pay when delivered' : 'Pay when you pick up' });
}
return methods;
}
function openCart() {
cartOpen = true;
renderMenu();
}
function closeCart() {
cartOpen = false;
renderMenu();
}
async function goToCheckout() {
var isDeliveryEnabled = restaurant.delivery_enabled !== false;
var isPickupEnabled = restaurant.pickup_enabled !== false;
var isDineInEnabled = restaurant.dine_in_enabled === true;
var isAnyServiceEnabled = isDeliveryEnabled || isPickupEnabled || isDineInEnabled;
if (!isAnyServiceEnabled) {
alert('Sorry, this restaurant is currently not accepting orders.');
return;
}
// Auto-select order type logic removed - User must select manually
if (!checkoutData.orderType) {
checkoutData.orderType = '';
}
cartOpen = false;
checkoutOpen = true;
confirmationOpen = false;
trackingOpen = false;
// Refresh restaurant data to ensure latest fees/tips/settings
try {
var resp = await fetch(BASE_URL + '/api/functions/getPublicMenuData?rid=' + RESTAURANT_ID);
if (resp.ok) {
var fresh = await resp.json();
if (fresh.success && fresh.restaurant) {
restaurant = fresh.restaurant;
EMBEDDED_RESTAURANT = fresh.restaurant;
if (Array.isArray(fresh.menuItems)) {
menuItems = fresh.menuItems.filter(function(item){ return item.available !== false; });
EMBEDDED_MENU_ITEMS = menuItems;
}
}
}
} catch (e) {
console.warn('[Embedder] Could not refresh data on checkout:', e);
}
renderMenu();
}
function closeCheckout() {
checkoutOpen = false;
renderMenu();
}
function goToConfirmation(orderId) {
currentOrderId = orderId;
checkoutOpen = false;
confirmationOpen = true;
trackingOpen = false;
// Show loading state immediately
currentOrderData = null;
previousOrderData = null;
renderMenu();
// Then fetch actual order data
fetchOrderData(orderId);
}
function closeConfirmation() {
confirmationOpen = false;
currentOrderId = null;
currentOrderData = null;
previousOrderData = null;
console.log('[Embedder] Closed confirmation, stopped polling');
renderMenu();
}
function openAccount() {
// Check if user is already logged in via token
var token = null;
try { token = localStorage.getItem('base44_embedder_token'); } catch(e) {}
if (token && !currentUser) {
// Verify token and get user
verifyTokenAndShowOrders(token);
} else if (currentUser) {
// Already logged in - show orders directly
fetchUserOrders();
} else {
// Not logged in - show login popup directly
renderLoginPopup();
}
}
async function verifyTokenAndShowOrders(token) {
try {
var response = await fetch(BASE_URL + '/api/functions/embedderAuth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'verify', token: token })
});
var data = await response.json();
if (data.success && data.user) {
currentUser = data.user;
isUserLoggedIn = true;
fetchUserOrders();
} else {
// Token invalid - clear and show login popup
try { localStorage.removeItem('base44_embedder_token'); } catch(e) {}
renderLoginPopup();
}
} catch (err) {
renderLoginPopup();
}
}
function renderLoginPopup() {
if (!currentModalContent) return;
var html = '';
html += '
';
html += '
';
html += '
🔐
';
html += '
Welcome Back ';
html += '
Login to view your orders
';
html += '
';
// Login form
html += '
';
// Signup form (hidden by default)
html += '
';
// Forgot Password form (hidden by default)
html += '
';
html += '
Email
';
html += '
';
html += '
';
html += '
Send Reset Link ';
html += '
';
html += '
';
html += '
← Back to Menu ';
html += '
';
currentModalContent.innerHTML = html;
// Attach all event listeners
attachLoginPopupListeners();
}
function attachLoginPopupListeners() {
if (!currentModalContent) return;
// Back button
var backBtn = currentModalContent.querySelector('#login-back');
if (backBtn) {
backBtn.onclick = function() {
accountOpen = false;
trackingOpen = false;
renderMenu();
};
}
// Toggle between login, signup, and forgot password
var showSignupLink = currentModalContent.querySelector('#show-signup');
var showLoginLink = currentModalContent.querySelector('#show-login');
var showForgotPasswordLink = currentModalContent.querySelector('#show-forgot-password');
var showLoginFromForgotLink = currentModalContent.querySelector('#show-login-from-forgot');
var loginForm = currentModalContent.querySelector('#login-form-section');
var signupForm = currentModalContent.querySelector('#signup-form-section');
var forgotPasswordForm = currentModalContent.querySelector('#forgot-password-section');
if (showSignupLink) {
showSignupLink.onclick = function() {
loginForm.style.display = 'none';
signupForm.style.display = 'block';
if (forgotPasswordForm) forgotPasswordForm.style.display = 'none';
};
}
if (showLoginLink) {
showLoginLink.onclick = function() {
signupForm.style.display = 'none';
loginForm.style.display = 'block';
if (forgotPasswordForm) forgotPasswordForm.style.display = 'none';
};
}
if (showForgotPasswordLink) {
showForgotPasswordLink.onclick = function() {
loginForm.style.display = 'none';
signupForm.style.display = 'none';
if (forgotPasswordForm) forgotPasswordForm.style.display = 'block';
};
}
if (showLoginFromForgotLink) {
showLoginFromForgotLink.onclick = function() {
if (forgotPasswordForm) forgotPasswordForm.style.display = 'none';
loginForm.style.display = 'block';
signupForm.style.display = 'none';
};
}
// Login submit
var loginSubmitBtn = currentModalContent.querySelector('#login-submit');
if (loginSubmitBtn) {
loginSubmitBtn.onclick = async function() {
var email = currentModalContent.querySelector('#login-email').value;
var password = currentModalContent.querySelector('#login-password').value;
var errorDiv = currentModalContent.querySelector('#login-error');
if (!email || !password) {
errorDiv.textContent = 'Please enter email and password';
errorDiv.style.display = 'block';
return;
}
loginSubmitBtn.textContent = 'Signing in...';
loginSubmitBtn.disabled = true;
try {
var response = await fetch(BASE_URL + '/api/functions/embedderAuth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'login', email: email, password: password })
});
var data = await response.json();
if (data.success) {
try { localStorage.setItem('base44_embedder_token', data.token); } catch(e) {}
currentUser = data.user;
isUserLoggedIn = true;
// Immediately show orders view
fetchUserOrders();
} else {
errorDiv.textContent = data.error || 'Login failed';
errorDiv.style.display = 'block';
loginSubmitBtn.textContent = 'Sign In';
loginSubmitBtn.disabled = false;
}
} catch (err) {
errorDiv.textContent = 'Connection error. Please try again.';
errorDiv.style.display = 'block';
loginSubmitBtn.textContent = 'Sign In';
loginSubmitBtn.disabled = false;
}
};
}
// Signup submit
var signupSubmitBtn = currentModalContent.querySelector('#signup-submit');
if (signupSubmitBtn) {
signupSubmitBtn.onclick = async function() {
var name = currentModalContent.querySelector('#signup-name').value;
var email = currentModalContent.querySelector('#signup-email').value;
var phone = currentModalContent.querySelector('#signup-phone').value;
var password = currentModalContent.querySelector('#signup-password').value;
var passwordConfirm = currentModalContent.querySelector('#signup-password-confirm').value;
var errorDiv = currentModalContent.querySelector('#signup-error');
if (!name || !email || !password || !passwordConfirm) {
errorDiv.textContent = 'Please fill in all required fields';
errorDiv.style.display = 'block';
return;
}
if (password !== passwordConfirm) {
errorDiv.textContent = 'Passwords do not match';
errorDiv.style.display = 'block';
return;
}
signupSubmitBtn.textContent = 'Creating account...';
signupSubmitBtn.disabled = true;
try {
var response = await fetch(BASE_URL + '/api/functions/embedderAuth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'signup', email: email, password: password, full_name: name, phone: phone })
});
var data = await response.json();
if (data.success) {
try { localStorage.setItem('base44_embedder_token', data.token); } catch(e) {}
currentUser = data.user;
isUserLoggedIn = true;
// Immediately show orders view
fetchUserOrders();
} else {
errorDiv.textContent = data.error || 'Signup failed';
errorDiv.style.display = 'block';
signupSubmitBtn.textContent = 'Create Account';
signupSubmitBtn.disabled = false;
}
} catch (err) {
errorDiv.textContent = 'Connection error. Please try again.';
errorDiv.style.display = 'block';
signupSubmitBtn.textContent = 'Create Account';
signupSubmitBtn.disabled = false;
}
};
}
// Forgot password submit
var forgotSubmitBtn = currentModalContent.querySelector('#forgot-submit');
if (forgotSubmitBtn) {
forgotSubmitBtn.onclick = async function() {
var email = currentModalContent.querySelector('#forgot-email').value;
var errorDiv = currentModalContent.querySelector('#forgot-error');
var successDiv = currentModalContent.querySelector('#forgot-success');
if (!email) {
errorDiv.textContent = 'Please enter your email address';
errorDiv.style.display = 'block';
successDiv.style.display = 'none';
return;
}
forgotSubmitBtn.textContent = 'Sending...';
forgotSubmitBtn.disabled = true;
errorDiv.style.display = 'none';
successDiv.style.display = 'none';
try {
var response = await fetch(BASE_URL + '/api/functions/requestPasswordReset', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: email })
});
var data = await response.json();
if (data.success) {
successDiv.textContent = '✅ Reset link sent! Check your email.';
successDiv.style.display = 'block';
errorDiv.style.display = 'none';
} else {
errorDiv.textContent = data.error || 'Failed to send reset link';
errorDiv.style.display = 'block';
successDiv.style.display = 'none';
}
forgotSubmitBtn.textContent = 'Send Reset Link';
forgotSubmitBtn.disabled = false;
} catch (err) {
errorDiv.textContent = 'Connection error. Please try again.';
errorDiv.style.display = 'block';
successDiv.style.display = 'none';
forgotSubmitBtn.textContent = 'Send Reset Link';
forgotSubmitBtn.disabled = false;
}
};
}
}
function closeAccount() {
accountOpen = false;
trackingOpen = false;
renderMenu();
}
function handleLogin() {
// Show login inside modal
accountOpen = false;
renderLoginInModal();
}
function renderLoginInModal() {
if (!currentModalContent) return;
var html = '';
html += '
';
html += '
';
html += '
🔐
';
html += '
Welcome Back ';
html += '
Login to view your orders
';
html += '
';
// Native login form instead of iframe
html += '
';
// Signup form (hidden by default)
html += '
';
// Forgot Password form (hidden by default)
html += '
';
html += '
Email
';
html += '
';
html += '
';
html += '
Send Reset Link ';
html += '
';
html += '
';
html += '
← Back to Menu ';
html += '
';
currentModalContent.innerHTML = html;
// Back button
var backBtn = currentModalContent.querySelector('#login-back');
if (backBtn) {
backBtn.onclick = function() {
accountOpen = false;
trackingOpen = false;
renderMenu();
};
}
// Toggle between login, signup, and forgot password
var showSignupLink = currentModalContent.querySelector('#show-signup');
var showLoginLink = currentModalContent.querySelector('#show-login');
var showForgotPasswordLink = currentModalContent.querySelector('#show-forgot-password');
var showLoginFromForgotLink = currentModalContent.querySelector('#show-login-from-forgot');
var loginForm = currentModalContent.querySelector('#native-login-form');
var signupForm = currentModalContent.querySelector('#native-signup-form');
var forgotPasswordForm = currentModalContent.querySelector('#native-forgot-password');
if (showSignupLink) {
showSignupLink.onclick = function() {
loginForm.style.display = 'none';
signupForm.style.display = 'block';
if (forgotPasswordForm) forgotPasswordForm.style.display = 'none';
};
}
if (showLoginLink) {
showLoginLink.onclick = function() {
signupForm.style.display = 'none';
loginForm.style.display = 'block';
if (forgotPasswordForm) forgotPasswordForm.style.display = 'none';
};
}
if (showForgotPasswordLink) {
showForgotPasswordLink.onclick = function() {
loginForm.style.display = 'none';
signupForm.style.display = 'none';
if (forgotPasswordForm) forgotPasswordForm.style.display = 'block';
};
}
if (showLoginFromForgotLink) {
showLoginFromForgotLink.onclick = function() {
if (forgotPasswordForm) forgotPasswordForm.style.display = 'none';
loginForm.style.display = 'block';
signupForm.style.display = 'none';
};
}
// Login submit
var loginSubmitBtn = currentModalContent.querySelector('#login-submit');
if (loginSubmitBtn) {
loginSubmitBtn.onclick = async function() {
var email = currentModalContent.querySelector('#login-email').value;
var password = currentModalContent.querySelector('#login-password').value;
var errorDiv = currentModalContent.querySelector('#login-error');
if (!email || !password) {
errorDiv.textContent = 'Please enter email and password';
errorDiv.style.display = 'block';
return;
}
loginSubmitBtn.textContent = 'Signing in...';
loginSubmitBtn.disabled = true;
try {
var response = await fetch(BASE_URL + '/api/functions/embedderAuth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'login', email: email, password: password })
});
var data = await response.json();
if (data.success) {
try { localStorage.setItem('base44_embedder_token', data.token); } catch(e) {}
currentUser = data.user;
isUserLoggedIn = true;
// Go to order tracking view
trackingOpen = true;
accountOpen = false;
renderOrdersView();
} else {
errorDiv.textContent = data.error || 'Login failed';
errorDiv.style.display = 'block';
loginSubmitBtn.textContent = 'Sign In';
loginSubmitBtn.disabled = false;
}
} catch (err) {
errorDiv.textContent = 'Connection error. Please try again.';
errorDiv.style.display = 'block';
loginSubmitBtn.textContent = 'Sign In';
loginSubmitBtn.disabled = false;
}
};
}
// Signup submit
var signupSubmitBtn = currentModalContent.querySelector('#signup-submit');
if (signupSubmitBtn) {
signupSubmitBtn.onclick = async function() {
var name = currentModalContent.querySelector('#signup-name').value;
var email = currentModalContent.querySelector('#signup-email').value;
var phone = currentModalContent.querySelector('#signup-phone').value;
var password = currentModalContent.querySelector('#signup-password').value;
var passwordConfirm = currentModalContent.querySelector('#signup-password-confirm').value;
var errorDiv = currentModalContent.querySelector('#signup-error');
if (!name || !email || !password || !passwordConfirm) {
errorDiv.textContent = 'Please fill in all required fields';
errorDiv.style.display = 'block';
return;
}
if (password !== passwordConfirm) {
errorDiv.textContent = 'Passwords do not match';
errorDiv.style.display = 'block';
return;
}
signupSubmitBtn.textContent = 'Creating account...';
signupSubmitBtn.disabled = true;
try {
var response = await fetch(BASE_URL + '/api/functions/embedderAuth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'signup', email: email, password: password, full_name: name, phone: phone })
});
var data = await response.json();
if (data.success) {
try { localStorage.setItem('base44_embedder_token', data.token); } catch(e) {}
currentUser = data.user;
isUserLoggedIn = true;
trackingOpen = true;
accountOpen = false;
fetchUserOrders();
} else {
errorDiv.textContent = data.error || 'Signup failed';
errorDiv.style.display = 'block';
signupSubmitBtn.textContent = 'Create Account';
signupSubmitBtn.disabled = false;
}
} catch (err) {
errorDiv.textContent = 'Connection error. Please try again.';
errorDiv.style.display = 'block';
signupSubmitBtn.textContent = 'Create Account';
signupSubmitBtn.disabled = false;
}
};
}
// Forgot password submit
var forgotSubmitBtn = currentModalContent.querySelector('#forgot-submit');
if (forgotSubmitBtn) {
forgotSubmitBtn.onclick = async function() {
var email = currentModalContent.querySelector('#forgot-email').value;
var errorDiv = currentModalContent.querySelector('#forgot-error');
var successDiv = currentModalContent.querySelector('#forgot-success');
if (!email) {
errorDiv.textContent = 'Please enter your email address';
errorDiv.style.display = 'block';
successDiv.style.display = 'none';
return;
}
forgotSubmitBtn.textContent = 'Sending...';
forgotSubmitBtn.disabled = true;
errorDiv.style.display = 'none';
successDiv.style.display = 'none';
try {
var response = await fetch(BASE_URL + '/api/functions/requestPasswordReset', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: email })
});
var data = await response.json();
if (data.success) {
successDiv.textContent = '✅ Reset link sent! Check your email.';
successDiv.style.display = 'block';
errorDiv.style.display = 'none';
} else {
errorDiv.textContent = data.error || 'Failed to send reset link';
errorDiv.style.display = 'block';
successDiv.style.display = 'none';
}
forgotSubmitBtn.textContent = 'Send Reset Link';
forgotSubmitBtn.disabled = false;
} catch (err) {
errorDiv.textContent = 'Connection error. Please try again.';
errorDiv.style.display = 'block';
successDiv.style.display = 'none';
forgotSubmitBtn.textContent = 'Send Reset Link';
forgotSubmitBtn.disabled = false;
}
};
}
}
var userOrders = [];
var isLoadingUserOrders = false;
async function fetchUserOrders() {
if (!currentUser || !currentUser.email) {
console.log('[Embedder] No currentUser, skipping fetch');
isLoadingUserOrders = false;
userOrders = [];
trackingOpen = true;
accountOpen = false;
renderOrdersView();
return;
}
isLoadingUserOrders = true;
userOrders = [];
trackingOpen = true;
accountOpen = false;
renderOrdersView();
try {
console.log('[Embedder] Fetching orders for:', currentUser.email);
var response = await fetch(BASE_URL + '/api/functions/findOrders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: currentUser.email,
restaurantId: RESTAURANT_ID
})
});
var data = await response.json();
if (data.orders) {
userOrders = data.orders;
console.log('[Embedder] SUCCESS - Got', userOrders.length, 'orders');
} else {
userOrders = [];
console.log('[Embedder] No orders found');
}
} catch (error) {
console.error('[Embedder] Fetch failed:', error);
userOrders = [];
}
isLoadingUserOrders = false;
renderOrdersView();
}
function renderOrdersView() {
if (!currentModalContent) return;
var currency = getCurrencySymbol(restaurant.currency);
var html = '';
// Only show content if we have currentUser
if (!currentUser) {
html += '
';
html += '
';
currentModalContent.innerHTML = html;
return;
}
// Header with user info and logout - mobile responsive
html += '';
html += '
Welcome, ' + (currentUser.full_name || currentUser.email) + '
';
html += '
';
html += '';
html += 'Logout ';
html += '
';
html += '📦 My Orders ';
if (isLoadingUserOrders) {
html += '';
} else if (userOrders.length === 0) {
html += '';
html += '
🛍️
';
html += '
No Orders Yet ';
html += '
Your order history will appear here
';
html += '
Start Shopping ';
html += '
';
} else {
html += '';
html += '';
html += '
';
userOrders.forEach(function(order) {
var statusConfig = {
pending: { icon: '⏳', title: 'Pending', color: '#d97706', bgColor: '#fef3c7' },
confirmed: { icon: '✅', title: 'Confirmed', color: '#059669', bgColor: '#d1fae5' },
preparing: { icon: '👨🍳', title: 'Preparing', color: '#ea580c', bgColor: '#ffedd5' },
ready: { icon: '🛍️', title: 'Ready', color: '#2563eb', bgColor: '#dbeafe' },
out_for_delivery: { icon: '🚗', title: 'On the Way', color: '#7c3aed', bgColor: '#ede9fe' },
completed: { icon: '✅', title: 'Completed', color: '#059669', bgColor: '#d1fae5' },
cancelled: { icon: '❌', title: 'Cancelled', color: '#dc2626', bgColor: '#fee2e2' }
};
var status = statusConfig[order.status] || statusConfig.pending;
html += '
';
html += '
';
html += '#' + order.id.slice(0, 8).toUpperCase() + ' ';
html += '' + status.icon + ' ' + status.title + ' ';
html += '
';
html += '
' + (order.order_type === 'delivery' ? '🚗 Delivery' : order.order_type === 'dine_in' ? '🍽️ Dine In' + (order.table_number ? ' (Table ' + order.table_number + ')' : '') : '🛍️ Pickup') + '
';
html += '
' + new Date(order.created_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true }) + '
';
html += '
' + (order.items?.length || 0) + ' items
';
// Expected time with countdown if available
if (order.estimated_ready_time && ['confirmed', 'preparing', 'ready', 'out_for_delivery'].includes(order.status)) {
var expectedTime = new Date(order.estimated_ready_time);
var formattedExpectedTime = expectedTime.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
var remaining = expectedTime.getTime() - Date.now();
if (remaining > 0) {
var mins = Math.floor(remaining / 60000);
html += '
⏱️ Ready in ~' + mins + ' min • Expected: ' + formattedExpectedTime + '
';
} else {
html += '
⏰ Expected: ' + formattedExpectedTime + '
';
}
}
html += '
' + currency + (order.total || 0).toFixed(2) + '
';
html += '
';
});
html += '
';
}
html += '';
currentModalContent.innerHTML = html;
// Attach event listeners
var backMenuBtn = currentModalContent.querySelector('#orders-back-menu');
if (backMenuBtn) {
backMenuBtn.onclick = function() {
trackingOpen = false;
accountOpen = false;
renderMenu();
};
}
var logoutBtn = currentModalContent.querySelector('#orders-logout');
if (logoutBtn) {
logoutBtn.onclick = async function() {
var token = null;
try { token = localStorage.getItem('base44_embedder_token'); } catch(e) {}
if (token) {
try {
await fetch(BASE_URL + '/api/functions/embedderAuth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'logout', token: token })
});
} catch(e) {}
}
try { localStorage.removeItem('base44_embedder_token'); } catch(e) {}
currentUser = null;
isUserLoggedIn = false;
userOrders = [];
trackingOpen = false;
accountOpen = false;
renderMenu();
};
}
var startShoppingBtn = currentModalContent.querySelector('#orders-start-shopping');
if (startShoppingBtn) {
startShoppingBtn.onclick = function() {
trackingOpen = false;
accountOpen = false;
renderMenu();
};
}
// Order card clicks - show order details
var orderCards = currentModalContent.querySelectorAll('.user-order-card');
orderCards.forEach(function(card) {
card.onclick = function() {
var orderId = card.dataset.orderId;
currentOrderId = orderId;
currentOrderData = null;
trackingOpen = false;
confirmationOpen = true;
renderMenu();
// Start polling for real-time updates
fetchOrderData(orderId);
};
});
}
function fetchOrderData(orderId) {
console.log('[Embedder] Fetching order data for:', orderId);
fetch(BASE_URL + '/api/functions/getOrderStatus?orderId=' + orderId + '&t=' + Date.now())
.then(function(res) { return res.json(); })
.then(function(data) {
console.log('[Embedder] Got order data:', data);
if (data.success && data.order) {
// Check if data actually changed before re-rendering
var hasChanged = !previousOrderData ||
previousOrderData.status !== data.order.status ||
JSON.stringify(previousOrderData.driver_location) !== JSON.stringify(data.order.driver_location) ||
previousOrderData.estimated_ready_time !== data.order.estimated_ready_time;
currentOrderData = data.order;
previousOrderData = JSON.parse(JSON.stringify(data.order)); // Deep copy
console.log('[Embedder] Data changed:', hasChanged, 'Status:', data.order.status);
if (confirmationOpen && hasChanged) {
renderMenu();
}
}
// Continue polling every 3 seconds if still on confirmation page
if (confirmationOpen && currentOrderId === orderId) {
setTimeout(function() {
fetchOrderData(orderId);
}, 3000);
}
})
.catch(function(err) {
console.error('[Embedder] Failed to fetch order:', err);
// Continue polling even on error
if (confirmationOpen && currentOrderId === orderId) {
setTimeout(function() {
fetchOrderData(orderId);
}, 4000);
}
});
}
function loadStripe() {
if (!STRIPE_PUBLISHABLE_KEY || window.Stripe) {
if (window.Stripe && STRIPE_PUBLISHABLE_KEY) {
initStripe();
}
return;
}
var script = document.createElement('script');
script.src = 'https://js.stripe.com/v3/';
script.async = true;
script.onload = function() {
initStripe();
};
document.head.appendChild(script);
}
function initStripe() {
if (window.Stripe && STRIPE_PUBLISHABLE_KEY && !stripeInstance) {
stripeInstance = window.Stripe(STRIPE_PUBLISHABLE_KEY);
initPaymentRequest();
}
}
var paymentRequest = null;
var canMakePaymentRequest = false;
function initPaymentRequest() {
if (!stripeInstance || !restaurant) return;
var total = getOrderTotal();
var currencyCode = (restaurant.currency || 'USD').toLowerCase();
paymentRequest = stripeInstance.paymentRequest({
country: 'US',
currency: currencyCode,
total: {
label: restaurant.name || 'Order Total',
amount: Math.round(total * 100),
},
requestPayerName: true,
requestPayerEmail: true,
requestPayerPhone: true,
});
paymentRequest.canMakePayment().then(function(result) {
if (result) {
canMakePaymentRequest = true;
console.log('Apple Pay / Google Pay available');
}
});
paymentRequest.on('paymentmethod', async function(ev) {
try {
var subtotal = getCartTotal();
var deliveryFee = getDeliveryFee();
var tipAmount = getTipAmount();
var total = subtotal + deliveryFee + tipAmount;
var response = await fetch(BASE_URL + '/api/functions/createStripePaymentIntent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: total,
currency: (restaurant.currency || 'USD').toLowerCase(),
restaurant_id: RESTAURANT_ID,
order_data: {
customer_name: ev.payerName || checkoutData.name,
customer_email: ev.payerEmail || checkoutData.email,
restaurant_location: restaurant.location || null,
delivery_location: checkoutData.deliveryLocation || null
}
})
});
var data = await response.json();
var clientSecret = data.client_secret || data.clientSecret;
if (!clientSecret) {
ev.complete('fail');
return;
}
var confirmResult = await stripeInstance.confirmCardPayment(
clientSecret,
{ payment_method: ev.paymentMethod.id },
{ handleActions: false }
);
if (confirmResult.error) {
ev.complete('fail');
return;
}
if (confirmResult.paymentIntent.status === 'requires_action') {
var actionResult = await stripeInstance.confirmCardPayment(clientSecret);
if (actionResult.error) {
ev.complete('fail');
return;
}
}
ev.complete('success');
submitOrderWithPayment(confirmResult.paymentIntent.id);
} catch (err) {
ev.complete('fail');
console.error('Payment Request error:', err);
}
});
}
function updatePaymentRequestTotal() {
if (paymentRequest) {
var total = getOrderTotal();
paymentRequest.update({
total: {
label: restaurant.name || 'Order Total',
amount: Math.round(total * 100),
}
});
}
}
function mountPaymentRequestButton() {
if (!stripeInstance || !paymentRequest || !canMakePaymentRequest || !currentModalContent) return;
var prButtonContainer = currentModalContent.querySelector('#payment-request-button');
if (!prButtonContainer || prButtonContainer.dataset.mounted === 'true') return;
updatePaymentRequestTotal();
var prButton = stripeInstance.elements().create('paymentRequestButton', {
paymentRequest: paymentRequest,
style: {
paymentRequestButton: {
type: 'default',
theme: 'dark',
height: '48px',
},
},
});
prButton.mount(prButtonContainer);
prButtonContainer.dataset.mounted = 'true';
}
function mountStripeCard() {
if (!stripeInstance || !currentModalContent) return;
var cardContainer = currentModalContent.querySelector('#stripe-card-element');
if (!cardContainer) return;
if (cardElement && cardContainer.children.length > 0) return;
if (cardElement) {
try { cardElement.unmount(); } catch(e) {}
cardElement = null;
stripeElements = null;
}
try {
stripeElements = stripeInstance.elements();
cardElement = stripeElements.create('card', {
style: {
base: {
fontSize: '16px',
color: '#1e293b',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
'::placeholder': { color: '#94a3b8' }
},
invalid: { color: '#ef4444' }
}
});
cardElement.mount(cardContainer);
cardElement.on('change', function(event) {
stripeError = event.error ? event.error.message : '';
var errorDisplay = currentModalContent.querySelector('#stripe-card-errors');
if (errorDisplay) errorDisplay.textContent = stripeError;
});
} catch (err) {
console.error('Error mounting Stripe card:', err);
stripeError = 'Failed to load payment form: ' + err.message;
}
}
async function processStripePayment() {
if (stripePaymentProcessing || !stripeInstance || !cardElement) return;
// Check minimum order based on order type
var subtotal = getCartTotal();
var minimumOrder = checkoutData.orderType === 'delivery'
? (restaurant.delivery_minimum_order || 0)
: checkoutData.orderType === 'pickup'
? (restaurant.pickup_minimum_order || 0)
: (restaurant.dine_in_minimum_order || 0);
if (minimumOrder > 0 && subtotal < minimumOrder) {
var currency = getCurrencySymbol(restaurant.currency);
alert('Minimum order of ' + currency + minimumOrder.toFixed(2) + ' required for ' + checkoutData.orderType.replace(/_/g, ' '));
return;
}
stripePaymentProcessing = true;
stripeError = '';
var payBtn = currentModalContent.querySelector('#stripe-pay-btn');
if (payBtn) {
payBtn.disabled = true;
payBtn.textContent = '⏳ Processing...';
}
try {
var deliveryFee = getDeliveryFee();
var tipAmount = getTipAmount();
var total = subtotal + deliveryFee + tipAmount;
var response = await fetch(BASE_URL + '/api/functions/createStripePaymentIntent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: total,
currency: (restaurant.currency || 'USD').toLowerCase(),
restaurant_id: RESTAURANT_ID,
order_data: {
customer_name: checkoutData.name,
customer_email: checkoutData.email,
restaurant_location: restaurant.location || null,
delivery_location: checkoutData.deliveryLocation || null
}
})
});
var data = await response.json();
var clientSecret = data.client_secret || data.clientSecret;
if (!clientSecret) {
throw new Error(data.error || 'Failed to create payment intent');
}
var result = await stripeInstance.confirmCardPayment(clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
name: checkoutData.name,
email: checkoutData.email || undefined,
phone: checkoutData.phone
}
}
});
if (result.error) {
stripeError = result.error.message;
stripePaymentProcessing = false;
var errorDisplay = currentModalContent.querySelector('#stripe-card-errors');
if (errorDisplay) errorDisplay.textContent = stripeError;
if (payBtn) {
payBtn.disabled = false;
payBtn.textContent = '💳 Pay with Card';
}
} else if (result.paymentIntent && result.paymentIntent.status === 'succeeded') {
submitOrderWithPayment(result.paymentIntent.id);
}
} catch (err) {
stripeError = err.message || 'Payment failed';
stripePaymentProcessing = false;
var errorDisplay = currentModalContent.querySelector('#stripe-card-errors');
if (errorDisplay) errorDisplay.textContent = stripeError;
if (payBtn) {
payBtn.disabled = false;
payBtn.textContent = '💳 Pay with Card';
}
}
}
function submitOrderWithPayment(paymentId) {
var subtotal = getCartTotal();
var deliveryFee = getDeliveryFee();
var tipAmount = getTipAmount();
var otherFeesTotal = calculateOtherFees();
var discount = calculateDiscount();
var vatCalc = calculateVAT();
var total = restaurant.vat_settings && restaurant.vat_settings.price_inclusive
? subtotal + deliveryFee + tipAmount + otherFeesTotal - discount
: subtotal + deliveryFee + tipAmount + otherFeesTotal - discount + vatCalc.vat_amount;
// Use logged-in user's email if available for order matching
var orderCustomerEmail = (currentUser && currentUser.email) ? currentUser.email : checkoutData.email;
var orderData = {
restaurant_id: RESTAURANT_ID,
restaurant_location: restaurant.location || null,
customer_name: checkoutData.name,
customer_phone: checkoutData.phone,
customer_email: orderCustomerEmail,
order_type: checkoutData.orderType,
delivery_address: checkoutData.orderType === 'delivery' ? checkoutData.address : '',
delivery_location: checkoutData.deliveryLocation || null,
special_notes: checkoutData.notes,
scheduled_time: checkoutData.scheduledTime || null,
items: cart.map(function(item) {
return {
item_id: item.id,
item_name: item.name,
quantity: item.quantity,
price: item.price,
special_instructions: item.special_instructions || '',
selected_options: formatOptionsForOrder(item)
};
}),
subtotal: subtotal,
delivery_fee: deliveryFee,
other_fees: otherFeesTotal,
other_fees_breakdown: getFeeBreakdown().map(function(f) { return { name: f.name, amount: f.amount }; }),
tip_amount: tipAmount,
vat_amount: vatCalc.vat_amount,
vat_rate: vatCalc.vat_rate,
vat_on_products: vatCalc.vat_on_products,
vat_on_delivery: vatCalc.vat_on_delivery,
vat_on_fees: vatCalc.vat_on_fees,
vat_on_tips: vatCalc.vat_on_tips,
total: total,
promotion_code: checkoutData.appliedPromo ? checkoutData.appliedPromo.promo_code : null,
discount_amount: discount,
status: 'pending',
payment_method: 'stripe',
payment_status: 'paid',
payment_id: paymentId
};
fetch(BASE_URL + '/api/functions/createEmbedOrder', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(orderData)
})
.then(function(res) { return res.json(); })
.then(function(data) {
stripePaymentProcessing = false;
if (data.success) {
cart = [];
saveCart();
checkoutData = { name: '', phone: '', email: '', orderType: 'pickup', address: '', notes: '', scheduledTime: '', tipType: 'percentage', tipPercentage: 0, tipFlatAmount: 0, paymentMethod: '', partySize: null };
cardElement = null;
stripeElements = null;
goToConfirmation(data.orderId);
} else {
stripeError = 'Order creation failed: ' + (data.error || 'Unknown error');
renderMenu();
}
})
.catch(function(err) {
stripePaymentProcessing = false;
stripeError = 'Order creation failed';
renderMenu();
});
}
async function submitOrder() {
console.log('[Embedder submitOrder] Called - isSubmitting:', isSubmitting);
console.log('[Embedder submitOrder] checkoutData:', JSON.stringify(checkoutData));
if (isSubmitting) {
console.log('[Embedder submitOrder] Already submitting, returning');
return;
}
if (!checkoutData.name || !checkoutData.phone || !checkoutData.email) {
alert('Please fill in your name, phone number, and email address');
checkoutData.openSection = 'contact';
renderMenu();
return;
}
if (checkoutData.orderType === 'delivery' && !checkoutData.address) {
alert('Please enter your delivery address');
checkoutData.openSection = 'address';
renderMenu();
return;
}
if (checkoutData.orderType === 'delivery' && (!deliveryFeeData || !deliveryFeeData.success)) {
alert('❌ ' + (deliveryFeeError || 'Please verify your delivery address is within our delivery range'));
checkoutData.openSection = 'address';
renderMenu();
return;
}
// Validate timing for delivery and pickup
if (checkoutData.orderType !== 'dine_in') {
if (!checkoutData.timingType) {
alert('Please select when you want your order (ASAP or Schedule)');
checkoutData.openSection = 'time';
renderMenu();
return;
}
if (checkoutData.timingType === 'scheduled' && (!checkoutData.scheduledTime || checkoutData.scheduledTime.indexOf(':') === -1)) {
alert('Please select a date and time for your scheduled order');
checkoutData.openSection = 'time';
renderMenu();
return;
}
}
if (checkoutData.orderType === 'dine_in') {
if (!checkoutData.partySize || checkoutData.partySize < 1) {
alert('Please enter the party size');
checkoutData.openSection = 'dine_in_party';
renderMenu();
return;
}
if (!checkoutData.scheduledTime || checkoutData.scheduledTime.indexOf(':') === -1) {
alert('Please select a date and time for your reservation');
checkoutData.openSection = 'dine_in_time';
renderMenu();
return;
}
}
// Check minimum order based on order type
var subtotal = getCartTotal();
var minimumOrder = checkoutData.orderType === 'delivery'
? (restaurant.delivery_minimum_order || 0)
: checkoutData.orderType === 'pickup'
? (restaurant.pickup_minimum_order || 0)
: (restaurant.dine_in_minimum_order || 0);
if (minimumOrder > 0 && subtotal < minimumOrder) {
var currency = getCurrencySymbol(restaurant.currency);
alert('Minimum order of ' + currency + minimumOrder.toFixed(2) + ' required for ' + checkoutData.orderType.replace(/_/g, ' '));
return;
}
console.log('[Embedder submitOrder] All validations passed, setting isSubmitting = true');
isSubmitting = true;
renderMenu();
try {
// For dine-in: Reservation creation is now handled by createEmbedOrder to prevent duplicates
var subtotal = getCartTotal();
var deliveryFee = getDeliveryFee();
var tipAmount = getTipAmount();
var otherFeesTotal = calculateOtherFees();
var discount = calculateDiscount();
var vatCalc = calculateVAT();
var total = restaurant.vat_settings && restaurant.vat_settings.price_inclusive
? subtotal + deliveryFee + tipAmount + otherFeesTotal - discount
: subtotal + deliveryFee + tipAmount + otherFeesTotal - discount + vatCalc.vat_amount;
// Use logged-in user's email if available for order matching
var submitOrderEmail = (currentUser && currentUser.email) ? currentUser.email : checkoutData.email;
var orderData = {
restaurant_id: RESTAURANT_ID,
restaurant_location: restaurant.location || null,
customer_name: checkoutData.name,
customer_phone: checkoutData.phone,
customer_email: submitOrderEmail,
order_type: checkoutData.orderType,
reservation_details: (checkoutData.orderType === 'dine_in') ? {
party_size: checkoutData.partySize,
reservation_date: checkoutData.scheduledTime ? new Date(checkoutData.scheduledTime).toISOString() : new Date().toISOString(),
special_requests: checkoutData.notes || ''
} : null,
delivery_address: checkoutData.orderType === 'delivery' ? checkoutData.address : '',
delivery_location: checkoutData.deliveryLocation || null,
special_notes: checkoutData.notes,
scheduled_time: checkoutData.scheduledTime || null,
items: cart.map(function(item) {
return {
item_id: item.id,
item_name: item.name,
quantity: item.quantity,
price: item.price,
special_instructions: item.special_instructions || '',
selected_options: formatOptionsForOrder(item)
};
}),
subtotal: subtotal,
delivery_fee: deliveryFee,
other_fees: otherFeesTotal,
other_fees_breakdown: getFeeBreakdown().map(function(f) { return { name: f.name, amount: f.amount }; }),
tip_amount: tipAmount,
vat_amount: vatCalc.vat_amount,
vat_rate: vatCalc.vat_rate,
vat_on_products: vatCalc.vat_on_products,
vat_on_delivery: vatCalc.vat_on_delivery,
vat_on_fees: vatCalc.vat_on_fees,
vat_on_tips: vatCalc.vat_on_tips,
total: total,
promotion_code: checkoutData.appliedPromo ? checkoutData.appliedPromo.promo_code : null,
discount_amount: discount,
status: 'pending',
payment_method: checkoutData.paymentMethod || 'cash_on_delivery',
payment_status: 'pending'
};
fetch(BASE_URL + '/api/functions/createEmbedOrder', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(orderData)
})
.then(function(res) { return res.json(); })
.then(function(data) {
isSubmitting = false;
if (data.success) {
cart = [];
saveCart();
checkoutData = { name: '', phone: '', email: '', orderType: 'pickup', address: '', notes: '', scheduledTime: '', tipType: 'percentage', tipPercentage: 0, tipFlatAmount: 0, paymentMethod: '', partySize: null };
goToConfirmation(data.orderId);
} else {
alert('Failed to place order: ' + (data.error || 'Unknown error'));
renderMenu();
}
})
.catch(function(err) {
isSubmitting = false;
alert('Failed to place order. Please try again.');
renderMenu();
});
} catch (err) {
isSubmitting = false;
console.error('Order submission error:', err);
alert(err.message || 'An unexpected error occurred while placing your order.');
renderMenu();
}
}
// Load Stripe if available
if (STRIPE_PUBLISHABLE_KEY) {
loadStripe();
}
function initGoogleServices() {
if (window.google && window.google.maps && window.google.maps.places && !autocompleteService) {
autocompleteService = new window.google.maps.places.AutocompleteService();
var dummyDiv = document.createElement('div');
placesService = new window.google.maps.places.PlacesService(dummyDiv);
autocompleteSessionToken = new window.google.maps.places.AutocompleteSessionToken();
console.log('Google Maps initialized in embedder');
}
}
function searchAddresses(query) {
if (!query || query.length < 2) {
addressSuggestions = [];
showAddressSuggestions = false;
var suggestionsEl = currentModalContent ? currentModalContent.querySelector('.address-autocomplete-suggestions') : null;
if (suggestionsEl) suggestionsEl.remove();
return;
}
if (!autocompleteService) {
console.log('Autocomplete service not ready, initializing...');
if (window.google && window.google.maps && window.google.maps.places) {
initGoogleServices();
}
return;
}
var request = {
input: query,
sessionToken: autocompleteSessionToken,
types: ['geocode']
};
if (restaurant.location) {
request.location = new window.google.maps.LatLng(restaurant.location.lat, restaurant.location.lng);
request.radius = 50000;
}
autocompleteService.getPlacePredictions(request, function(predictions, status) {
if (status === window.google.maps.places.PlacesServiceStatus.OK && predictions) {
addressSuggestions = predictions.map(function(p) {
return {
placeId: p.place_id,
description: p.description,
mainText: p.structured_formatting ? p.structured_formatting.main_text : p.description,
secondaryText: p.structured_formatting ? p.structured_formatting.secondary_text : ''
};
});
showAddressSuggestions = true;
updateAddressSuggestions();
} else {
addressSuggestions = [];
showAddressSuggestions = false;
var suggestionsEl = currentModalContent ? currentModalContent.querySelector('.address-autocomplete-suggestions') : null;
if (suggestionsEl) suggestionsEl.remove();
}
});
}
function updateAddressSuggestions() {
if (!currentModalContent) return;
var addressWrapper = currentModalContent.querySelector('#address-field-wrapper');
if (!addressWrapper) return;
var existing = addressWrapper.querySelector('.address-autocomplete-suggestions');
if (existing) existing.remove();
if (!showAddressSuggestions || addressSuggestions.length === 0) return;
var suggestionsHtml = '';
addressSuggestions.forEach(function(s, idx) {
suggestionsHtml += '
';
suggestionsHtml += '
' + s.mainText + '
';
if (s.secondaryText) suggestionsHtml += '
' + s.secondaryText + '
';
suggestionsHtml += '
';
});
suggestionsHtml += '
';
addressWrapper.insertAdjacentHTML('beforeend', suggestionsHtml);
var suggestionItems = addressWrapper.querySelectorAll('.address-suggestion-item');
suggestionItems.forEach(function(item) {
item.onclick = function(e) {
e.preventDefault();
var idx = parseInt(item.dataset.suggestionIdx);
if (addressSuggestions[idx]) {
selectAddress(addressSuggestions[idx]);
}
};
});
}
async function calculateDeliveryFee() {
if (!checkoutData.deliveryLocation || !restaurant.location) {
calculatedDeliveryFee = 0;
deliveryFeeData = null;
deliveryFeeError = '';
deliveryFeeLoading = false;
updateDeliveryFeeUI();
return;
}
deliveryFeeLoading = true;
deliveryFeeError = '';
updateDeliveryFeeUI();
try {
var response = await fetch(BASE_URL + '/api/functions/calculateDeliveryFee', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
restaurant_location: restaurant.location,
delivery_location: checkoutData.deliveryLocation,
restaurant_id: RESTAURANT_ID
})
});
var data = await response.json();
deliveryFeeLoading = false;
if (response.ok && data.success) {
deliveryFeeData = data;
calculatedDeliveryFee = data.delivery_fee;
deliveryFeeError = '';
} else {
deliveryFeeData = { success: false, error: data.error || 'Error calculating fee' };
calculatedDeliveryFee = 0;
deliveryFeeError = data.error || 'Address outside delivery range';
// Immediately alert and keep address section open
setTimeout(function() {
alert('❌ ' + (deliveryFeeError || 'This address is outside our delivery range. Please choose a different address.'));
}, 100);
}
} catch (error) {
deliveryFeeLoading = false;
deliveryFeeData = { success: false, error: 'Could not verify delivery zone' };
calculatedDeliveryFee = 0;
deliveryFeeError = 'Could not calculate delivery fee';
// Immediately alert and keep address section open
setTimeout(function() {
alert('❌ Could not calculate delivery fee. Please verify your address.');
}, 100);
} finally {
deliveryFeeLoading = false;
}
updateDeliveryFeeUI();
// Don't call renderMenu - just update UI elements to avoid closing the section
}
function updateDeliveryFeeUI() {
if (!currentModalContent) return;
var loadingEl = currentModalContent.querySelector('#delivery-fee-loading');
var successEl = currentModalContent.querySelector('#delivery-fee-success');
var errorEl = currentModalContent.querySelector('#delivery-fee-error');
if (loadingEl) {
loadingEl.style.display = deliveryFeeLoading ? 'flex' : 'none';
}
if (errorEl) {
errorEl.style.display = deliveryFeeError ? 'block' : 'none';
if (deliveryFeeError) {
errorEl.innerHTML = '⚠️ Delivery Not Available ' + deliveryFeeError + '
';
}
}
if (successEl && deliveryFeeData && deliveryFeeData.success) {
successEl.style.display = 'block';
var currency = getCurrencySymbol(restaurant.currency);
var typeName = deliveryFeeData.method === 'zone_based'
? deliveryFeeData.zone_matched
: deliveryFeeData.fee_breakdown && deliveryFeeData.fee_breakdown.tier_matched
? 'Tiered'
: 'Linear';
var distanceText = deliveryFeeData.distance && deliveryFeeData.distance.text ? deliveryFeeData.distance.text : 'N/A';
var travelTimeText = deliveryFeeData.duration && deliveryFeeData.duration.text ? deliveryFeeData.duration.text : 'N/A';
successEl.innerHTML = '✅ ' + typeName + ' • Distance: ' + distanceText + ' • Est. Travel Time: ' + travelTimeText + ' • Total Delivery Fee: ' + currency + deliveryFeeData.delivery_fee.toFixed(2) + '
';
} else if (successEl) {
successEl.style.display = 'none';
}
}
function selectAddress(suggestion) {
if (!placesService) return;
selectedAddressFromList = true;
placesService.getDetails({
placeId: suggestion.placeId,
fields: ['formatted_address', 'geometry']
}, function(place, status) {
if (status === window.google.maps.places.PlacesServiceStatus.OK && place) {
checkoutData.address = place.formatted_address || suggestion.description;
if (place.geometry && place.geometry.location) {
checkoutData.deliveryLocation = {
lat: place.geometry.location.lat(),
lng: place.geometry.location.lng()
};
calculateDeliveryFee();
}
autocompleteSessionToken = new window.google.maps.places.AutocompleteSessionToken();
} else {
checkoutData.address = suggestion.description;
}
addressSuggestions = [];
showAddressSuggestions = false;
var addressInput = currentModalContent.querySelector('#checkout-address');
if (addressInput) addressInput.value = checkoutData.address;
var suggestionsEl = currentModalContent.querySelector('.address-autocomplete-suggestions');
if (suggestionsEl) suggestionsEl.remove();
});
}
function geocodeManualAddress(address) {
if (!window.google || !window.google.maps) {
console.log('Google Maps not loaded for geocoding');
return;
}
var geocoder = new window.google.maps.Geocoder();
geocoder.geocode({ address: address }, function(results, status) {
if (status === 'OK' && results[0]) {
var location = results[0].geometry.location;
checkoutData.deliveryLocation = {
lat: location.lat(),
lng: location.lng()
};
checkoutData.address = results[0].formatted_address;
var addressInput = currentModalContent ? currentModalContent.querySelector('#checkout-address') : null;
if (addressInput) addressInput.value = checkoutData.address;
calculateDeliveryFee();
} else {
console.log('Geocoding failed:', status);
deliveryFeeError = 'Could not verify this address. Please select from suggestions.';
deliveryFeeData = { success: false, error: deliveryFeeError };
updateDeliveryFeeUI();
}
});
}
function loadGoogleMaps() {
if (googleMapsLoaded || window.google) {
if (window.google && window.google.maps && window.google.maps.places) {
initGoogleServices();
}
return;
}
if (document.querySelector('script[src*="maps.googleapis.com"]')) {
var checkInterval = setInterval(function() {
if (window.google && window.google.maps && window.google.maps.places) {
clearInterval(checkInterval);
googleMapsLoaded = true;
initGoogleServices();
}
}, 100);
setTimeout(function() { clearInterval(checkInterval); }, 10000);
return;
}
if (!GOOGLE_MAPS_API_KEY) {
console.log('No Google Maps API key');
return;
}
var script = document.createElement('script');
script.src = 'https://maps.googleapis.com/maps/api/js?key=' + GOOGLE_MAPS_API_KEY + '&libraries=places';
script.async = true;
script.defer = true;
script.onload = function() {
googleMapsLoaded = true;
initGoogleServices();
};
document.head.appendChild(script);
}
if (GOOGLE_MAPS_API_KEY) {
loadGoogleMaps();
}
function handleLocateMe() {
if (!navigator.geolocation) {
alert('Geolocation is not supported by your browser');
return;
}
gettingLocation = true;
var locateBtn = currentModalContent.querySelector('#locate-me-btn');
if (locateBtn) {
locateBtn.innerHTML = ' Locating...';
locateBtn.disabled = true;
}
navigator.geolocation.getCurrentPosition(
function(position) {
var lat = position.coords.latitude;
var lng = position.coords.longitude;
checkoutData.deliveryLocation = { lat: lat, lng: lng };
if (window.google && window.google.maps) {
var geocoder = new window.google.maps.Geocoder();
geocoder.geocode({ location: { lat: lat, lng: lng } }, function(results, status) {
gettingLocation = false;
var locateBtn = currentModalContent.querySelector('#locate-me-btn');
if (locateBtn) {
locateBtn.innerHTML = 'Locate ';
locateBtn.disabled = false;
}
if (status === 'OK' && results[0]) {
checkoutData.address = results[0].formatted_address;
var addressInput = currentModalContent.querySelector('#checkout-address');
if (addressInput) addressInput.value = checkoutData.address;
calculateDeliveryFee();
} else {
checkoutData.address = lat.toFixed(6) + ', ' + lng.toFixed(6);
var addressInput = currentModalContent.querySelector('#checkout-address');
if (addressInput) addressInput.value = checkoutData.address;
}
});
} else {
gettingLocation = false;
var locateBtn = currentModalContent.querySelector('#locate-me-btn');
if (locateBtn) {
locateBtn.innerHTML = 'Locate ';
locateBtn.disabled = false;
}
checkoutData.address = lat.toFixed(6) + ', ' + lng.toFixed(6);
var addressInput = currentModalContent.querySelector('#checkout-address');
if (addressInput) addressInput.value = checkoutData.address;
}
},
function(error) {
gettingLocation = false;
var locateBtn = currentModalContent.querySelector('#locate-me-btn');
if (locateBtn) {
locateBtn.innerHTML = 'Locate ';
locateBtn.disabled = false;
}
var errorMsg = 'Unable to get your location';
if (error.code === 1) {
errorMsg = 'Location permission denied. Please allow location access in your browser settings.';
} else if (error.code === 2) {
errorMsg = 'Location unavailable. Please check your device\'s location settings.';
} else if (error.code === 3) {
errorMsg = 'Location request timed out. Please try again.';
}
alert(errorMsg);
},
{ enableHighAccuracy: false, timeout: 15000, maximumAge: 60000 }
);
}
function geocodeManualAddress(address) {
if (!window.google || !window.google.maps) {
console.log('Google Maps not loaded for geocoding');
return;
}
var geocoder = new window.google.maps.Geocoder();
geocoder.geocode({ address: address }, function(results, status) {
if (status === 'OK' && results[0]) {
var location = results[0].geometry.location;
checkoutData.deliveryLocation = {
lat: location.lat(),
lng: location.lng()
};
checkoutData.address = results[0].formatted_address;
var addressInput = currentModalContent ? currentModalContent.querySelector('#checkout-address') : null;
if (addressInput) addressInput.value = checkoutData.address;
calculateDeliveryFee();
} else {
console.log('Geocoding failed:', status);
deliveryFeeError = 'Could not verify this address. Please select from suggestions.';
deliveryFeeData = { success: false, error: deliveryFeeError };
updateDeliveryFeeUI();
}
});
}
if (GOOGLE_MAPS_API_KEY) {
loadGoogleMaps();
}
function renderMenuItemsOnly(items, container) {
if (!container) return;
var currency = getCurrencySymbol(restaurant.currency);
var isDeliveryEnabled = restaurant.delivery_enabled !== false;
var isPickupEnabled = restaurant.pickup_enabled !== false;
var isDineInEnabled = restaurant.dine_in_enabled === true;
var isAnyServiceEnabled = isDeliveryEnabled || isPickupEnabled || isDineInEnabled;
var html = '';
items.forEach(function(item) {
var isTemporarilyUnavailable = item.not_available_until && new Date(item.not_available_until) > new Date();
var itemCountInCart = cart.filter(function(c) { return c.id === item.id; }).reduce(function(sum, c) { return sum + c.quantity; }, 0);
var hasOptions = item.options && item.options.length > 0;
html += '';
});
if (items.length === 0) {
html = 'No items found
';
}
container.innerHTML = html;
// Re-attach add button listeners
var addBtns = container.querySelectorAll('.add-btn');
addBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var itemId = btn.dataset.itemId;
var item = menuItems.find(function(i) { return i.id === itemId; });
if (item) {
if (item.options && item.options.length > 0) {
handleItemClick(item);
} else {
var notesInput = container.querySelector('.menu-item-notes[data-item-id="' + itemId + '"]');
var notes = notesInput ? notesInput.value : '';
var qty = itemQuantities[itemId] || 1;
for (var i = 0; i < qty; i++) {
addToCart(item, null, notes);
}
if (notesInput) notesInput.value = '';
itemQuantities[itemId] = 1;
}
}
};
});
// Attach quantity button listeners
var qtyDecreaseBtns = container.querySelectorAll('.qty-decrease-btn');
qtyDecreaseBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var itemId = btn.dataset.itemId;
var current = itemQuantities[itemId] || 1;
if (current > 1) {
itemQuantities[itemId] = current - 1;
renderMenuItemsOnly(getFilteredItems(), container);
}
};
});
var qtyIncreaseBtns = container.querySelectorAll('.qty-increase-btn');
qtyIncreaseBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var itemId = btn.dataset.itemId;
var current = itemQuantities[itemId] || 1;
itemQuantities[itemId] = current + 1;
renderMenuItemsOnly(getFilteredItems(), container);
};
});
}
function initializeInlineMenu(container) {
if (!container) return;
if (FETCH_ERROR) {
container.innerHTML = 'Failed to load menu: ' + FETCH_ERROR + '
';
return;
}
if (!restaurant) {
container.innerHTML = 'Restaurant not found
';
return;
}
if (!menuItems.length) {
container.innerHTML = 'No menu items available
';
return;
}
// Re-merge cart duplicates each time modal opens
var mergedCart = [];
cart.forEach(function(item) {
var itemOptions = item.selected_options;
var itemHasOptions = itemOptions && Object.keys(itemOptions).length > 0;
var optionsKey = itemHasOptions ? JSON.stringify(itemOptions) : '';
var existingIndex = -1;
for (var i = 0; i < mergedCart.length; i++) {
var mergedOptions = mergedCart[i].selected_options;
var mergedHasOptions = mergedOptions && Object.keys(mergedOptions).length > 0;
var mergedOptionsKey = mergedHasOptions ? JSON.stringify(mergedOptions) : '';
if (mergedCart[i].id === item.id && mergedOptionsKey === optionsKey) {
existingIndex = i;
break;
}
}
if (existingIndex !== -1) {
mergedCart[existingIndex].quantity += item.quantity;
} else {
mergedCart.push(item);
}
});
cart = mergedCart;
saveCart();
currentModalContent = container;
categories = getUniqueCategories();
renderMenu();
}
function renderMenu() {
if (!currentModalContent) return;
var currency = getCurrencySymbol(restaurant.currency);
var filteredItems = getFilteredItems();
var cartTotal = getCartTotal();
var cartCount = getCartCount();
var isDeliveryEnabled = restaurant.delivery_enabled !== false;
var isPickupEnabled = restaurant.pickup_enabled !== false;
var isDineInEnabled = restaurant.dine_in_enabled === true;
var isAnyServiceEnabled = isDeliveryEnabled || isPickupEnabled || isDineInEnabled;
// Build the menu HTML
var html = '';
// Header for inline mode (same as modal)
if (DISPLAY_MODE === 'inline') {
var headerTextColor = restaurant.theme_header_text_color || '#1E293B';
html += '
';
html += '
' + (restaurant.name || 'Order Online') + ' ';
html += '
';
html += '
My Orders ';
html += '
';
}
html += '
';
// Service unavailable banner
if (!isAnyServiceEnabled) {
html += '
⚠️ This restaurant is currently not accepting orders.
';
}
// Estimated times banner
var timeBannerParts = [];
if (isPickupEnabled && restaurant.estimated_pickup_time) {
timeBannerParts.push('🛍️ Pickup: ' + restaurant.estimated_pickup_time + ' min');
}
if (timeBannerParts.length > 0) {
html += '
' + timeBannerParts.join(' • ') + '
';
}
// Categories - sticky horizontal scroll like mobile view
var buttonColor = restaurant.theme_button_color || '#f97316';
html += '
';
html += '
';
html += '
';
html += '
';
html += '
';
categories.forEach(function(cat) {
var isActive = selectedCategory === cat;
html += '
' + formatCategory(cat) + ' ';
});
html += '
';
html += '
';
// Menu items grid
html += '
';
filteredItems.forEach(function(item) {
var isTemporarilyUnavailable = item.not_available_until && new Date(item.not_available_until) > new Date();
var itemCountInCart = cart.filter(function(c) { return c.id === item.id; }).reduce(function(sum, c) { return sum + c.quantity; }, 0);
var hasOptions = item.options && item.options.length > 0;
html += '';
});
html += '
';
// Floating cart button with hover preview
if (cartCount > 0 && !cartOpen && !checkoutOpen && !confirmationOpen) {
var lastItem = cart[cart.length - 1];
// Get total quantity of last item type in cart
var lastItemTotalQty = cart.filter(function(c) { return c.id === lastItem.id; }).reduce(function(sum, c) { return sum + c.quantity; }, 0);
var lastItemName = lastItem.name.length > 20 ? lastItem.name.substring(0, 20) + '...' : lastItem.name;
html += '
';
// Expanded cart preview (hidden by default, shows on hover) - matches cart page design
html += '
';
// Scrollable items area
html += '
';
html += '
🛒 Cart (' + cartCount + ')
';
cart.slice().reverse().forEach(function(item, idx) {
var isNewest = idx === 0;
html += '
';
if (item.image_url) {
html += '
';
}
html += '
';
html += '
' + item.quantity + 'x ' + item.name + '
';
if (item.selected_options && Object.keys(item.selected_options).length > 0) {
var optText = [];
Object.keys(item.selected_options).forEach(function(optName) {
var optVal = item.selected_options[optName];
if (Array.isArray(optVal) && optVal.length > 0) {
optText.push(optVal.join(', '));
} else if (optVal && typeof optVal === 'string') {
optText.push(optVal);
}
});
if (optText.length > 0) {
html += '
' + optText.join(' • ') + '
';
}
}
if (item.special_instructions) {
html += '
📝 ' + item.special_instructions + '
';
}
html += '
' + currency + item.price.toFixed(2) + ' × ' + item.quantity + ' ' + currency + (item.price * item.quantity).toFixed(2) + '
';
html += '
';
});
html += '
Total ' + currency + cartTotal.toFixed(2) + '
';
html += '
';
// Sticky action buttons at bottom
html += '
';
html += 'Edit Cart ';
html += 'Checkout ';
html += '
';
html += '
';
// Main cart button
html += '
';
html += '
✓ ' + lastItemTotalQty + 'x ' + lastItemName + '
';
html += '
🛒 ' + cartCount + ' item' + (cartCount > 1 ? 's' : '') + ' • ' + currency + cartTotal.toFixed(2) + '
';
html += '
';
html += '
';
}
html += '
';
// Account/Tracking overlay
if (accountOpen) {
html = renderAccountPage(currency);
}
// Orders view for logged in users
if (trackingOpen && currentUser) {
// Will be handled by renderOrdersView
}
// Cart overlay
if (cartOpen) {
html = renderCartPage(currency);
}
// Checkout overlay
if (checkoutOpen) {
html = renderCheckoutPage(currency);
}
// Confirmation overlay
if (confirmationOpen) {
if (currentOrderData) {
html = renderConfirmationPage(currency);
} else {
// Show loading state while fetching order data
html = '
' +
'
' +
'
⏳
' +
'
Loading Order... ' +
'
Please wait while we fetch your order details.
' +
'
' +
'
';
}
}
// Options modal
if (optionsModalOpen && selectedItemForOptions) {
html += renderOptionsModal(currency);
}
currentModalContent.innerHTML = html;
attachEventListeners();
// Initialize map if confirmation page with tracking data
if (confirmationOpen && currentOrderData) {
console.log('[Embedder Map] confirmationOpen=true, checking for map data...');
console.log('[Embedder Map] Status:', currentOrderData.status);
console.log('[Embedder Map] Has driver_location:', !!currentOrderData.driver_location);
console.log('[Embedder Map] Has delivery_location:', !!currentOrderData.delivery_location);
console.log('[Embedder Map] Has restaurant_location:', !!currentOrderData.restaurant_location);
if (currentOrderData.status === 'out_for_delivery' &&
currentOrderData.driver_location &&
currentOrderData.delivery_location &&
currentOrderData.restaurant_location) {
console.log('[Embedder Map] All conditions met, loading map...');
loadLeafletAndInitMap(currentOrderData);
} else {
console.log('[Embedder Map] Conditions not met, skipping map');
}
}
// Update delivery fee UI after render
if (checkoutOpen) {
updateDeliveryFeeUI();
// Auto-focus address field when delivery type is selected and address section is active
if (checkoutData.orderType === 'delivery' && checkoutData.openSection === 'address') {
setTimeout(function() {
var addressField = currentModalContent.querySelector('#checkout-address');
if (addressField) {
addressField.focus();
addressField.classList.add('base44-pulse-field');
}
}, 300);
}
}
// Mount Stripe if needed
if (checkoutOpen && checkoutData.paymentMethod === 'stripe' && STRIPE_PUBLISHABLE_KEY && stripeInstance) {
setTimeout(function() {
if (!cardElement) mountStripeCard();
}, 100);
}
}
function renderCartPage(currency) {
var cartTotal = getCartTotal();
var cartCount = getCartCount();
var html = '
';
// Header with back button
html += '
';
html += '
← Back to Menu ';
html += '
🛒 Cart (' + cartCount + ') ';
html += '
';
html += '
';
if (cart.length === 0) {
html += '
';
html += '
🛒
';
html += '
Your cart is empty ';
html += '
Add items to get started!
';
html += '
';
} else {
// Grid layout for larger screens, single column for mobile
html += '
';
cart.forEach(function(item, index) {
html += '
';
// Desktop: Large image at top, Mobile: Small image inline
if (item.image_url) {
html += '
';
}
html += '
';
// Item header row with delete button (mobile shows small image here)
html += '
';
if (item.image_url) {
html += '
';
}
html += '
';
html += '
';
html += '
' + item.name + ' ';
html += '× ';
html += '';
// Options in compact form
if (item.selected_options && Object.keys(item.selected_options).length > 0) {
var optText = [];
Object.keys(item.selected_options).forEach(function(optName) {
var optVal = item.selected_options[optName];
if (Array.isArray(optVal) && optVal.length > 0) {
optText.push(optVal.join(', '));
} else if (optVal && typeof optVal === 'string') {
optText.push(optVal);
}
});
if (optText.length > 0) {
html += '
' + optText.join(' • ') + '
';
}
}
html += '
';
html += '
';
// Special instructions - styled with pen icon like embedderInline
html += '
';
if (item.special_instructions) {
html += '
' + item.special_instructions + '
';
html += '
✏️ ';
} else {
html += '
✏️ Add note ';
}
html += '
';
// Price row below note: unit price - qty - total
html += '
';
html += '
' + currency + item.price.toFixed(2) + ' ';
html += '
';
html += '− ';
html += '' + item.quantity + ' ';
html += '+ ';
html += '
';
html += '
' + currency + (item.price * item.quantity).toFixed(2) + ' ';
html += '
';
html += '
'; // close padding div
html += '
'; // close card
});
html += '
';
// Add responsive styles for cart images
html += '';
}
// Floating checkout button - compact
if (cart.length > 0) {
html += '
';
html += '
';
html += '
';
html += '
Subtotal
';
html += '
' + currency + cartTotal.toFixed(2) + '
';
html += '
';
html += '
Checkout → ';
html += '
';
}
html += '
';
return html;
}
function renderCheckoutPage(currency) {
var cartTotal = getCartTotal();
var tipAmount = getTipAmount();
var deliveryFee = getDeliveryFee();
var discount = calculateDiscount();
var feeBreakdown = getFeeBreakdown();
var otherFeesTotal = feeBreakdown.reduce(function(sum, fee) { return sum + fee.amount; }, 0);
var orderTotal = getOrderTotal();
var isDeliveryEnabled = restaurant.delivery_enabled !== false;
var isPickupEnabled = restaurant.pickup_enabled !== false;
var isDineInEnabled = restaurant.dine_in_enabled === true;
var paymentMethods = getAvailablePaymentMethods();
// Determine completion status
var isOrderTypeComplete = !!checkoutData.orderType;
var isTimingComplete = false;
if (checkoutData.orderType === 'dine_in') {
var dateTimeParts = checkoutData.scheduledTime ? checkoutData.scheduledTime.split('T') : [];
var hasDate = dateTimeParts[0] && dateTimeParts[0].trim().length > 0;
var hasTime = dateTimeParts[1] && dateTimeParts[1].trim().length > 0 && dateTimeParts[1].indexOf(':') > -1;
isTimingComplete = !!(checkoutData.partySize && hasDate && hasTime);
} else {
if (checkoutData.timingType === 'asap') {
isTimingComplete = true;
} else if (checkoutData.timingType === 'scheduled') {
isTimingComplete = !!(checkoutData.scheduledTime && checkoutData.scheduledTime.indexOf(':') > -1);
}
}
var isAddressComplete = true;
if (checkoutData.orderType === 'delivery') {
isAddressComplete = !!(checkoutData.address && checkoutData.address.length > 3);
}
var isContactComplete = !!(checkoutData.name && checkoutData.phone && checkoutData.email);
// Determine active section (accordion logic)
var nextIncomplete = 'type';
if (isOrderTypeComplete) {
if (checkoutData.orderType === 'delivery') {
// Delivery Flow: Type -> Address -> Time -> Contact
nextIncomplete = 'address';
if (isAddressComplete) nextIncomplete = 'time';
if (isAddressComplete && isTimingComplete) nextIncomplete = 'contact';
} else if (checkoutData.orderType === 'dine_in') {
// Dine-in Flow: Type -> Booking (combined) -> Contact
nextIncomplete = 'booking';
if (isTimingComplete && checkoutData.partySize) nextIncomplete = 'contact';
} else {
// Pickup Flow: Type -> Time -> Contact
nextIncomplete = 'time';
if (isTimingComplete) nextIncomplete = 'contact';
}
}
if (isOrderTypeComplete && isTimingComplete && isAddressComplete && isContactComplete) nextIncomplete = 'payment';
// CRITICAL: Define activeSection here for use throughout renderCheckoutPage
// ALWAYS keep payment section open
var activeSection = checkoutData.openSection || nextIncomplete;
var isPaymentSectionActive = true; // Payment section is ALWAYS open
// Helper to render accordion section
function renderSection(id, title, icon, isComplete, contentHtml, badgeText) {
// Payment section is ALWAYS open
var isActive = (id === 'payment') ? true : (activeSection === id);
var isDisabled = false;
// Determine if disabled (must complete previous steps)
if (id === 'time') {
if (!isOrderTypeComplete) isDisabled = true;
if (checkoutData.orderType === 'delivery' && !isAddressComplete) isDisabled = true;
}
if (id === 'address' && !isOrderTypeComplete) isDisabled = true;
if (id === 'contact') {
if (!isOrderTypeComplete || !isTimingComplete) isDisabled = true;
if (checkoutData.orderType === 'delivery' && !isAddressComplete) isDisabled = true;
}
if (['payment','notes','promo','tip'].includes(id)) {
if (!isOrderTypeComplete || !isTimingComplete || !isContactComplete) isDisabled = true;
if (checkoutData.orderType === 'delivery' && !isAddressComplete) isDisabled = true;
}
// Special case: Address only shows for delivery
if (id === 'address' && checkoutData.orderType !== 'delivery') return '';
var headerColor = isActive ? '#1e293b' : (isComplete ? '#059669' : '#64748b');
var iconColor = isActive ? (restaurant.theme_button_color || '#f97316') : (isComplete ? '#059669' : '#94a3b8');
var isRequired = ['contact', 'dine_in_contact', 'payment'].includes(id);
var borderColor = isActive ? (restaurant.theme_button_color || '#f97316') : (!isComplete && !isDisabled && isRequired ? '#ef4444' : '#e2e8f0');
var pulseClass = (!isComplete && !isDisabled && isRequired && !isActive) ? 'base44-pulse-field' : '';
var sectionHtml = '
';
// Header
sectionHtml += '';
// Content
if (isActive) {
sectionHtml += '
' + contentHtml + '
';
}
sectionHtml += '
';
return sectionHtml;
}
var html = '
';
// Back buttons
html += '
';
html += '← Menu ';
html += '🛒 Cart ';
html += '
';
html += '
Checkout ';
html += '';
html += '
';
html += '
';
// --- SECTION 1: Order Type ---
var typeHtml = '
';
var typePulseClass = !checkoutData.orderType ? ' base44-pulse-field' : '';
if (isPickupEnabled) typeHtml += '🛍️ Pickup ';
if (isDeliveryEnabled) typeHtml += '🚗 Delivery ';
if (isDineInEnabled) typeHtml += '🍽️ Dine In ';
typeHtml += '
';
html += renderSection('type', 'Order Type', '🛍️', isOrderTypeComplete, typeHtml, checkoutData.orderType ? (checkoutData.orderType === 'dine_in' ? 'Dine In' : checkoutData.orderType.charAt(0).toUpperCase() + checkoutData.orderType.slice(1)) : '');
// TIMING CONTENT
var timeHtml = '';
var timeLabel = checkoutData.orderType === 'dine_in' ? 'Order Timing & Party Size' : 'Order Timing';
var timeBadge = 'Required';
if (checkoutData.orderType === 'dine_in') {
var pickerBg = restaurant.theme_background_color || '#f8fafc';
var dateNotSelected = !checkoutData.scheduledTime || !checkoutData.scheduledTime.split('T')[0];
var timeNotSelected = !checkoutData.scheduledTime || !checkoutData.scheduledTime.split('T')[1];
timeHtml += '';
timeHtml += '
';
timeHtml += '
';
timeHtml += '
';
timeHtml += '
';
if (checkoutData.scheduledTime && checkoutData.scheduledTime.indexOf(':') > -1) {
var sched = checkoutData.scheduledTime.split('T');
var dateParts = sched[0].split('-');
var timeH = parseInt(sched[1].split(':')[0]);
var timeMin = sched[1].split(':')[1];
var h12 = (timeH % 12 === 0) ? 12 : (timeH % 12);
var ampm = timeH < 12 ? 'AM' : 'PM';
timeBadge = dateParts[2] + '/' + dateParts[1] + '/' + dateParts[0] + ' ' + String(h12).padStart(2, '0') + ':' + timeMin + ' ' + ampm;
} else {
timeBadge = 'Select Date & Time';
}
} else if (checkoutData.orderType !== 'dine_in') {
var isAsap = checkoutData.timingType === 'asap';
var isScheduled = checkoutData.timingType === 'scheduled';
var timingPulseClass = !checkoutData.timingType ? ' base44-pulse-field' : '';
timeHtml += '';
timeHtml += '
';
timeHtml += '
⚡
ASAP
' + (isAsap ? '' : 'Select for immediate
') + '';
timeHtml += '
📅
Schedule
' + (isScheduled ? '' : 'Pick date & time
') + '';
timeHtml += '
';
if (isScheduled) {
var pickerBg = restaurant.theme_background_color || '#f8fafc';
var dateNotSelected = !checkoutData.scheduledTime || !checkoutData.scheduledTime.split('T')[0];
var timeNotSelected = !checkoutData.scheduledTime || !checkoutData.scheduledTime.split('T')[1];
timeHtml += '';
timeHtml += '
';
timeHtml += '
';
timeHtml += '
';
timeHtml += '
';
}
if (isAsap) {
timeBadge = 'ASAP';
} else if (isScheduled) {
if (checkoutData.scheduledTime && checkoutData.scheduledTime.indexOf(':') > -1) {
var sched = checkoutData.scheduledTime.split('T');
var dateParts = sched[0].split('-');
var timeH = parseInt(sched[1].split(':')[0]);
var timeMin = sched[1].split(':')[1];
var h12 = (timeH % 12 === 0) ? 12 : (timeH % 12);
var ampm = timeH < 12 ? 'AM' : 'PM';
timeBadge = dateParts[2] + '/' + dateParts[1] + '/' + dateParts[0] + ' ' + String(h12).padStart(2, '0') + ':' + timeMin + ' ' + ampm;
} else {
timeBadge = 'Select Date & Time';
}
} else {
timeBadge = 'Choose ASAP or Schedule';
}
}
// ADDRESS CONTENT
var addrHtml = '';
if (checkoutData.orderType === 'delivery') {
addrHtml += '
Address * ';
addrHtml += '
';
addrHtml += '
';
addrHtml += '
📍
';
var addrPulseClass = !checkoutData.address ? ' class="base44-pulse-field"' : '';
addrHtml += '
';
addrHtml += '
Locate ';
addrHtml += '
';
addrHtml += '
Calculating delivery fee...
';
addrHtml += '
';
addrHtml += '
';
addrHtml += '
Save & Continue ';
addrHtml += '
';
}
// RENDER SECTIONS IN CORRECT ORDER
if (checkoutData.orderType === 'delivery') {
html += renderSection('address', 'Delivery Address', '📍', isAddressComplete, addrHtml, checkoutData.address ? (checkoutData.address.length > 35 ? checkoutData.address.substring(0,35)+'...' : checkoutData.address) : '');
html += renderSection('time', timeLabel, '⏰', isTimingComplete, timeHtml, timeBadge);
} else if (checkoutData.orderType === 'dine_in') {
// Split into 3 separate sections for dine-in
// Section 1: Timing
var timingSummary = '';
if (checkoutData.scheduledTime) {
var sched = checkoutData.scheduledTime.split('T');
var dateParts = sched[0].split('-');
var timeH = parseInt(sched[1].split(':')[0]);
var timeMin = sched[1].split(':')[1];
var h12 = (timeH % 12 === 0) ? 12 : (timeH % 12);
var ampm = timeH < 12 ? 'AM' : 'PM';
timingSummary = dateParts[2] + '/' + dateParts[1] + '/' + dateParts[0] + ' ' + String(h12).padStart(2, '0') + ':' + timeMin + ' ' + ampm;
}
html += renderSection('dine_in_time', 'Order Timing', '⏰', isTimingComplete, timeHtml, timingSummary || 'Select Date & Time');
// Section 2: Party Size
var formatPartySize = function(n) {
if (!n) return 'Select Party Size';
return n + ' ' + (n === 1 ? 'person' : 'people');
};
var partySizeHtml = '
Party Size ';
var partySizeClass = !checkoutData.partySize ? 'base44-pulse-field' : '';
partySizeHtml += '
';
for (var ps = 1; ps <= 10; ps++) {
var isSelected = checkoutData.partySize === ps;
var btnColor = isSelected ? (restaurant.theme_button_color || '#f97316') : '#f8fafc';
var btnTextColor = isSelected ? (restaurant.theme_button_text_color || '#fff') : '#1e293b';
var btnBorder = isSelected ? (restaurant.theme_button_color || '#f97316') : '#e2e8f0';
partySizeHtml += '' + ps + ' ';
}
partySizeHtml += '
';
html += renderSection('dine_in_party', 'Party Size', '👥', !!checkoutData.partySize, partySizeHtml, checkoutData.partySize ? formatPartySize(checkoutData.partySize) : '');
// Section 3: Contact Info
var contactHtml = '
';
var namePulse = !checkoutData.name ? ' class="base44-pulse-field"' : '';
var phonePulse = !checkoutData.phone ? ' class="base44-pulse-field"' : '';
var emailPulse = !checkoutData.email ? ' class="base44-pulse-field"' : '';
contactHtml += ' ';
contactHtml += ' ';
contactHtml += ' ';
contactHtml += ' Receive offers & updates ';
contactHtml += 'Save & Continue ';
contactHtml += '
';
var contactSummary = checkoutData.name;
if (checkoutData.phone) contactSummary += ' • ' + checkoutData.phone;
html += renderSection('dine_in_contact', 'Contact Information', '👤', isContactComplete, contactHtml, contactSummary || '');
} else {
html += renderSection('time', timeLabel, '⏰', isTimingComplete, timeHtml, timeBadge);
}
// --- SECTION 4: Contact Info (for delivery/pickup) ---
if (checkoutData.orderType !== 'dine_in') {
var contactHtml = '
';
var namePulse = !checkoutData.name ? ' class="base44-pulse-field"' : '';
var phonePulse = !checkoutData.phone ? ' class="base44-pulse-field"' : '';
var emailPulse = !checkoutData.email ? ' class="base44-pulse-field"' : '';
contactHtml += ' ';
contactHtml += ' ';
contactHtml += ' ';
contactHtml += ' Receive offers & updates ';
contactHtml += 'Save & Continue ';
contactHtml += '
';
var contactSummary = checkoutData.name;
if (checkoutData.phone) contactSummary += ' • ' + checkoutData.phone;
html += renderSection('contact', 'Contact Information', '👤', isContactComplete, contactHtml, contactSummary || '');
}
// --- SECTION 5: Special Instructions (Optional) ---
var notesHtml = '
';
var noteSummary = checkoutData.notes ? (checkoutData.notes.length > 25 ? checkoutData.notes.substring(0, 25) + '...' : checkoutData.notes) : '(Optional)';
html += renderSection('notes', 'Special Instructions', '📝', !!checkoutData.notes, notesHtml, noteSummary);
// --- SECTION 6: Promo Code (Optional) ---
var promoHtml = '';
if (checkoutData.appliedPromo) {
promoHtml += '
';
promoHtml += '' + checkoutData.appliedPromo.name + ' ';
promoHtml += 'Remove ';
promoHtml += '
';
} else {
promoHtml += '
Apply
';
}
html += renderSection('promo', 'Promo Code', '🎁', !!checkoutData.appliedPromo, promoHtml, checkoutData.appliedPromo ? checkoutData.appliedPromo.promo_code : '(Optional)');
// --- SECTION 7: Tip (Optional) ---
var tipsSettings = restaurant.tips_settings || {};
var tipsGlobalEnabled = tipsSettings.enabled;
var orderTypeKey = checkoutData.orderType === 'delivery' ? 'delivery_enabled'
: checkoutData.orderType === 'pickup' ? 'pickup_enabled'
: 'dine_in_enabled';
var tipsEnabledForOrderType = tipsGlobalEnabled && tipsSettings[orderTypeKey];
var tipHtml = '';
if (tipsEnabledForOrderType) {
tipHtml += '
';
// Tip Amount Display
tipHtml += '
';
tipHtml += '
Tip Amount ';
tipHtml += '
';
tipHtml += '
' + currency + tipAmount.toFixed(2) + '
';
tipHtml += '
' + (checkoutData.tipType === 'percentage' ? checkoutData.tipPercentage + '% of subtotal' : 'Custom amount') + '
';
tipHtml += '
';
// Tip Type Toggle (if both enabled)
var showPercentage = tipsSettings.percentage_enabled;
var showFlat = tipsSettings.flat_amount_enabled;
// Default fallback if undefined
if (showPercentage === undefined) showPercentage = true;
if (showFlat === undefined) showFlat = false;
if (showPercentage && showFlat) {
tipHtml += '
';
tipHtml += 'Percentage ';
tipHtml += 'Fixed Amount ';
tipHtml += '
';
} else if (!showPercentage && showFlat) {
// Force flat amount if percentage is disabled
checkoutData.tipType = 'flat_amount';
} else if (showPercentage && !showFlat) {
checkoutData.tipType = 'percentage';
}
if (checkoutData.tipType === 'percentage' && showPercentage) {
var percentages = tipsSettings.default_percentages || [10, 15, 20, 25];
tipHtml += '
';
// Slider
tipHtml += '
';
tipHtml += '
0% 15% 30%
';
tipHtml += '
';
tipHtml += '
';
percentages.forEach(function(pct){
tipHtml += '' + pct + '% ';
});
tipHtml += '
';
} else if (showFlat) {
var flatAmounts = tipsSettings.default_flat_amounts || [2, 5, 10];
tipHtml += '
';
flatAmounts.forEach(function(amt){
tipHtml += '' + currency + amt + ' ';
});
tipHtml += '
';
tipHtml += '
Or enter custom amount:
';
}
tipHtml += '
';
}
html += renderSection('tip', 'Add a Tip', '💚', tipAmount > 0, tipHtml, tipAmount > 0 ? currency + tipAmount.toFixed(2) : (tipsEnabledForOrderType ? '(Optional)' : 'Not available'));
// --- SECTION 8: Payment Method ---
// Check if all required fields are complete to enable payment selection
var canSelectPayment = isOrderTypeComplete && isTimingComplete && isAddressComplete && isContactComplete;
var payHtml = '
';
if (paymentMethods.length > 0) {
var payPulse = !checkoutData.paymentMethod ? ' class="base44-pulse-field"' : '';
var isDisabled = !canSelectPayment;
payHtml += '
';
payHtml += '' + (isDisabled ? 'Complete required fields first' : 'Select Payment Method') + ' ';
paymentMethods.forEach(function(pm){
payHtml += '' + pm.name + ' ';
});
payHtml += ' ';
if (isDisabled) {
payHtml += '
⚠️ Please complete all required sections above to select payment
';
}
if (checkoutData.paymentMethod === 'stripe') {
payHtml += '
';
payHtml += '
';
}
} else {
payHtml += '
No payment methods available
';
}
payHtml += '
';
var paymentMethodName = '';
if (checkoutData.paymentMethod) {
var pm = paymentMethods.find(function(p) { return p.id === checkoutData.paymentMethod; });
paymentMethodName = pm ? pm.name : (checkoutData.paymentMethod === 'stripe' ? 'Card' : checkoutData.paymentMethod.replace(/_/g, ' '));
}
html += renderSection('payment', 'Payment Method', '💳', !!checkoutData.paymentMethod, payHtml, paymentMethodName);
// Close Left Column
html += '
';
// Right Column (Summary)
html += '
';
html += '
';
html += '
📋 Order Summary ';
cart.forEach(function(item) {
html += '
';
html += '
' + item.quantity + 'x ' + item.name + ' ' + currency + (item.price * item.quantity).toFixed(2) + '
';
// Display options in checkout
if (item.selected_options && Object.keys(item.selected_options).length > 0) {
var optText = [];
Object.keys(item.selected_options).forEach(function(optName) {
var optVal = item.selected_options[optName];
if (Array.isArray(optVal) && optVal.length > 0) {
optText.push(optVal.join(', '));
} else if (optVal && typeof optVal === 'string') {
optText.push(optVal);
}
});
if (optText.length > 0) {
html += '
' + optText.join(' • ') + '
';
}
}
if (item.special_instructions) {
html += '
📝 ' + item.special_instructions + '
';
}
html += '
';
});
var vatCalc = calculateVAT();
html += '
';
html += '
Subtotal ' + currency + cartTotal.toFixed(2) + '
';
if (tipAmount > 0) {
html += '
Tip ' + currency + tipAmount.toFixed(2) + '
';
}
if (deliveryFee > 0) {
html += '
🚗 Delivery Fee ' + currency + deliveryFee.toFixed(2) + '
';
}
if (feeBreakdown.length > 0) {
feeBreakdown.forEach(function(fee) {
if(fee.amount > 0) {
html += '
' + fee.name + (fee.type === 'percentage' ? ' (' + fee.value + '%)' : '') + ' ' + currency + fee.amount.toFixed(2) + '
';
}
});
}
if (discount > 0) {
html += '
Discount -' + currency + discount.toFixed(2) + '
';
}
if (vatCalc.vat_amount > 0) {
html += '
VAT (' + vatCalc.vat_rate + '%) ' + currency + vatCalc.vat_amount.toFixed(2) + '
';
}
html += '
Total ' + currency + orderTotal.toFixed(2) + '
';
html += '
';
html += '
'; // Spacer for floating button
html += '
';
// Floating sticky payment button at bottom
html += '
';
html += '
';
if (checkoutData.paymentMethod === 'stripe' && STRIPE_PUBLISHABLE_KEY) {
html += '' + (stripePaymentProcessing ? '⏳ Processing...' : '💳 Pay ' + currency + orderTotal.toFixed(2) + ' with Card') + ' ';
} else {
html += '' + (isSubmitting ? '⏳ Placing Order...' : '🛒 Place Order - ' + currency + orderTotal.toFixed(2)) + ' ';
}
html += '
';
return html;
}
function renderAccountPage(currency) {
// This should not be called anymore - redirect to login popup
renderLoginPopup();
return '';
}
function renderConfirmationPage(currency) {
var order = currentOrderData;
console.log('[Embedder] Rendering confirmation page - order status:', order ? order.status : 'NO ORDER');
var statusConfig = {
pending: { icon: '⏳', title: 'Order Pending', desc: "We're reviewing your order. You'll be notified once it's confirmed.", color: '#d97706', bgColor: '#fef3c7' },
confirmed: { icon: '✅', title: 'Confirmed', desc: 'Great! We have confirmed your order and will start preparing it soon.', color: '#059669', bgColor: '#d1fae5' },
preparing: { icon: '👨🍳', title: 'Being Prepared', desc: 'Our kitchen is working on your order right now!', color: '#ea580c', bgColor: '#ffedd5' },
ready: { icon: '🛍️', title: 'Ready', desc: order.order_type === 'delivery' ? 'Your order is ready and waiting for pickup by our delivery driver.' : 'Your order is ready for pickup! Please come to the restaurant.', color: '#2563eb', bgColor: '#dbeafe' },
out_for_delivery: { icon: '🚗', title: 'Out for Delivery', desc: 'Your order is on the way! It should arrive soon.', color: '#7c3aed', bgColor: '#ede9fe' },
completed: { icon: '✅', title: order.order_type === 'delivery' ? 'Delivered' : order.order_type === 'pickup' ? 'Picked Up' : 'Completed', desc: order.order_type === 'delivery' ? 'Your order has been delivered. Enjoy your meal!' : order.order_type === 'pickup' ? 'Your order has been picked up. Enjoy your meal!' : 'Your order is complete. Enjoy your meal!', color: '#059669', bgColor: '#d1fae5' },
cancelled: { icon: '❌', title: 'Cancelled', desc: order.rejection_reason ? 'This order was cancelled. Reason: ' + order.rejection_reason.replace(/_/g, ' ') : 'This order has been cancelled.', color: '#dc2626', bgColor: '#fee2e2' }
};
var status = statusConfig[order.status] || statusConfig.pending;
var stages = ['pending', 'confirmed', 'preparing', 'ready'];
if (order.order_type === 'delivery') stages.push('out_for_delivery');
stages.push('completed');
// Helper function to format time remaining or overdue
function formatTimeRemaining(ms) {
var isOverdue = ms < 0;
var absMs = Math.abs(ms);
var totalSeconds = Math.floor(absMs / 1000);
var hours = Math.floor(totalSeconds / 3600);
var minutes = Math.floor((totalSeconds % 3600) / 60);
var seconds = totalSeconds % 60;
var timeStr = '';
if (hours > 0) timeStr = hours + ' hr ' + minutes + ' min';
else if (minutes > 0) timeStr = minutes + ' min ' + seconds + ' sec';
else timeStr = seconds + ' sec';
return { timeStr: timeStr, isOverdue: isOverdue };
}
// Helper function to format date/time
function formatDateTime(dateStr) {
if (!dateStr) return null;
var d = new Date(dateStr);
return d.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true });
}
function formatTime(dateStr) {
if (!dateStr) return null;
var d = new Date(dateStr);
return d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
}
// Calculate time remaining or overdue
var timeRemaining = null;
var expectedAtTime = null;
var isOverdue = false;
if (order.estimated_ready_time && ['confirmed', 'preparing', 'ready', 'out_for_delivery'].includes(order.status)) {
var estimatedTime = new Date(order.estimated_ready_time).getTime();
var now = Date.now();
var remaining = estimatedTime - now;
var timeData = formatTimeRemaining(remaining);
timeRemaining = timeData.timeStr;
isOverdue = timeData.isOverdue;
expectedAtTime = formatTime(order.estimated_ready_time);
}
var requestedTime = order.scheduled_time ? formatDateTime(order.scheduled_time) : null;
var html = '
';
// Optimized layout container with min-width fix for grid items
html += '
';
html += '';
html += '
';
// Left side - Status and Progress
html += '
';
html += '
';
html += '
' + status.icon + '
';
html += '
' + status.title + ' ';
html += '
' + status.desc + '
';
// Expected Time Display with Countdown/Overdue
if (order.estimated_ready_time && ['confirmed', 'preparing', 'ready', 'out_for_delivery'].includes(order.status)) {
var bgGradient = isOverdue ? 'linear-gradient(135deg, #fee2e2, #fecaca)' : 'linear-gradient(135deg, #ecfdf5, #d1fae5)';
var borderColor = isOverdue ? '#FF0400' : '#2BB595';
var iconBg = isOverdue ? '#FF0400' : '#2BB595';
var labelColor = isOverdue ? '#991b1b' : '#059669';
var valueColor = isOverdue ? '#7f1d1d' : '#065f46';
html += '
';
html += '
';
html += '
' + (isOverdue ? '⚠️' : '⏱️') + '
';
html += '
';
if (timeRemaining) {
html += '
' + (isOverdue ? 'OVERDUE' : 'Time Remaining') + '
';
html += '
' + timeRemaining + '
';
}
if (expectedAtTime) {
html += '
Expected: ' + expectedAtTime + '
';
}
html += '
';
}
// Requested/Scheduled Time
if (requestedTime) {
html += '
';
html += '
';
html += '
📅 ';
html += '
';
html += '
Requested Time
';
html += '
' + requestedTime + '
';
html += '
';
}
// Order Info Grid - mobile friendly
html += '
';
html += '
Restaurant: ' + (restaurant.name || 'Restaurant') + '
';
html += '
Order Type: ' + (order.order_type === 'delivery' ? '🚗 DELIVERY' : order.order_type === 'dine_in' ? '🍽️ DINE IN' : '🛍️ PICKUP') + '
';
if (order.table_number) {
html += '
Table: ' + order.table_number + '
';
}
if (order.party_size) {
html += '
Guests: ' + order.party_size + ' people
';
}
html += '
Order ID: #' + order.id.slice(0, 8).toUpperCase() + '
';
html += '
Payment: ' + (order.payment_status || 'PENDING').toUpperCase() + '
';
html += '
Method: ' + (order.payment_method ? order.payment_method.replace(/_/g, ' ').toUpperCase() : 'N/A') + '
';
html += '
Total: ' + currency + (order.total || 0).toFixed(2) + '
';
html += '
';
html += '
';
// Helper function to get stage timestamp
function getStageTimestamp(order, stage) {
switch (stage) {
case 'pending':
return order.created_date;
case 'confirmed':
return order.confirmed_at;
case 'preparing':
return order.preparing_started_at;
case 'ready':
return order.ready_at;
case 'out_for_delivery':
return order.out_for_delivery_at;
case 'completed':
return order.completed_at;
default:
return null;
}
}
// Progress Tracker - responsive with modern design
html += '
';
html += '
Order Progress ';
html += '
';
html += '';
html += '
';
stages.forEach(function(stage, idx) {
var isActive = stages.indexOf(order.status) >= idx;
var isCurrent = order.status === stage;
var stageConfig = statusConfig[stage] || { icon: '⏳', title: stage, color: '#64748b', bgColor: '#e2e8f0' };
var timestamp = getStageTimestamp(order, stage);
html += '
';
// Connecting line (horizontal line between stages)
if (idx < stages.length - 1) {
var lineColor = isActive && !isCurrent ? '#22c55e' : isActive ? stageConfig.color : '#cbd5e1';
html += '
';
}
// Circle with icon
html += '
' + stageConfig.icon + '
';
// Status title and timestamp
html += '
';
html += '
' + stageConfig.title + '
';
if (timestamp) {
var timestampDate = new Date(timestamp);
var formattedTime = timestampDate.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
html += '
' + formattedTime + '
';
}
html += '
';
html += '
';
});
html += '
';
html += '
';
// Live Tracking Map - only for out_for_delivery orders
if (order.status === 'out_for_delivery' && order.driver_location && order.delivery_location && order.restaurant_location) {
html += '
';
html += '
🚗 Live Tracking
';
html += '
';
// Driver info card below map
html += '
';
html += '
🚗
';
html += '
';
if (order.driver_name) {
html += '
' + order.driver_name + '
';
}
if (order.driver_phone) {
html += '
📞 ' + order.driver_phone + ' ';
}
if (order.driver_vehicle_type && order.driver_vehicle_number) {
html += '
🚙 ' + order.driver_vehicle_type.toUpperCase() + ' • ' + order.driver_vehicle_number + '
';
}
html += '
';
html += '
On the way
';
html += '
';
html += '
';
}
// Action Buttons - responsive
html += '
';
html += '🔄 Order Again ';
html += '';
html += '
';
html += '
';
// Right side - Order Items - responsive
html += '
';
html += '
';
html += '
🛍️ Order Items ';
(order.items || []).forEach(function(item) {
html += '
';
html += '
';
html += '
' + (item.is_prepared ? '✅ ' : '') + item.quantity + 'x ' + item.item_name + '
';
// Display options in confirmation
if (item.selected_options && Array.isArray(item.selected_options) && item.selected_options.length > 0) {
var optStr = item.selected_options.map(function(opt) {
return opt.selected_choices.map(function(c) { return c.choice_name; }).join(', ');
}).join(' • ');
html += '
' + optStr + '
';
}
if (item.special_instructions) {
html += '
📝 ' + item.special_instructions + '
';
}
if (item.is_prepared) {
html += '
✓ Prepared ';
}
html += '
';
html += '
' + currency + (item.price * item.quantity).toFixed(2) + '
';
html += '
';
});
// Order Totals - mobile friendly
html += '
';
html += '
Subtotal ' + currency + (order.subtotal || 0).toFixed(2) + '
';
if (order.tip_amount > 0) {
html += '
💚 Tip ' + currency + order.tip_amount.toFixed(2) + '
';
}
if (order.delivery_fee > 0) {
html += '
🚗 Delivery Fee ' + currency + order.delivery_fee.toFixed(2) + '
';
}
if (order.other_fees_breakdown && order.other_fees_breakdown.length > 0) {
order.other_fees_breakdown.forEach(function(fee) {
if (fee.amount > 0) {
html += '
' + fee.name + ' ' + currency + fee.amount.toFixed(2) + '
';
}
});
} else if (order.other_fees > 0) { // Fallback for older orders
html += '
Other Fees ' + currency + order.other_fees.toFixed(2) + '
';
}
if (order.discount_amount > 0) {
html += '
Discount -' + currency + order.discount_amount.toFixed(2) + '
';
}
if (order.vat_amount > 0) {
html += '
VAT (' + order.vat_rate + '%) ' + currency + order.vat_amount.toFixed(2) + '
';
}
html += '
Total ' + currency + (order.total || 0).toFixed(2) + '
';
html += '
';
// Special Notes - mobile friendly
if (order.special_notes) {
html += '
📝 Special Notes:
' + order.special_notes + '
';
}
html += '
';
html += '
';
html += '
';
// Add pulse animation for current status and payment method
var confirmPulseColor = restaurant.theme_button_color || '#f97316';
var confirmPulseRgb = hexToRgb(confirmPulseColor);
html += '';
html += '
';
return html;
}
function renderOptionsModal(currency) {
var item = selectedItemForOptions;
console.log('[Embedder Options Render] =============================');
console.log('[Embedder Options Render] Item:', item.name);
console.log('[Embedder Options Render] Item.options structure:', JSON.stringify(item.options, null, 2));
console.log('[Embedder Options Render] selectedOptions:', JSON.stringify(selectedOptions));
console.log('[Embedder Options Render] =============================');
// Calculate total price with options
var calculatedPrice = item.price;
if (item.options) {
item.options.forEach(function(optGroup) {
var selections = selectedOptions[optGroup.name] || [];
selections.forEach(function(selectedChoice) {
var choice = optGroup.choices.find(function(c) { return c.name === selectedChoice; });
if (choice && choice.price_modifier) {
calculatedPrice += choice.price_modifier;
}
});
});
}
var totalPrice = calculatedPrice * optionsQuantity;
var html = '
';
html += '
';
// Header
html += '
';
html += '
' + item.name + ' ';
html += '× ';
html += '';
// Scrollable content
html += '
';
// Image
if (item.image_url) {
html += '
';
html += '
';
html += '
';
}
// Description
if (item.description) {
html += '
' + item.description + '
';
}
// Base Price
html += '
';
html += 'Base Price: ';
html += '' + currency + item.price.toFixed(2) + ' ';
html += '
';
// Option Groups
if (item.options && item.options.length > 0) {
html += '
';
item.options.forEach(function(optGroup) {
console.log('[Embedder Options Render] Processing group:', optGroup.name, 'Type:', optGroup.type);
html += '
';
// Group header with badge
html += '
';
html += '
' + optGroup.name + ' ';
if (optGroup.required) {
html += '* ';
}
html += '';
if (optGroup.type === 'single_select') {
html += 'Choose one';
} else {
var minSel = optGroup.min_selections || 0;
var maxSel = optGroup.max_selections || 'any';
html += 'Select ' + minSel + '-' + maxSel;
}
html += ' ';
html += '';
// Choices
html += '
';
optGroup.choices.forEach(function(choice) {
var isSelected = false;
var currentSelection = selectedOptions[optGroup.name];
if (optGroup.type === 'single_select') {
if (Array.isArray(currentSelection)) {
isSelected = currentSelection.length > 0 && currentSelection[0] === choice.name;
} else if (currentSelection) {
isSelected = currentSelection === choice.name;
}
} else {
if (Array.isArray(currentSelection)) {
isSelected = currentSelection.indexOf(choice.name) !== -1;
}
}
var buttonBg = isSelected ? (restaurant.theme_button_color || '#f97316') : '#f8fafc';
var buttonColor = isSelected ? (restaurant.theme_button_text_color || '#fff') : (restaurant.theme_text_color || '#1E293B');
var buttonBorder = isSelected ? (restaurant.theme_button_color || '#f97316') : '#e2e8f0';
html += '';
html += '' + choice.name;
if (choice.price_modifier && choice.price_modifier !== 0) {
html += '(+' + currency + Math.abs(choice.price_modifier).toFixed(2) + ') ';
}
html += ' ';
html += ' ';
});
html += '
';
html += '
';
});
html += '
';
}
// Special instructions
html += '
';
html += 'Special Instructions (Optional) ';
html += '';
html += '
';
// Quantity
html += '
';
html += '
Quantity: ';
html += '
';
html += '− ';
html += '' + optionsQuantity + ' ';
html += '+ ';
html += '
';
html += '
';
// Footer with action buttons
html += '
';
html += '';
html += '
';
html += '
';
return html;
}
function loadLeafletAndInitMap(order) {
// Load Leaflet CSS if not already loaded
if (!document.querySelector('link[href*="leaflet.css"]')) {
var leafletCSS = document.createElement('link');
leafletCSS.rel = 'stylesheet';
leafletCSS.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
leafletCSS.integrity = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=';
leafletCSS.crossOrigin = '';
document.head.appendChild(leafletCSS);
}
// Load Leaflet JS if not already loaded
if (!window.L) {
var leafletScript = document.createElement('script');
leafletScript.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
leafletScript.integrity = 'sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=';
leafletScript.crossOrigin = '';
leafletScript.onload = function() {
setTimeout(function() { initTrackingMap(order); }, 500);
};
document.head.appendChild(leafletScript);
} else {
setTimeout(function() { initTrackingMap(order); }, 500);
}
}
function initTrackingMap(order) {
if (!window.L) {
console.error('Leaflet not loaded');
return;
}
var mapDiv = document.getElementById('live-tracking-map-' + order.id);
if (!mapDiv) {
console.log('Map div not found');
return;
}
if (mapDiv.offsetHeight === 0) {
mapDiv.style.height = '300px';
mapDiv.style.width = '100%';
}
try {
// Clear any existing map
while (mapDiv.firstChild) {
mapDiv.removeChild(mapDiv.firstChild);
}
// Fix Leaflet icon paths
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png',
iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png'
});
// Create map
var map = L.map(mapDiv, {
zoomControl: true,
scrollWheelZoom: false,
dragging: true,
tap: false
}).setView([order.driver_location.lat, order.driver_location.lng], 14);
// Add Google Maps tiles
L.tileLayer('https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', {
maxZoom: 20,
attribution: '© Google Maps'
}).addTo(map);
// Custom icons
var driverIcon = L.icon({
iconUrl: 'https://static.vecteezy.com/system/resources/thumbnails/036/876/825/small/3d-delivery-man-character-deivering-package-with-a-scooter-free-png.png',
iconSize: [50, 50],
iconAnchor: [25, 25],
popupAnchor: [0, -25]
});
var restaurantIcon = L.icon({
iconUrl: 'https://static.vecteezy.com/system/resources/thumbnails/066/380/758/small/restaurant-icon-3d-rendering-illustration-png.png',
iconSize: [40, 40],
iconAnchor: [20, 40],
popupAnchor: [0, -40]
});
var customerIcon = L.icon({
iconUrl: 'https://cdn3d.iconscout.com/3d/premium/thumb/home-3d-icon-png-download-8383048.png',
iconSize: [40, 40],
iconAnchor: [20, 40],
popupAnchor: [0, -40]
});
// Add markers
L.marker([order.driver_location.lat, order.driver_location.lng], { icon: driverIcon })
.addTo(map)
.bindPopup('
🚗 Driver ' + (order.driver_name ? '
' + order.driver_name : '') + (order.driver_phone ? '
📞 ' + order.driver_phone : ''));
L.marker([order.restaurant_location.lat, order.restaurant_location.lng], { icon: restaurantIcon })
.addTo(map)
.bindPopup('
🍴 Restaurant ');
L.marker([order.delivery_location.lat, order.delivery_location.lng], { icon: customerIcon })
.addTo(map)
.bindPopup('
🏠 Delivery Address ');
// Draw route line
var routeCoords = [
[order.driver_location.lat, order.driver_location.lng],
[order.delivery_location.lat, order.delivery_location.lng]
];
L.polyline(routeCoords, { color: '#7c3aed', weight: 4, opacity: 0.7 }).addTo(map);
// Fit bounds to show all markers
var bounds = L.latLngBounds([
[order.restaurant_location.lat, order.restaurant_location.lng],
[order.driver_location.lat, order.driver_location.lng],
[order.delivery_location.lat, order.delivery_location.lng]
]);
map.fitBounds(bounds, { padding: [40, 40] });
// Force map to recalculate size after render
setTimeout(function() {
try { map.invalidateSize(); } catch(e) {}
}, 300);
} catch (e) {
console.error('Map init error:', e);
mapDiv.innerHTML = '
Map unavailable
';
}
}
function attachEventListeners() {
if (!currentModalContent) return;
// Search toggle and handlers
var modalSearchToggle = currentModalContent.querySelector('#modal-search-toggle');
var modalSearchClose = currentModalContent.querySelector('#modal-search-close');
var modalSearchInput = currentModalContent.querySelector('#modal-search-input');
if (modalSearchToggle) {
modalSearchToggle.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
modalSearchToggle.style.display = 'none';
if (modalSearchInput) {
modalSearchInput.style.display = 'block';
modalSearchInput.value = searchQuery || '';
modalSearchInput.focus();
}
if (modalSearchClose) modalSearchClose.style.display = 'flex';
};
}
if (modalSearchClose) {
modalSearchClose.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
searchQuery = '';
if (modalSearchInput) modalSearchInput.style.display = 'none';
modalSearchClose.style.display = 'none';
if (modalSearchToggle) modalSearchToggle.style.display = 'flex';
renderMenu();
};
}
if (modalSearchInput) {
// Prevent renderMenu from stealing focus
modalSearchInput.onkeyup = function(e) {
searchQuery = e.target.value;
if (e.key === 'Enter') {
e.target.blur();
}
};
modalSearchInput.onchange = function(e) {
searchQuery = e.target.value;
};
// Live search - filter items as user types
var searchTimeout = null;
modalSearchInput.oninput = function(e) {
searchQuery = e.target.value;
clearTimeout(searchTimeout);
searchTimeout = setTimeout(function() {
// Update only the menu items grid, not the whole page
var filteredItems = getFilteredItems();
var menuGrid = currentModalContent.querySelector('[data-menu-grid]');
if (menuGrid) {
// Re-render just the items
renderMenuItemsOnly(filteredItems, menuGrid);
}
}, 300);
};
}
// Accordion headers
var checkoutHeaders = currentModalContent.querySelectorAll('.checkout-header');
checkoutHeaders.forEach(function(header) {
header.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var id = header.dataset.id;
// PAYMENT SECTION: Always keep open, don't allow collapse
if (id === 'payment') {
return; // Do nothing - keep payment section always open
}
// Toggle: if already open, close it; otherwise open it
if (checkoutData.openSection === id) {
checkoutData.openSection = null;
} else {
checkoutData.openSection = id;
}
renderMenu();
};
});
// Category buttons
var catBtns = currentModalContent.querySelectorAll('.cat-btn');
catBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
selectedCategory = btn.dataset.category;
// Clear search when clicking a category
searchQuery = '';
renderMenu();
};
});
// Add buttons
var addBtns = currentModalContent.querySelectorAll('.add-btn');
addBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var itemId = btn.dataset.itemId;
var item = menuItems.find(function(i) { return i.id === itemId; });
if (item) {
// Check if item has options
if (item.options && item.options.length > 0) {
handleItemClick(item);
} else {
// Get notes from the input field
var notesInput = currentModalContent.querySelector('.menu-item-notes[data-item-id="' + itemId + '"]');
var notes = notesInput ? notesInput.value : '';
var qty = itemQuantities[itemId] || 1;
for (var i = 0; i < qty; i++) {
addToCart(item, null, notes);
}
if (notesInput) notesInput.value = '';
itemQuantities[itemId] = 1;
if (window.showCartPreviewBriefly) window.showCartPreviewBriefly();
}
}
};
});
// Quantity buttons
var qtyDecreaseBtns = currentModalContent.querySelectorAll('.qty-decrease-btn');
qtyDecreaseBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var itemId = btn.dataset.itemId;
var current = itemQuantities[itemId] || 1;
if (current > 1) {
itemQuantities[itemId] = current - 1;
renderMenu();
}
};
});
var qtyIncreaseBtns = currentModalContent.querySelectorAll('.qty-increase-btn');
qtyIncreaseBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var itemId = btn.dataset.itemId;
var current = itemQuantities[itemId] || 1;
itemQuantities[itemId] = current + 1;
renderMenu();
};
});
// Floating cart button with hover preview
var floatingCartBtn = currentModalContent.querySelector('#floating-cart-btn');
var floatingCartContainer = currentModalContent.querySelector('#floating-cart-container');
var floatingCartPreview = currentModalContent.querySelector('#floating-cart-preview');
if (floatingCartBtn) {
floatingCartBtn.onclick = function(e) {
e.preventDefault();
openCart();
};
}
var previewTimeout = null;
if (floatingCartContainer && floatingCartPreview) {
floatingCartContainer.onmouseenter = function() {
if (previewTimeout) clearTimeout(previewTimeout);
floatingCartPreview.style.display = 'flex';
};
floatingCartContainer.onmouseleave = function() {
floatingCartPreview.style.display = 'none';
};
}
// Auto-show preview briefly when item added
window.showCartPreviewBriefly = function() {
if (floatingCartPreview) {
if (previewTimeout) clearTimeout(previewTimeout);
floatingCartPreview.style.display = 'flex';
previewTimeout = setTimeout(function() {
floatingCartPreview.style.display = 'none';
}, 4000);
}
};
// Preview action buttons
var previewViewCartBtn = currentModalContent.querySelector('#preview-view-cart-btn');
var previewCheckoutBtn = currentModalContent.querySelector('#preview-checkout-btn');
if (previewViewCartBtn) {
previewViewCartBtn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
openCart();
};
}
if (previewCheckoutBtn) {
previewCheckoutBtn.onclick = async function(e) {
e.preventDefault();
e.stopPropagation();
await goToCheckout();
};
}
// Cart back button
var cartBackBtn = currentModalContent.querySelector('#cart-back');
if (cartBackBtn) {
cartBackBtn.onclick = function(e) {
e.preventDefault();
closeCart();
};
}
// Cart quantity buttons
var cartQtyBtns = currentModalContent.querySelectorAll('.cart-qty-btn');
cartQtyBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
var index = parseInt(btn.dataset.index);
var action = btn.dataset.action;
if (action === 'increase') {
cart[index].quantity += 1;
} else if (action === 'decrease') {
if (cart[index].quantity > 1) {
cart[index].quantity -= 1;
} else {
cart.splice(index, 1);
}
}
saveCart();
if (cart.length === 0) {
cartOpen = false;
}
renderMenu();
};
});
// Cart remove buttons
var cartRemoveBtns = currentModalContent.querySelectorAll('.cart-remove-btn');
cartRemoveBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
var index = parseInt(btn.dataset.index);
cart.splice(index, 1);
saveCart();
if (cart.length === 0) {
cartOpen = false;
}
renderMenu();
};
});
// Edit instructions buttons
var editInstructionsBtns = currentModalContent.querySelectorAll('.edit-instructions-btn');
editInstructionsBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var index = parseInt(btn.dataset.index);
var instructionsDiv = btn.closest('.cart-item-instructions');
if (instructionsDiv) {
var existingText = cart[index].special_instructions || '';
instructionsDiv.style.flexDirection = 'column';
instructionsDiv.style.alignItems = 'stretch';
instructionsDiv.innerHTML = '
Save Note ';
var textarea = instructionsDiv.querySelector('.instructions-textarea');
var saveBtn = instructionsDiv.querySelector('.save-instructions-btn');
if (saveBtn) {
saveBtn.onclick = function(ev) {
ev.preventDefault();
ev.stopPropagation();
var newText = textarea ? textarea.value.trim() : '';
cart[index].special_instructions = newText;
saveCart();
renderMenu();
};
}
if (textarea) {
setTimeout(function() {
textarea.focus({ preventScroll: true });
}, 100);
}
}
};
});
// Proceed to checkout button
var proceedCheckoutBtn = currentModalContent.querySelector('#proceed-checkout-btn');
if (proceedCheckoutBtn) {
proceedCheckoutBtn.onclick = async function(e) {
e.preventDefault();
await goToCheckout();
};
}
// Checkout back button
var checkoutBackBtn = currentModalContent.querySelector('#checkout-back');
if (checkoutBackBtn) {
checkoutBackBtn.onclick = function(e) {
e.preventDefault();
closeCheckout();
};
}
// Checkout back to cart button
var checkoutBackCartBtn = currentModalContent.querySelector('#checkout-back-cart');
if (checkoutBackCartBtn) {
checkoutBackCartBtn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
checkoutOpen = false;
cartOpen = true;
renderMenu();
};
}
// Mount Stripe if needed
if (checkoutOpen && checkoutData.paymentMethod === 'stripe' && STRIPE_PUBLISHABLE_KEY && stripeInstance) {
setTimeout(function() {
if (!cardElement) mountStripeCard();
mountPaymentRequestButton();
}, 100);
}
var orderTypeBtns = currentModalContent.querySelectorAll('.order-type-btn');
orderTypeBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
console.log('[Embedder] >>> Order type button clicked:', btn.dataset.type);
checkoutData.orderType = btn.dataset.type;
checkoutData.paymentMethod = '';
// Reset timing selection when switching order type
checkoutData.timingType = null;
// Auto-open next section based on order type
if (btn.dataset.type === 'delivery') {
checkoutData.openSection = 'address';
} else if (btn.dataset.type === 'dine_in') {
checkoutData.scheduledTime = '';
checkoutData.dineInInitialized = true;
checkoutData.openSection = 'dine_in_time';
} else {
// pickup
checkoutData.openSection = 'time';
}
renderMenu();
};
});
// Payment method select
var paymentSelect = currentModalContent.querySelector('#payment-method-select');
if (paymentSelect) {
paymentSelect.onchange = function(e) {
console.log('[Embedder] Payment method changed to:', e.target.value);
// VALIDATION: Ensure order type is selected before allowing payment selection
if (!checkoutData.orderType) {
alert('Please select an order type (Delivery, Pickup, or Dine In) first');
e.target.value = '';
checkoutData.openSection = 'type';
renderMenu();
return;
}
if (cardElement) {
try { cardElement.unmount(); } catch(err) {}
cardElement = null;
stripeElements = null;
}
stripeError = '';
checkoutData.paymentMethod = e.target.value;
console.log('[Embedder] checkoutData.paymentMethod set to:', checkoutData.paymentMethod);
renderMenu();
};
}
// Inline My Orders button
var inlineMyOrdersBtn = currentModalContent.querySelector('#inline-my-orders-btn');
if (inlineMyOrdersBtn) {
inlineMyOrdersBtn.onclick = function(e) {
e.preventDefault();
openAccount();
};
inlineMyOrdersBtn.onmouseover = function() { this.style.background = 'rgba(255,255,255,0.3)'; };
inlineMyOrdersBtn.onmouseout = function() { this.style.background = 'rgba(255,255,255,0.2)'; };
}
// Input fields
var nameInput = currentModalContent.querySelector('#checkout-name');
var phoneInput = currentModalContent.querySelector('#checkout-phone');
var emailInput = currentModalContent.querySelector('#checkout-email');
var addressInput = currentModalContent.querySelector('#checkout-address');
var notesInput = currentModalContent.querySelector('#checkout-notes');
if (nameInput) {
nameInput.oninput = function(e) {
checkoutData.name = e.target.value;
};
}
if (phoneInput) {
phoneInput.oninput = function(e) {
checkoutData.phone = e.target.value;
};
}
if (emailInput) {
emailInput.oninput = function(e) {
checkoutData.email = e.target.value;
};
}
// Save contact button (delivery/pickup)
var saveContactBtn = currentModalContent.querySelector('#save-contact-btn');
if (saveContactBtn) {
saveContactBtn.onclick = function(e) {
e.preventDefault();
if (checkoutData.name && checkoutData.phone && checkoutData.email) {
checkoutData.openSection = 'payment';
renderMenu();
}
};
}
// Save dine-in contact button
var saveDineInContactBtn = currentModalContent.querySelector('#save-dine-in-contact-btn');
if (saveDineInContactBtn) {
saveDineInContactBtn.onclick = function(e) {
e.preventDefault();
if (checkoutData.name && checkoutData.phone && checkoutData.email) {
checkoutData.openSection = 'payment';
renderMenu();
}
};
}
// Save address button
var saveAddressBtn = currentModalContent.querySelector('#save-address-btn');
if (saveAddressBtn) {
saveAddressBtn.onclick = function(e) {
e.preventDefault();
if (checkoutData.address && checkoutData.address.length > 3 && deliveryFeeData && deliveryFeeData.success) {
checkoutData.openSection = 'time';
renderMenu();
}
};
};
// Marketing opt-in checkbox
var marketingCheckbox = currentModalContent.querySelector('#checkout-marketing');
if (marketingCheckbox) {
marketingCheckbox.onchange = function(e) { checkoutData.marketingOptIn = e.target.checked; };
}
// Party size input for dine-in
var partySizeInput = currentModalContent.querySelector('#party-size-input');
if (partySizeInput) {
partySizeInput.oninput = function(e) {
checkoutData.partySize = e.target.value ? parseInt(e.target.value) : null;
};
}
if (addressInput) {
var geocodeDebounceTimer = null;
addressInput.oninput = function(e) {
checkoutData.address = e.target.value;
checkoutData.deliveryLocation = null;
selectedAddressFromList = false;
if (autocompleteService) {
searchAddresses(e.target.value);
} else if (GOOGLE_MAPS_API_KEY && !googleMapsLoaded) {
loadGoogleMaps();
}
// Debounced geocoding on each keystroke
if (geocodeDebounceTimer) clearTimeout(geocodeDebounceTimer);
if (checkoutData.address && checkoutData.address.length >= 5) {
geocodeDebounceTimer = setTimeout(function() {
if (!selectedAddressFromList) {
geocodeManualAddress(checkoutData.address);
}
}, 800);
}
};
// Removed auto-advance on blur - user controls via Save & Continue button
addressInput.onfocus = function() {
if (!autocompleteService && window.google && window.google.maps && window.google.maps.places) {
initGoogleServices();
}
};
addressInput.onblur = function() {
setTimeout(function() {
if (showAddressSuggestions) {
showAddressSuggestions = false;
var suggestionsEl = currentModalContent.querySelector('.address-autocomplete-suggestions');
if (suggestionsEl) suggestionsEl.remove();
}
}, 300);
};
addressInput.onkeydown = function(e) {
if (e.key === 'Enter') {
e.preventDefault();
if (geocodeDebounceTimer) clearTimeout(geocodeDebounceTimer);
if (checkoutData.address) {
geocodeManualAddress(checkoutData.address);
}
}
};
}
if (notesInput) notesInput.oninput = function(e) { checkoutData.notes = e.target.value; };
// Locate me button
var locateMeBtn = currentModalContent.querySelector('#locate-me-btn');
if (locateMeBtn) {
locateMeBtn.onclick = function(e) {
e.preventDefault();
handleLocateMe();
};
}
// Schedule buttons
var scheduleBtns = currentModalContent.querySelectorAll('.schedule-btn');
scheduleBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
if (btn.dataset.schedule === 'asap') {
checkoutData.timingType = 'asap';
checkoutData.scheduledTime = '';
// Auto-advance to contact when ASAP is selected
checkoutData.openSection = 'contact';
} else {
checkoutData.timingType = 'scheduled';
// Don't initialize date - keep section open for user to select both
checkoutData.scheduledTime = '';
}
renderMenu();
};
});
// Generate unified date/time picker
function renderUnifiedPicker() {
var calCont = currentModalContent.querySelector('#calendar-container');
var timeCont = currentModalContent.querySelector('#time-container');
if (!calCont || !timeCont) return;
var currentDate = checkoutData.scheduledTime ? new Date(checkoutData.scheduledTime.split('T')[0]) : new Date();
var year = currentDate.getFullYear();
var month = currentDate.getMonth();
// Store current view month
if (!window.pickerMonth) window.pickerMonth = month;
if (!window.pickerYear) window.pickerYear = year;
month = window.pickerMonth;
year = window.pickerYear;
// Render calendar
var monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var firstDay = new Date(year, month, 1);
var lastDay = new Date(year, month + 1, 0);
var daysInMonth = lastDay.getDate();
var startDay = firstDay.getDay();
var themeButtonColor = restaurant.theme_button_color || '#f97316';
var themeButtonTextColor = restaurant.theme_button_text_color || '#fff';
var dateSelectedClass = !checkoutData.scheduledTime || !checkoutData.scheduledTime.split('T')[0] ? ' base44-pulse-field' : '';
var calHtml = '
';
calHtml += '
';
calHtml += '
◀ ';
calHtml += '
' + monthNames[month] + ' ' + year + '
';
calHtml += '
▶ ';
calHtml += '
';
calHtml += '
';
var dayLabels = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
dayLabels.forEach(function(day) { calHtml += '
' + day + '
'; });
calHtml += '
';
calHtml += '
';
for (var i = 0; i < startDay; i++) { calHtml += '
'; }
var today = new Date();
today.setHours(0, 0, 0, 0);
for (var i = 1; i <= daysInMonth; i++) {
var dateStr = year + '-' + String(month + 1).padStart(2, '0') + '-' + String(i).padStart(2, '0');
var isSelected = checkoutData.scheduledTime && checkoutData.scheduledTime.startsWith(dateStr);
var checkDate = new Date(dateStr);
var isPast = checkDate < today;
calHtml += '
' + i + ' ';
}
calHtml += '
';
calCont.innerHTML = calHtml;
// Render times with AM/PM - fixed colors
var timeHtml = '';
var hasDateSelected = checkoutData.scheduledTime && checkoutData.scheduledTime.split('T')[0];
for (var h = 0; h < 24; h++) {
var hour24 = String(h).padStart(2, '0');
var hour12 = (h % 12 === 0) ? 12 : (h % 12);
var ampm = h < 12 ? 'AM' : 'PM';
var timeStr = hour24 + ':00';
var displayTime = hour12 + ':00 ' + ampm;
var isSelected = checkoutData.scheduledTime && checkoutData.scheduledTime.endsWith(timeStr);
var isDisabled = !hasDateSelected;
timeHtml += '
' + displayTime + ' ';
}
timeCont.innerHTML = timeHtml;
// Month navigation
var prevBtn = currentModalContent.querySelector('.month-prev-btn');
var nextBtn = currentModalContent.querySelector('.month-next-btn');
if (prevBtn) {
prevBtn.onclick = function(e) {
e.preventDefault();
window.pickerMonth--;
if (window.pickerMonth < 0) {
window.pickerMonth = 11;
window.pickerYear--;
}
renderUnifiedPicker();
};
}
if (nextBtn) {
nextBtn.onclick = function(e) {
e.preventDefault();
window.pickerMonth++;
if (window.pickerMonth > 11) {
window.pickerMonth = 0;
window.pickerYear++;
}
renderUnifiedPicker();
};
}
// Attach calendar listeners
var calBtns = currentModalContent.querySelectorAll('.cal-day-btn');
calBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
var dateVal = btn.dataset.date;
var currentTime = checkoutData.scheduledTime ? checkoutData.scheduledTime.split('T')[1] : '';
checkoutData.scheduledTime = dateVal + 'T' + currentTime;
// Auto-advance if both date AND time are selected AND not empty
var dateTimeParts = checkoutData.scheduledTime.split('T');
var hasDate = dateTimeParts[0] && dateTimeParts[0].trim().length > 0;
var hasTime = dateTimeParts[1] && dateTimeParts[1].trim().length > 0 && dateTimeParts[1].indexOf(':') > -1;
if (hasDate && hasTime) {
if (checkoutData.orderType === 'dine_in') {
checkoutData.openSection = 'dine_in_party';
} else {
checkoutData.openSection = 'contact';
}
}
renderUnifiedPicker();
renderMenu();
};
});
// Attach time listeners
var timeBtns = currentModalContent.querySelectorAll('.time-slot-btn');
timeBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
var timeVal = btn.dataset.time;
var currentDate = checkoutData.scheduledTime ? checkoutData.scheduledTime.split('T')[0] : '';
checkoutData.scheduledTime = currentDate + 'T' + timeVal;
// Auto-advance to next section when time selected
if (checkoutData.orderType === 'dine_in') {
checkoutData.openSection = 'dine_in_party';
} else {
checkoutData.openSection = 'contact';
}
renderUnifiedPicker();
renderMenu();
};
});
}
// Initialize unified picker when needed
var unifiedPicker = currentModalContent.querySelector('#unified-picker');
if (unifiedPicker) {
renderUnifiedPicker();
}
// Party size buttons
var partySizeBtns = currentModalContent.querySelectorAll('.party-size-btn');
partySizeBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var size = parseInt(btn.dataset.partySize);
checkoutData.partySize = size;
var partySizePicker = currentModalContent.querySelector('.party-size-picker');
if (partySizePicker) partySizePicker.classList.remove('base44-pulse-field');
// Auto-advance to contact info when party size is set
checkoutData.openSection = 'dine_in_contact';
renderMenu();
};
});
// Tip type buttons
var tipTypeBtns = currentModalContent.querySelectorAll('.tip-type-btn');
tipTypeBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
checkoutData.tipType = btn.dataset.tipType;
checkoutData.tipPercentage = 0;
checkoutData.tipFlatAmount = 0;
renderMenu();
};
});
// Tip percentage buttons
var tipPercentBtns = currentModalContent.querySelectorAll('.tip-percent-btn');
tipPercentBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
checkoutData.tipPercentage = parseInt(btn.dataset.percent) || 0;
checkoutData.openSection = 'payment';
renderMenu();
};
});
// Tip flat amount buttons
var tipFlatBtns = currentModalContent.querySelectorAll('.tip-flat-btn');
tipFlatBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
checkoutData.tipFlatAmount = parseFloat(btn.dataset.amount) || 0;
checkoutData.openSection = 'payment';
renderMenu();
};
});
// Custom tip amount input
var tipCustomInput = currentModalContent.querySelector('#tip-custom-amount');
if (tipCustomInput) {
tipCustomInput.onchange = function(e) {
checkoutData.tipFlatAmount = parseFloat(e.target.value) || 0;
checkoutData.openSection = 'payment';
renderMenu();
};
tipCustomInput.onblur = function(e) {
checkoutData.tipFlatAmount = parseFloat(e.target.value) || 0;
checkoutData.openSection = 'payment';
renderMenu();
};
}
// Inline My Orders button
var inlineMyOrdersBtn = currentModalContent.querySelector('#inline-my-orders-btn');
if (inlineMyOrdersBtn) {
inlineMyOrdersBtn.onclick = function(e) {
e.preventDefault();
openAccount();
};
}
// Promo code handlers
var promoInput = currentModalContent.querySelector('#promo-code-input');
if (promoInput) {
promoInput.oninput = function(e) { checkoutData.promoCode = e.target.value; };
}
var applyPromoBtn = currentModalContent.querySelector('#apply-promo-btn');
if (applyPromoBtn) {
applyPromoBtn.onclick = async function(e) {
e.preventDefault();
if (!checkoutData.promoCode || !checkoutData.promoCode.trim()) {
alert('Please enter a promo code');
return;
}
try {
var response = await fetch(BASE_URL + '/api/functions/validatePromo', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
promo_code: checkoutData.promoCode.trim(),
restaurant_id: RESTAURANT_ID,
subtotal: getCartTotal()
})
});
var data = await response.json();
if (data.success && data.promo) {
checkoutData.appliedPromo = data.promo;
alert('Promo code "' + data.promo.name + '" applied!');
checkoutData.openSection = 'payment';
renderMenu();
} else {
alert(data.error || 'Invalid promo code');
}
} catch (err) {
alert('Failed to validate promo code');
}
};
}
var removePromoBtn = currentModalContent.querySelector('#remove-promo-btn');
if (removePromoBtn) {
removePromoBtn.onclick = function(e) {
e.preventDefault();
checkoutData.appliedPromo = null;
checkoutData.promoCode = '';
renderMenu();
};
}
// Tip percentage slider
var tipSlider = currentModalContent.querySelector('#tip-percentage-slider');
if (tipSlider) {
tipSlider.oninput = function(e) {
checkoutData.tipPercentage = parseInt(e.target.value) || 0;
// Update slider background
var percent = (checkoutData.tipPercentage / 30) * 100;
tipSlider.style.background = 'linear-gradient(to right, #10b981 0%, #10b981 ' + percent + '%, #e5e7eb ' + percent + '%, #e5e7eb 100%)';
};
tipSlider.onchange = function(e) {
checkoutData.tipPercentage = parseInt(e.target.value) || 0;
checkoutData.openSection = 'payment';
renderMenu();
};
}
// Stripe pay button
var stripePayBtn = currentModalContent.querySelector('#stripe-pay-btn');
if (stripePayBtn) {
stripePayBtn.onclick = function(e) {
e.preventDefault();
if (!checkoutData.name || !checkoutData.phone || !checkoutData.email) {
alert('Please fill in your name, phone number, and email address');
return;
}
if (checkoutData.orderType === 'delivery' && !checkoutData.address) {
alert('Please enter your delivery address');
return;
}
if (checkoutData.orderType === 'delivery' && deliveryFeeData && !deliveryFeeData.success) {
alert('❌ ' + (deliveryFeeError || 'This address is outside our delivery range. Please choose a different address or select pickup.'));
return;
}
processStripePayment();
};
}
// Place order button
var placeOrderBtn = currentModalContent.querySelector('#place-order-btn');
if (placeOrderBtn) {
placeOrderBtn.onclick = function(e) {
e.preventDefault();
// Save all form values first
var nameVal = currentModalContent.querySelector('#checkout-name');
var phoneVal = currentModalContent.querySelector('#checkout-phone');
var emailVal = currentModalContent.querySelector('#checkout-email');
var addressVal = currentModalContent.querySelector('#checkout-address');
var notesVal = currentModalContent.querySelector('#checkout-notes');
var partySizeVal = currentModalContent.querySelector('#party-size-input');
var marketingVal = currentModalContent.querySelector('#checkout-marketing');
if (nameVal) checkoutData.name = nameVal.value;
if (phoneVal) checkoutData.phone = phoneVal.value;
if (emailVal) checkoutData.email = emailVal.value;
if (addressVal) checkoutData.address = addressVal.value;
if (notesVal) checkoutData.notes = notesVal.value;
if (partySizeVal) checkoutData.partySize = partySizeVal.value ? parseInt(partySizeVal.value) : null;
if (marketingVal) checkoutData.marketingOptIn = marketingVal.checked;
console.log('[Embedder] Place order clicked - orderType:', checkoutData.orderType, 'paymentMethod:', checkoutData.paymentMethod);
console.log('[Embedder] Form values - name:', checkoutData.name, 'phone:', checkoutData.phone, 'email:', checkoutData.email);
// Validate delivery address and fee
if (checkoutData.orderType === 'delivery') {
if (!checkoutData.address || checkoutData.address.length < 3) {
alert('Please enter a valid delivery address');
checkoutData.openSection = 'address';
renderMenu();
return;
}
if (!deliveryFeeData || !deliveryFeeData.success) {
alert('❌ ' + (deliveryFeeError || 'Please verify your delivery address is within our delivery range'));
checkoutData.openSection = 'address';
renderMenu();
return;
}
}
if (!checkoutData.paymentMethod) {
alert('Please select a payment method');
checkoutData.openSection = 'payment';
renderMenu();
return;
}
console.log('[Embedder] All validations passed, submitting order');
submitOrder();
};
}
// Account back button
var accountBackBtn = currentModalContent.querySelector('#account-back');
if (accountBackBtn) {
accountBackBtn.onclick = function(e) {
e.preventDefault();
closeAccount();
};
}
// Login button
var loginBtn = currentModalContent.querySelector('#login-btn');
if (loginBtn) {
loginBtn.onclick = function(e) {
e.preventDefault();
handleLogin();
};
}
// Confirmation back button
var confBackBtn = currentModalContent.querySelector('#conf-back-menu');
if (confBackBtn) {
confBackBtn.onclick = function(e) {
e.preventDefault();
closeConfirmation();
};
}
// Order again button
var confOrderAgainBtn = currentModalContent.querySelector('#conf-order-again');
if (confOrderAgainBtn) {
confOrderAgainBtn.onclick = function(e) {
e.preventDefault();
// Add order items back to cart
if (currentOrderData && currentOrderData.items) {
currentOrderData.items.forEach(function(orderItem) {
var menuItem = menuItems.find(function(m) { return m.id === orderItem.item_id || m.name === orderItem.item_name; });
if (menuItem) {
var cartItem = Object.assign({}, menuItem, {
quantity: orderItem.quantity,
cartId: Date.now() + Math.random(),
special_instructions: orderItem.special_instructions || '',
selected_options: {}
});
cart.push(cartItem);
}
});
saveCart();
}
closeConfirmation();
};
}
// Options modal
var optionsModalClose = currentModalContent.querySelector('#options-modal-close');
var optionsCancelBtn = currentModalContent.querySelector('#options-cancel-btn');
var optionsModalOverlay = currentModalContent.querySelector('#options-modal-overlay');
var optionsAddBtn = currentModalContent.querySelector('#options-add-btn');
if (optionsModalClose) {
optionsModalClose.onclick = function(e) {
e.preventDefault();
closeOptionsModal();
};
}
if (optionsCancelBtn) {
optionsCancelBtn.onclick = function(e) {
e.preventDefault();
closeOptionsModal();
};
}
if (optionsModalOverlay) {
optionsModalOverlay.onclick = function(e) {
if (e.target === optionsModalOverlay) closeOptionsModal();
};
}
if (optionsAddBtn) {
optionsAddBtn.onclick = function(e) {
e.preventDefault();
addItemWithOptions();
};
}
// Option choice buttons
var optionChoiceBtns = currentModalContent.querySelectorAll('.option-choice-btn');
optionChoiceBtns.forEach(function(btn) {
btn.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var groupName = btn.dataset.group;
var choiceName = btn.dataset.choice;
var type = btn.dataset.type;
console.log('[Embedder Options] Clicked:', groupName, choiceName, 'Type:', type);
console.log('[Embedder Options] Before - selectedOptions:', JSON.stringify(selectedOptions));
// Save current special instructions before updating state
var instructionsTextarea = currentModalContent.querySelector('#options-special-instructions');
if (instructionsTextarea) {
optionsSpecialInstructions = instructionsTextarea.value;
}
if (type === 'single_select') {
selectedOptions[groupName] = [choiceName];
} else if (type === 'multi_select') {
if (!selectedOptions[groupName]) selectedOptions[groupName] = [];
var idx = selectedOptions[groupName].indexOf(choiceName);
if (idx === -1) {
selectedOptions[groupName].push(choiceName);
} else {
selectedOptions[groupName].splice(idx, 1);
}
}
console.log('[Embedder Options] After - selectedOptions:', JSON.stringify(selectedOptions));
// Update UI without full re-render to prevent flickering
var buttonColor = restaurant.theme_button_color || '#f97316';
var buttonTextColor = restaurant.theme_button_text_color || '#fff';
// Get all buttons in this group and update styles
var groupBtns = currentModalContent.querySelectorAll('.option-choice-btn[data-group="' + groupName + '"]');
groupBtns.forEach(function(groupBtn) {
var btnChoice = groupBtn.dataset.choice;
var isNowSelected = false;
if (type === 'single_select') {
isNowSelected = selectedOptions[groupName] && selectedOptions[groupName][0] === btnChoice;
} else {
isNowSelected = selectedOptions[groupName] && selectedOptions[groupName].indexOf(btnChoice) !== -1;
}
// Update button styles
if (isNowSelected) {
groupBtn.style.background = buttonColor;
groupBtn.style.color = buttonTextColor;
groupBtn.style.borderColor = buttonColor;
// Update text spans to inherit button color
var textSpan = groupBtn.querySelector('.choice-text');
var priceSpan = groupBtn.querySelector('.choice-price');
if (textSpan) textSpan.style.color = buttonTextColor;
if (priceSpan) {
priceSpan.style.color = buttonTextColor;
priceSpan.style.opacity = '0.8';
}
} else {
groupBtn.style.background = '#f8fafc';
groupBtn.style.color = '#1e293b';
groupBtn.style.borderColor = '#e2e8f0';
// Reset text colors
var textSpan = groupBtn.querySelector('.choice-text');
var priceSpan = groupBtn.querySelector('.choice-price');
if (textSpan) textSpan.style.color = '#1e293b';
if (priceSpan) {
priceSpan.style.color = '#64748b';
priceSpan.style.opacity = '0.6';
}
}
});
// Calculate and update price - get base price from original menu item
var originalItem = menuItems.find(function(mi) { return mi.id === selectedItemForOptions.id; });
var newCalculatedPrice = originalItem ? originalItem.price : selectedItemForOptions.price;
if (selectedItemForOptions.options) {
selectedItemForOptions.options.forEach(function(optGroup) {
var selections = selectedOptions[optGroup.name] || [];
selections.forEach(function(selectedChoice) {
var choice = optGroup.choices.find(function(c) {
return c.name === selectedChoice;
});
if (choice && choice.price_modifier) {
newCalculatedPrice += choice.price_modifier;
}
});
});
}
var newTotalPrice = newCalculatedPrice * optionsQuantity;
var addBtn = currentModalContent.querySelector('#options-add-btn');
if (addBtn) {
var currencySymbol = getCurrencySymbol(restaurant.currency);
addBtn.textContent = 'Add to Cart - ' + currencySymbol + newTotalPrice.toFixed(2);
}
console.log('[Embedder Options] Price updated to:', newTotalPrice.toFixed(2));
};
});
// Options special instructions input - persist value
var optionsInstructionsInput = currentModalContent.querySelector('#options-special-instructions');
if (optionsInstructionsInput) {
optionsInstructionsInput.oninput = function(e) {
optionsSpecialInstructions = e.target.value;
};
}
// Options quantity buttons
var optionsQtyDecrease = currentModalContent.querySelector('#options-qty-decrease');
var optionsQtyIncrease = currentModalContent.querySelector('#options-qty-increase');
if (optionsQtyDecrease) {
optionsQtyDecrease.onclick = function(e) {
e.preventDefault();
if (optionsQuantity > 1) {
optionsQuantity--;
// Update quantity display and price without re-rendering
var qtyDisplay = currentModalContent.querySelector('#options-qty-decrease').nextElementSibling;
if (qtyDisplay) {
qtyDisplay.textContent = optionsQuantity;
}
// Recalculate and update price
var calculatedPrice = selectedItemForOptions.price;
if (selectedItemForOptions.options) {
selectedItemForOptions.options.forEach(function(optGroup) {
var selections = selectedOptions[optGroup.name] || [];
selections.forEach(function(selectedChoice) {
var choice = optGroup.choices.find(function(c) { return c.name === selectedChoice; });
if (choice && choice.price_modifier) {
calculatedPrice += choice.price_modifier;
}
});
});
}
var totalPrice = calculatedPrice * optionsQuantity;
var addBtn = currentModalContent.querySelector('#options-add-btn');
if (addBtn) {
addBtn.textContent = 'Add to Cart - ' + getCurrencySymbol(restaurant.currency) + totalPrice.toFixed(2);
}
// Update decrease button disabled state
optionsQtyDecrease.disabled = optionsQuantity <= 1;
optionsQtyDecrease.style.cursor = optionsQuantity <= 1 ? 'not-allowed' : 'pointer';
optionsQtyDecrease.style.opacity = optionsQuantity <= 1 ? '0.5' : '1';
}
};
}
if (optionsQtyIncrease) {
optionsQtyIncrease.onclick = function(e) {
e.preventDefault();
optionsQuantity++;
// Update quantity display and price without re-rendering
var qtyDisplay = currentModalContent.querySelector('#options-qty-increase').previousElementSibling;
if (qtyDisplay) {
qtyDisplay.textContent = optionsQuantity;
}
// Recalculate and update price
var calculatedPrice = selectedItemForOptions.price;
if (selectedItemForOptions.options) {
selectedItemForOptions.options.forEach(function(optGroup) {
var selections = selectedOptions[optGroup.name] || [];
selections.forEach(function(selectedChoice) {
var choice = optGroup.choices.find(function(c) { return c.name === selectedChoice; });
if (choice && choice.price_modifier) {
calculatedPrice += choice.price_modifier;
}
});
});
}
var totalPrice = calculatedPrice * optionsQuantity;
var addBtn = currentModalContent.querySelector('#options-add-btn');
if (addBtn) {
addBtn.textContent = 'Add to Cart - ' + getCurrencySymbol(restaurant.currency) + totalPrice.toFixed(2);
}
// Update decrease button enabled state
var decreaseBtn = currentModalContent.querySelector('#options-qty-decrease');
if (decreaseBtn) {
decreaseBtn.disabled = false;
decreaseBtn.style.cursor = 'pointer';
decreaseBtn.style.opacity = '1';
}
};
}
}
function createButton(element, restaurantId, restaurantName) {
var buttonColor = EMBEDDED_RESTAURANT?.embedder_button_color || '#fb923c';
var buttonText = EMBEDDED_RESTAURANT?.embedder_button_text || element.textContent || 'Order Online';
var buttonTextColor = EMBEDDED_RESTAURANT?.embedder_button_text_color || '#0f172a';
var borderRadius = typeof EMBEDDED_RESTAURANT?.embedder_button_border_radius === 'number' ? EMBEDDED_RESTAURANT.embedder_button_border_radius : 20;
var button = document.createElement('button');
button.style.cssText = 'display: inline-flex !important; align-items: center !important; gap: 0 !important; cursor: pointer !important; transition: all 0.2s !important; border: 2px solid ' + buttonColor + ' !important; background: transparent !important; padding: 0 !important; border-radius: ' + borderRadius + 'px !important; overflow: hidden !important; box-sizing: border-box !important; margin: 12px !important;';
var iconBox = document.createElement('div');
iconBox.style.cssText = 'display: flex !important; align-items: center !important; justify-content: center !important; width: 56px !important; height: 56px !important; background: linear-gradient(135deg, ' + buttonColor + ', ' + adjustColorBrightness(buttonColor, -15) + ') !important; flex-shrink: 0 !important; border-radius: 0 !important;';
iconBox.innerHTML = '
';
var textBox = document.createElement('div');
textBox.style.cssText = 'padding: 16px 24px !important; background: transparent !important; color: ' + buttonTextColor + ' !important; font-weight: 600 !important; font-size: 16px !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important; flex-shrink: 0 !important; border-radius: 0 !important;';
textBox.textContent = buttonText;
button.appendChild(iconBox);
button.appendChild(textBox);
button.onmouseover = function() {
button.style.transform = 'translateY(-2px)';
};
button.onmouseout = function() {
button.style.transform = 'translateY(0)';
};
button.onclick = async function(e) {
e.preventDefault();
await createModal(restaurantId, restaurantName);
};
element.parentNode.replaceChild(button, element);
}
function adjustColorBrightness(hex, percent) {
var num = parseInt(hex.replace('#', ''), 16);
var r = Math.max(0, Math.min(255, ((num >> 16) & 0xFF) + percent));
var g = Math.max(0, Math.min(255, ((num >> 8) & 0xFF) + percent));
var b = Math.max(0, Math.min(255, (num & 0xFF) + percent));
return '#' + ((r << 16) | (g << 8) | b).toString(16).padStart(6, '0');
}
function init() {
console.log('[Embedder INIT] Starting initialization - Mode:', DISPLAY_MODE);
console.log('[Embedder INIT] Restaurant ID:', RESTAURANT_ID);
console.log('[Embedder INIT] Document ready state:', document.readyState);
injectStyles();
// INLINE MODE - Render directly into container
if (DISPLAY_MODE === 'inline') {
console.log('[Embedder INIT] Inline mode detected');
var container = document.getElementById('base44-inline-menu');
if (!container) {
console.error('[Embedder INIT] Container #base44-inline-menu not found');
return;
}
console.log('[Embedder INIT] Container found, initializing inline menu');
if (FETCH_ERROR) {
container.innerHTML = '
⚠️ Failed to load menu: ' + FETCH_ERROR + '
';
return;
}
if (!EMBEDDED_RESTAURANT) {
container.innerHTML = '
⚠️ Restaurant not found
';
return;
}
if (!EMBEDDED_MENU_ITEMS || EMBEDDED_MENU_ITEMS.length === 0) {
container.innerHTML = '
No menu items available
';
return;
}
console.log('[Embedder INIT] Rendering inline menu with', EMBEDDED_MENU_ITEMS.length, 'items');
currentModalContent = container;
categories = getUniqueCategories();
renderMenu();
return;
}
// BUTTON MODE - Create buttons
if (RESTAURANT_ID) {
var elements = document.querySelectorAll('[data-base44-order-button]:not(.base44-initialized)');
console.log('[Embedder INIT] Found', elements.length, 'button elements');
elements.forEach(function(element, idx) {
console.log('[Embedder INIT] Button', idx, ':', element.tagName, element.textContent);
var restaurantName = element.getAttribute('data-restaurant-name') || '';
element.classList.add('base44-initialized');
createButton(element, RESTAURANT_ID, restaurantName);
console.log('[Embedder INIT] Button', idx, 'replaced successfully');
});
} else {
console.warn('[Embedder INIT] NO RESTAURANT_ID - cannot initialize buttons');
}
var elements2 = document.querySelectorAll('[data-base44-restaurant-id]:not(.base44-initialized)');
console.log('[Embedder INIT] Found', elements2.length, 'legacy button elements');
elements2.forEach(function(element) {
var restId = element.getAttribute('data-base44-restaurant-id');
var restaurantName = element.getAttribute('data-base44-restaurant-name');
if (restId) {
element.classList.add('base44-initialized');
createButton(element, restId, restaurantName);
}
});
console.log('[Embedder INIT] Initialization complete!');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
if (typeof MutationObserver !== 'undefined') {
var observer = new MutationObserver(function(mutations) {
var hasNewElements = mutations.some(function(mutation) {
return Array.from(mutation.addedNodes).some(function(node) {
return node.nodeType === 1 && (
node.hasAttribute('data-base44-restaurant-id') ||
node.hasAttribute('data-base44-order-button') ||
(node.querySelector && (node.querySelector('[data-base44-restaurant-id]') || node.querySelector('[data-base44-order-button]')))
);
});
});
if (hasNewElements) init();
});
observer.observe(document.body, { childList: true, subtree: true });
}
window.Base44Embedder = {
version: '3.4.0',
openMenu: createModal,
init: init
};
console.log('[Embedder] >>> Script loaded - version 3.4.0');
})();