DeepLearning101 commited on
Commit
bba7a48
·
verified ·
1 Parent(s): 946dd5a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +31 -34
app.py CHANGED
@@ -5,7 +5,6 @@ from supabase import create_client, Client
5
  from datetime import datetime, timedelta, timezone
6
 
7
  # --- 1. 設定與初始化 ---
8
- # ✅ 補回:設定台北時區 (UTC+8)
9
  TAIPEI_TZ = timezone(timedelta(hours=8))
10
 
11
  LINE_ACCESS_TOKEN = os.getenv("LINE_ACCESS_TOKEN")
@@ -16,17 +15,16 @@ SUPABASE_KEY = os.getenv("SUPABASE_KEY")
16
  if SUPABASE_URL and SUPABASE_KEY:
17
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
18
 
19
- # --- 2. 座位控管設定 (老闆可在此修改) ---
20
  DEFAULT_LIMIT = 30
21
  SPECIAL_DAYS = {
22
- "2026-12-31": 10, # 跨年夜
23
- "2026-02-14": 15 # 情人節
24
  }
25
 
26
  # --- 3. 輔助函式 ---
27
  def get_date_options():
28
  options = []
29
- # ✅ 修正:使用台北時間
30
  today = datetime.now(TAIPEI_TZ)
31
  weekdays = ["(一)", "(二)", "(三)", "(四)", "(五)", "(六)", "(日)"]
32
  for i in range(30):
@@ -47,7 +45,6 @@ def update_time_slots(date_str):
47
  "21:00", "21:30", "22:00", "22:30", "23:00", "23:30", "00:00", "00:30", "01:00", "01:30"]
48
  if weekday == 4 or weekday == 5: slots.extend(["02:00", "02:30"])
49
 
50
- # 檢查剩餘座位
51
  clean_date = date_str.split(" ")[0]
52
  daily_limit = SPECIAL_DAYS.get(clean_date, DEFAULT_LIMIT)
53
 
@@ -60,20 +57,16 @@ def update_time_slots(date_str):
60
 
61
  return gr.update(choices=slots, value=slots[0] if slots else None), status_msg
62
 
63
- # --- 4. 核心邏輯 ---
64
  def get_line_id_from_url(request: gr.Request):
65
- """從網址參數讀取 line_id"""
66
  if request:
67
  return request.query_params.get("line_id", "")
68
  return ""
69
 
70
  def handle_booking(name, tel, email, date_str, time, pax, remarks, line_id):
71
- # 👇👇👇 加入這一行除錯 👇👇👇
72
- # print(f"🔥 DEBUG: 收到訂單,Name={name}, Line_ID={line_id}")
73
  if not name or not tel or not date_str or not time:
74
  return "⚠️ 請完整填寫必填欄位"
75
 
76
- # A. 檢查座位上限
77
  clean_date = date_str.split(" ")[0]
78
  daily_limit = SPECIAL_DAYS.get(clean_date, DEFAULT_LIMIT)
79
  try:
@@ -83,29 +76,23 @@ def handle_booking(name, tel, email, date_str, time, pax, remarks, line_id):
83
  return "⚠️ 抱歉,該時段剩餘座位不足,請調整人數或日期。"
84
  except: pass
85
 
86
- # B. 防重複提交
87
  try:
88
  existing = supabase.table("bookings").select("id").eq("tel", tel).eq("date", date_str).eq("time", time).neq("status", "顧客已取消").execute()
89
  if existing.data: return "⚠️ 您已預約過此時段,請勿重複提交。"
90
  except: pass
91
 
92
- # C. 寫入資料庫
93
  data = {
94
  "name": name, "tel": tel, "email": email, "date": date_str, "time": time,
95
  "pax": pax, "remarks": remarks, "status": "待處理",
96
- "user_id": line_id # 存入 LINE ID
97
  }
98
 
99
  try:
100
  supabase.table("bookings").insert(data).execute()
101
 
102
- # D. 發送 LINE Notify 給老闆 (⚠️ 這裡修改了格式)
103
  if LINE_ACCESS_TOKEN and LINE_ADMIN_ID:
104
  src = "🟢 LINE用戶" if line_id else "⚪ 訪客"
105
- # 處理備註:如果是空字串顯示 "無"
106
  note = remarks if remarks else "無"
