Update app.py
Browse files
app.py
CHANGED
|
@@ -85,13 +85,13 @@ def get_line_id_from_url(request: gr.Request):
|
|
| 85 |
def handle_booking(name, tel, email, date_str, time, pax, remarks, line_id):
|
| 86 |
# 1. 基本必填檢查
|
| 87 |
if not name or not tel or not date_str or not time:
|
| 88 |
-
return "⚠️ 請完整填寫必填欄位 (姓名、電話、日期、時間)"
|
| 89 |
|
| 90 |
# 🔥🔥🔥 [新增] 格式驗證邏輯 🔥🔥🔥
|
| 91 |
|
| 92 |
# 2. Email 驗證 (如果用戶有填寫 Email,就必須包含 @)
|
| 93 |
if email and "@" not in email:
|
| 94 |
-
return "⚠️ Email 格式錯誤 (請確認包含 @ 符號)"
|
| 95 |
|
| 96 |
# 3. 電話驗證 (寬鬆模式:相容外國人)
|
| 97 |
# 邏輯:不限制符號 (+, -, 空白, 括號都可),但「數字」的總數至少要有 6 碼
|
|
@@ -99,7 +99,7 @@ def handle_booking(name, tel, email, date_str, time, pax, remarks, line_id):
|
|
| 99 |
# 例如: "Test Phone" (數字0碼) -> 失敗
|
| 100 |
digit_count = sum(c.isdigit() for c in tel)
|
| 101 |
if digit_count < 6:
|
| 102 |
-
return "⚠️ 電話號碼格式錯誤 (請填寫有效的電話號碼)"
|
| 103 |
|
| 104 |
# 🔥🔥🔥 驗證結束 🔥🔥🔥
|
| 105 |
|
|
@@ -109,12 +109,12 @@ def handle_booking(name, tel, email, date_str, time, pax, remarks, line_id):
|
|
| 109 |
res = supabase.table("bookings").select("pax").eq("date", date_str).neq("status", "顧客已取消").execute()
|
| 110 |
current_total = sum([item['pax'] for item in res.data])
|
| 111 |
if current_total + pax > daily_limit:
|
| 112 |
-
return "⚠️ 抱歉,該時段剩餘座位不足,請調整人數或日期。"
|
| 113 |
except: pass
|
| 114 |
|
| 115 |
try:
|
| 116 |
existing = supabase.table("bookings").select("id").eq("tel", tel).eq("date", date_str).eq("time", time).neq("status", "顧客已取消").execute()
|
| 117 |
-
if existing.data: return "⚠️ 您已預約過此時段,請勿重複提交。"
|
| 118 |
except: pass
|
| 119 |
|
| 120 |
data = {
|
|
@@ -141,8 +141,14 @@ def handle_booking(name, tel, email, date_str, time, pax, remarks, line_id):
|
|
| 141 |
)
|
| 142 |
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": msg}]})
|
| 143 |
|
| 144 |
-
|
| 145 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
|
| 147 |
# --- 5. Webhook (確認/取消 + 回傳轉址 URL + 🔥通知老闆) ---
|
| 148 |
def check_confirmation(request: gr.Request):
|
|
@@ -210,10 +216,18 @@ custom_css = "footer {display: none !important;} .gradio-container, .block, .row
|
|
| 210 |
with gr.Blocks(theme=theme, css=custom_css, title="Booking") as demo:
|
| 211 |
line_id_box = gr.Textbox(visible=True, elem_id="hidden_box", label="LINE ID")
|
| 212 |
redirect_url_box = gr.Textbox(visible=False)
|
|
|
|
| 213 |
|
| 214 |
demo.load(get_line_id_from_url, None, line_id_box)
|
| 215 |
demo.load(check_confirmation, None, redirect_url_box)
|
| 216 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
redirect_url_box.change(
|
| 218 |
fn=None,
|
| 219 |
inputs=redirect_url_box,
|
|
@@ -250,7 +264,7 @@ with gr.Blocks(theme=theme, css=custom_css, title="Booking") as demo:
|
|
| 250 |
gr.HTML("""<div class="legal-footer"><p style="margin-bottom: 5px;">© 2026 CIE CIE TAIPEI. All Rights Reserved.</p><p>內容涉及酒類產品訊息,請勿轉發分享給未達法定購買年齡者;未滿十八歲請勿飲酒。<br><strong>喝酒不開車,開車不喝酒。</strong></p></div>""")
|
| 251 |
|
| 252 |
booking_date.change(update_time_slots, inputs=booking_date, outputs=[time_slot, status_box])
|
| 253 |
-
submit_btn.click(handle_booking, inputs=[cust_name, cust_tel, cust_email, booking_date, time_slot, pax_count, cust_remarks, line_id_box], outputs=output_msg)
|
| 254 |
|
| 255 |
if __name__ == "__main__":
|
| 256 |
demo.launch()
|
|
|
|
| 85 |
def handle_booking(name, tel, email, date_str, time, pax, remarks, line_id):
|
| 86 |
# 1. 基本必填檢查
|
| 87 |
if not name or not tel or not date_str or not time:
|
| 88 |
+
return "⚠️ 請完整填寫必填欄位 (姓名、電話、日期、時間)", ""
|
| 89 |
|
| 90 |
# 🔥🔥🔥 [新增] 格式驗證邏輯 🔥🔥🔥
|
| 91 |
|
| 92 |
# 2. Email 驗證 (如果用戶有填寫 Email,就必須包含 @)
|
| 93 |
if email and "@" not in email:
|
| 94 |
+
return "⚠️ Email 格式錯誤 (請確認包含 @ 符號)", ""
|
| 95 |
|
| 96 |
# 3. 電話驗證 (寬鬆模式:相容外國人)
|
| 97 |
# 邏輯:不限制符號 (+, -, 空白, 括號都可),但「數字」的總數至少要有 6 碼
|
|
|
|
| 99 |
# 例如: "Test Phone" (數字0碼) -> 失敗
|
| 100 |
digit_count = sum(c.isdigit() for c in tel)
|
| 101 |
if digit_count < 6:
|
| 102 |
+
return "⚠️ 電話號碼格式錯誤 (請填寫有效的電話號碼)", ""
|
| 103 |
|
| 104 |
# 🔥🔥🔥 驗證結束 🔥🔥🔥
|
| 105 |
|
|
|
|
| 109 |
res = supabase.table("bookings").select("pax").eq("date", date_str).neq("status", "顧客已取消").execute()
|
| 110 |
current_total = sum([item['pax'] for item in res.data])
|
| 111 |
if current_total + pax > daily_limit:
|
| 112 |
+
return "⚠️ 抱歉,該時段剩餘座位不足,請調整人數或日期。", ""
|
| 113 |
except: pass
|
| 114 |
|
| 115 |
try:
|
| 116 |
existing = supabase.table("bookings").select("id").eq("tel", tel).eq("date", date_str).eq("time", time).neq("status", "顧客已取消").execute()
|
| 117 |
+
if existing.data: return "⚠️ 您已預約過此時段,請勿重複提交。", ""
|
| 118 |
except: pass
|
| 119 |
|
| 120 |
data = {
|
|
|
|
| 141 |
)
|
| 142 |
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": msg}]})
|
| 143 |
|
| 144 |
+
html_response = """<div style='text-align: center; color: #fff; padding: 20px; border: 1px solid #d4af37; border-radius: 8px; background: #222;'><h2 style='color: #d4af37; margin: 0;'>Request Received</h2><p style='margin: 10px 0;'>🥂 預約申請已提交</p><p style='font-size: 0.9em; color: #aaa;'>請留意 Email 確認信 或 Line 訊息。</p></div>"""
|
| 145 |
+
|
| 146 |
+
# 👇 成功時,第二個參數回傳 "success"
|
| 147 |
+
return html_response, "success"
|
| 148 |
+
|
| 149 |
+
# return """<div style='text-align: center; color: #fff; padding: 20px; border: 1px solid #d4af37; border-radius: 8px; background: #222;'><h2 style='color: #d4af37; margin: 0;'>Request Received</h2><p style='margin: 10px 0;'>🥂 預約申請已提交</p><p style='font-size: 0.9em; color: #aaa;'>請留意 Email 確認信 或 Line 訊息。</p></div>"""
|
| 150 |
+
|
| 151 |
+
except Exception as e: return f"❌ 系統錯誤: {str(e)}", ""
|
| 152 |
|
| 153 |
# --- 5. Webhook (確認/取消 + 回傳轉址 URL + 🔥通知老闆) ---
|
| 154 |
def check_confirmation(request: gr.Request):
|
|
|
|
| 216 |
with gr.Blocks(theme=theme, css=custom_css, title="Booking") as demo:
|
| 217 |
line_id_box = gr.Textbox(visible=True, elem_id="hidden_box", label="LINE ID")
|
| 218 |
redirect_url_box = gr.Textbox(visible=False)
|
| 219 |
+
ga4_trigger_box = gr.Textbox(visible=False)
|
| 220 |
|
| 221 |
demo.load(get_line_id_from_url, None, line_id_box)
|
| 222 |
demo.load(check_confirmation, None, redirect_url_box)
|
| 223 |
|
| 224 |
+
ga4_trigger_box.change(
|
| 225 |
+
fn=None,
|
| 226 |
+
inputs=ga4_trigger_box,
|
| 227 |
+
outputs=None,
|
| 228 |
+
js="(val) => { if(val === 'success' && window.parent) { window.parent.postMessage('booking_success', '*'); } }"
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
redirect_url_box.change(
|
| 232 |
fn=None,
|
| 233 |
inputs=redirect_url_box,
|
|
|
|
| 264 |
gr.HTML("""<div class="legal-footer"><p style="margin-bottom: 5px;">© 2026 CIE CIE TAIPEI. All Rights Reserved.</p><p>內容涉及酒類產品訊息,請勿轉發分享給未達法定購買年齡者;未滿十八歲請勿飲酒。<br><strong>喝酒不開車,開車不喝酒。</strong></p></div>""")
|
| 265 |
|
| 266 |
booking_date.change(update_time_slots, inputs=booking_date, outputs=[time_slot, status_box])
|
| 267 |
+
submit_btn.click(handle_booking, inputs=[cust_name, cust_tel, cust_email, booking_date, time_slot, pax_count, cust_remarks, line_id_box], outputs=[output_msg, ga4_trigger_box])
|
| 268 |
|
| 269 |
if __name__ == "__main__":
|
| 270 |
demo.launch()
|