import gradio as gr from datetime import datetime, timedelta import os import requests from supabase import create_client, Client # --- 連線設定 --- LINE_ACCESS_TOKEN = os.getenv("LINE_ACCESS_TOKEN") LINE_ADMIN_ID = os.getenv("LINE_ADMIN_ID") SUPABASE_URL = os.getenv("SUPABASE_URL") SUPABASE_KEY = os.getenv("SUPABASE_KEY") supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) # --- LINE 通知函式 (更新版:包含備註) --- def send_line_notify(data): if not LINE_ACCESS_TOKEN or not LINE_ADMIN_ID: return msg_body = [ {"type": "text", "text": "姓名", "color": "#aaaaaa", "size": "sm", "flex": 2}, {"type": "text", "text": data['name'], "wrap": True, "color": "#666666", "size": "sm", "flex": 5}, {"type": "text", "text": "電話", "color": "#aaaaaa", "size": "sm", "flex": 2}, {"type": "text", "text": data['tel'], "wrap": True, "color": "#666666", "size": "sm", "flex": 5}, {"type": "text", "text": "Email", "color": "#aaaaaa", "size": "sm", "flex": 2}, {"type": "text", "text": data.get('email', '-'), "wrap": True, "color": "#666666", "size": "sm", "flex": 5}, {"type": "text", "text": "備註", "color": "#aaaaaa", "size": "sm", "flex": 2}, {"type": "text", "text": data.get('remarks', '無'), "wrap": True, "color": "#666666", "size": "sm", "flex": 5} ] # ... (此處省略部分重複的 Flex Message 結構,重點是把上面的 msg_body 塞進去) ... # 為了節省篇幅,這裡直接發送簡單通知,您可以用之前的 Flex Message 結構替換 requests.post( "https://api.line.me/v2/bot/message/push", headers={"Authorization": f"Bearer {LINE_ACCESS_TOKEN}", "Content-Type": "application/json"}, json={"to": LINE_ADMIN_ID, "messages": [{"type": "text", "text": f"🔥 新訂位:{data['name']} ({data['pax']}人)\n時間:{data['date']} {data['time']}\n備註:{data.get('remarks', '無')}"}]} ) # --- 核心邏輯 --- def handle_booking(name, tel, email, date_str, time, pax, remarks): if not name or not tel or not date_str or not time: return "⚠️ 請完整填寫必填欄位" data = { "name": name, "tel": tel, "email": email, "date": date_str, "time": time, "pax": pax, "remarks": remarks, "status": "待處理" } try: # 寫入資料庫 supabase.table("bookings").insert(data).execute() # 發送 LINE send_line_notify(data) return """

Request Received

🥂 預約申請已提交,請留意確認信。

""" except Exception as e: return f"❌ 系統錯誤: {str(e)}" # --- 處理確認連結 (Webhook) --- def check_confirmation(request: gr.Request): """ 當網址帶有 ?id=xx&action=confirm 時觸發 """ if not request: return "" params = request.query_params action = params.get('action') bid = params.get('id') if action == 'confirm' and bid: try: # 更新資料庫狀態 supabase.table("bookings").update({"status": "顧客已確認"}).eq("id", bid).execute() return f"""
🎉 感謝您的確認!我們期待您的光臨。
""" except: return "" return "" # --- 介面佈局 --- # 主題與 CSS 設定 (沿用之前的) ... with gr.Blocks(theme=gr.themes.Soft(), title="Booking") as demo: # 隱藏的 HTML 區塊,用來接收確認訊息 confirm_msg_box = gr.HTML() # 頁面載入時檢查網址參數 demo.load(check_confirmation, inputs=None, outputs=confirm_msg_box) # ... (Header 與 日期選擇區塊 保持不變) ... gr.Markdown("### 👤 聯絡人資料 Contact") with gr.Group(): with gr.Row(): cust_name = gr.Textbox(label="訂位姓名 *", placeholder="ex. 王小明") cust_tel = gr.Textbox(label="手機號碼 *", placeholder="ex. 0912-xxx-xxx") with gr.Row(): cust_email = gr.Textbox(label="電子信箱 (接收確認信用)", placeholder="example@gmail.com") with gr.Row(): cust_remarks = gr.Textbox(label="備註 (過敏/慶生/特殊需求)", lines=2) submit_btn = gr.Button("確認預約 Request Booking", variant="primary") output_msg = gr.HTML() # ... (Event Listeners) ... submit_btn.click( handle_booking, inputs=[cust_name, cust_tel, cust_email, booking_date, time_slot, pax_count, cust_remarks], outputs=output_msg ) demo.launch()