107
-
108
- # 👇 新的漂亮格式 👇
109
  msg = (
110
  f"🔥 新訂位 ({src})\n"
111
  f"姓名:{name}\n"
@@ -120,46 +107,56 @@ def handle_booking(name, tel, email, date_str, time, pax, remarks, line_id):
120
  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>"""
121
  except Exception as e: return f"❌ 系統錯誤: {str(e)}"
122
 
123
- # --- 5. Webhook (確認/取消邏輯 + 自動轉址到首頁) ---
 
124
  def check_confirmation(request: gr.Request):
125
  if not request: return ""
126
  action = request.query_params.get('action')
127
  bid = request.query_params.get('id')
128
 
129
- # ✅ 修改這裡:目標改為官網首頁 (index.html)
130
  OFFICIAL_SITE = "https://ciecietaipei.github.io/index.html"
131
 
132
  if action == 'confirm' and bid:
133
  try:
134
  supabase.table("bookings").update({"status": "顧客已確認"}).eq("id", bid).execute()
135
- # 成功後,跳轉回首頁並帶上 status=confirmed
136
- return f"""<script>window.location.href = "{OFFICIAL_SITE}?status=confirmed";</script>"""
137
- except:
138
- return "系統錯誤"
139
 
140
  elif action == 'cancel' and bid:
141
  try:
142
  supabase.table("bookings").update({"status": "顧客已取消"}).eq("id", bid).execute()
143
- # 取消後,跳轉回首頁並帶上 status=canceled
144
- return f"""<script>window.location.href = "{OFFICIAL_SITE}?status=canceled";</script>"""
145
- except:
146
- return "系統錯誤"
147
 
148
  return ""
149
 
150
  # --- 6. 介面 ---
151
  theme = gr.themes.Soft(primary_hue="amber", neutral_hue="zinc").set(body_background_fill="#0F0F0F", block_background_fill="#1a1a1a", block_border_width="1px", block_border_color="#333", input_background_fill="#262626", input_border_color="#444", body_text_color="#E0E0E0", block_title_text_color="#d4af37", button_primary_background_fill="#d4af37", button_primary_text_color="#000000")
152
 
153
- # 修改 1:在 CSS 最後面加上 #hidden_box 的隱藏規則
154
  custom_css = "footer {display: none !important;} .gradio-container, .block, .row, .column { overflow: visible !important; } .options, .wrap .options { background-color: #262626 !important; border: 1px solid #d4af37 !important; z-index: 10000 !important; box-shadow: 0 5px 15px rgba(0,0,0,0.5); } .item:hover, .options .item:hover { background-color: #d4af37 !important; color: black !important; } .legal-footer { text-align: center; margin-top: 15px; padding-top: 15px; border-top: 1px solid #333; color: #666; font-size: 0.75rem; } #hidden_box { display: none !important; }"
155
 
156
-
157
  with gr.Blocks(theme=theme, css=custom_css, title="Booking") as demo:
158
- # 修改 2:保持 visible=True (確保資料流通),但加上 elem_id 讓 CSS 把它藏起來
159
- line_id_box = gr.Textbox(visible=True, elem_id="hidden_box", label="LINE ID")
160
- confirm_msg_box = gr.HTML()
 
 
 
 
161
  demo.load(get_line_id_from_url, None, line_id_box)
162
- demo.load(check_confirmation, None, confirm_msg_box)
 
 
 
 
 
 
 
 
163
 
164
  with gr.Row():
165
  with gr.Column():
 
5
  from datetime import datetime, timedelta, timezone
6
 
7
  # --- 1. 設定與初始化 ---
 
8
  TAIPEI_TZ = timezone(timedelta(hours=8))
9
 
10
  LINE_ACCESS_TOKEN = os.getenv("LINE_ACCESS_TOKEN")
 
15
  if SUPABASE_URL and SUPABASE_KEY:
16
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
17
 
18
+ # --- 2. 座位控管設定 ---
19
  DEFAULT_LIMIT = 30
20
  SPECIAL_DAYS = {
21
+ "2026-12-31": 10,
22
+ "2026-02-14": 15
23
  }
24
 
25
  # --- 3. 輔助函式 ---
26
  def get_date_options():
27
  options = []
 
28
  today = datetime.now(TAIPEI_TZ)
29
  weekdays = ["(一)", "(二)", "(三)", "(四)", "(五)", "(六)", "(日)"]
30
  for i in range(30):
 
45
  "21:00", "21:30", "22:00", "22:30", "23:00", "23:30", "00:00", "00:30", "01:00", "01:30"]
46
  if weekday == 4 or weekday == 5: slots.extend(["02:00", "02:30"])
47
 
 
48
  clean_date = date_str.split(" ")[0]
49
  daily_limit = SPECIAL_DAYS.get(clean_date, DEFAULT_LIMIT)
50
 
 
57
 
58
  return gr.update(choices=slots, value=slots[0] if slots else None), status_msg
59
 
60
+ # --- 4. 核心邏輯 (抓 ID 與 處理訂位) ---
61
  def get_line_id_from_url(request: gr.Request):
 
62
  if request:
63
  return request.query_params.get("line_id", "")
64
  return ""
65
 
66
  def handle_booking(name, tel, email, date_str, time, pax, remarks, line_id):
 
 
67
  if not name or not tel or not date_str or not time:
68
  return "⚠️ 請完整填寫必填欄位"
69
 
 
70
  clean_date = date_str.split(" ")[0]
71
  daily_limit = SPECIAL_DAYS.get(clean_date, DEFAULT_LIMIT)
72
  try:
 
76
  return "⚠️ 抱歉,該時段剩餘座位不足,請調整人數或日期。"
77
  except: pass
78
 
 
79
  try:
80
  existing = supabase.table("bookings").select("id").eq("tel", tel).eq("date", date_str).eq("time", time).neq("status", "顧客已取消").execute()
81
  if existing.data: return "⚠️ 您已預約過此時段,請勿重複提交。"
82
  except: pass
83
 
 
84
  data = {
85
  "name": name, "tel": tel, "email": email, "date": date_str, "time": time,
86
  "pax": pax, "remarks": remarks, "status": "待處理",
87
+ "user_id": line_id
88
  }
89
 
90
  try:
91
  supabase.table("bookings").insert(data).execute()
92
 
 
93
  if LINE_ACCESS_TOKEN and LINE_ADMIN_ID:
94
  src = "🟢 LINE用戶" if line_id else "⚪ 訪客"
 
95
  note = remarks if remarks else "無"
 
 
96
  msg = (
97
  f"🔥 新訂位 ({src})\n"
98
  f"姓名:{name}\n"
 
107
  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>"""
108
  except Exception as e: return f"❌ 系統錯誤: {str(e)}"
109
 
110
+ # --- 5. Webhook (確認/取消 + 回傳轉址 URL) ---
111
+ # ⚠️ 改動:這裡只回傳「純網址字串」,不回傳 script 標籤
112
  def check_confirmation(request: gr.Request):
113
  if not request: return ""
114
  action = request.query_params.get('action')
115
  bid = request.query_params.get('id')
116
 
117
+ # 目標官網首頁
118
  OFFICIAL_SITE = "https://ciecietaipei.github.io/index.html"
119
 
120
  if action == 'confirm' and bid:
121
  try:
122
  supabase.table("bookings").update({"status": "顧客已確認"}).eq("id", bid).execute()
123
+ # 回傳目標網址
124
+ return f"{OFFICIAL_SITE}?status=confirmed"
125
+ except: pass
 
126
 
127
  elif action == 'cancel' and bid:
128
  try:
129
  supabase.table("bookings").update({"status": "顧客已取消"}).eq("id", bid).execute()
130
+ # 回傳目標網址
131
+ return f"{OFFICIAL_SITE}?status=canceled"
132
+ except: pass
 
133
 
134
  return ""
135
 
136
  # --- 6. 介面 ---
137
  theme = gr.themes.Soft(primary_hue="amber", neutral_hue="zinc").set(body_background_fill="#0F0F0F", block_background_fill="#1a1a1a", block_border_width="1px", block_border_color="#333", input_background_fill="#262626", input_border_color="#444", body_text_color="#E0E0E0", block_title_text_color="#d4af37", button_primary_background_fill="#d4af37", button_primary_text_color="#000000")
138
 
139
+ # CSS 隱藏技巧 (保留 line_id_box 的隱藏設定)
140
  custom_css = "footer {display: none !important;} .gradio-container, .block, .row, .column { overflow: visible !important; } .options, .wrap .options { background-color: #262626 !important; border: 1px solid #d4af37 !important; z-index: 10000 !important; box-shadow: 0 5px 15px rgba(0,0,0,0.5); } .item:hover, .options .item:hover { background-color: #d4af37 !important; color: black !important; } .legal-footer { text-align: center; margin-top: 15px; padding-top: 15px; border-top: 1px solid #333; color: #666; font-size: 0.75rem; } #hidden_box { display: none !important; }"
141
 
 
142
  with gr.Blocks(theme=theme, css=custom_css, title="Booking") as demo:
143
+ # 接收 LINE ID 的盒子 (CSS 隱藏)
144
+ line_id_box = gr.Textbox(visible=True, elem_id="hidden_box", label="LINE ID")
145
+
146
+ # ⚠️ 新增:接收轉址 URL 的盒子 (隱藏)
147
+ redirect_url_box = gr.Textbox(visible=False)
148
+
149
+ # 載入時觸發
150
  demo.load(get_line_id_from_url, None, line_id_box)
151
+ demo.load(check_confirmation, None, redirect_url_box)
152
+
153
+ # ⚠️ 關鍵:當 redirect_url_box 有值時,執行 JS 轉址
154
+ redirect_url_box.change(
155
+ fn=None,
156
+ inputs=redirect_url_box,
157
+ outputs=None,
158
+ js="(url) => { if(url) window.location.href = url; }"
159
+ )
160
 
161
  with gr.Row():
162
  with gr.Column():