DeepLearning101 commited on
Commit
16c25fa
·
verified ·
1 Parent(s): dbcfe96

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -52
app.py CHANGED
@@ -29,37 +29,48 @@ def get_bookings():
29
  except:
30
  return pd.DataFrame()
31
 
32
- # 🔥🔥🔥 核心邏輯:自動判斷是「確認信」還是「提醒信」 🔥🔥🔥
33
  def send_confirmation_hybrid(booking_id):
34
  if not booking_id: return "❌ 無效 ID"
35
  try:
 
36
  res = supabase.table("bookings").select("*").eq("id", booking_id).execute()
37
  if not res.data: return "❌ 找不到訂單"
38
  booking = res.data[0]
39
- email, user_id = booking.get('email'), booking.get('user_id')
40
- current_status = booking.get('status', '')
41
 
42
- # 判斷是否為「已確認」狀態
43
- is_confirmed = "確認" in current_status
44
-
45
- log_msg = f"🆔 {booking_id}: "
46
 
47
  confirm_link = f"{PUBLIC_SPACE_URL}/?id={booking_id}&action=confirm"
48
  cancel_link = f"{PUBLIC_SPACE_URL}/?id={booking_id}&action=cancel"
 
 
49
 
50
- # --- 內容生成邏輯 ---
51
- if is_confirmed:
52
- # 🔔 提醒模式 (Reminder)
53
- mail_subject = f"🥂 訂位提醒: {booking['date']} - Cié Cié Taipei"
54
- line_text = f"🔔 訂位提醒\n\n{booking['name']} 您好,期待今晚與您相見!\n\n📅 日期:{booking['date']}\n⏰ 時間:{booking['time']}\n👥 人數:{booking['pax']}位\n\n交通資訊:https://g.co/kgs/xxxx"
 
 
55
 
56
- # 提醒信 HTML (沒有確認按鈕,語氣為期待光臨)
 
 
 
 
 
 
 
 
 
 
57
  mail_html = f"""
58
  <div style="background:#1a1a1a; color:#d4af37; padding:20px; border-radius:8px; font-family:sans-serif; border:1px solid #333;">
59
  <h2 style="text-align:center; border-bottom:1px solid #444; padding-bottom:15px;">Cié Cié Taipei</h2>
60
- <p style="color:#eee; font-size:16px;"><strong>{booking['name']}</strong> 您好,期待您的光臨!</p>
61
- <p style="color:#aaa;">這是一封貼心提醒,我們已為您準備好座位:</p>
62
- <div style="background:#2a2a2a; padding:15px; border-radius:8px; margin:20px 0; border-left:4px solid #2ecc71;">
63
  <ul style="color:#ddd; padding-left:20px; line-height:1.8;">
64
  <li>📅 日期:<strong style="color:#fff;">{booking['date']}</strong></li>
65
  <li>⏰ 時間:<strong style="color:#fff;">{booking['time']}</strong></li>
@@ -67,22 +78,34 @@ def send_confirmation_hybrid(booking_id):
67
  </ul>
68
  </div>
69
  <div style="text-align:center; margin-top:30px;">
70
- <span style="color:#888; font-size:0.9em;">若您行程有變,請點擊下方按鈕</span><br><br>
71
- <a href="{cancel_link}" style="display:inline-block; border:1px solid #555; color:#aaa; padding:10px 25px; text-decoration:none; border-radius:50px;">🚫 取消或更改</a>
72
  </div>
73
  </div>
74
  """
75
- action_log = "提醒信"
76
  else:
77
- # 🚀 確認模式 (Confirmation)
 
 
 
78
  mail_subject = f"訂位確認: {booking['date']} - Cié Cié Taipei"
79
- line_text = f"✅ 訂位確認\n\n{booking['name']} 您好,已收到您的預約。\n\n📅 日期:{booking['date']}\n⏰ 時間:{booking['time']}\n👥 人數:{booking['pax']}位\n\n請查收 Email 確認信並點擊確認按鈕,謝謝!"
80
 
81
- # 確認信 HTML (有確認按鈕)
 
 
 
 
 
 
 
 
 
 
82
  mail_html = f"""
83
  <div style="background:#1a1a1a; color:#d4af37; padding:20px; border-radius:8px; font-family:sans-serif; border:1px solid #333;">
84
  <h2 style="text-align:center; border-bottom:1px solid #444; padding-bottom:15px;">Cié Cié Taipei</h2>
85
- <p style="color:#eee; font-size:16px;"><strong>{booking['name']}</strong> 您好,已為您保留座位:</p>
86
  <div style="background:#2a2a2a; padding:15px; border-radius:8px; margin:20px 0; border-left:4px solid #d4af37;">
87
  <ul style="color:#ddd; padding-left:20px; line-height:1.8;">
88
  <li>📅 日期:<strong style="color:#fff;">{booking['date']}</strong></li>
@@ -91,39 +114,37 @@ def send_confirmation_hybrid(booking_id):
91
  </ul>
92
  </div>
93
  <div style="text-align:center; margin-top:30px;">
94
- <a href="{confirm_link}" style="display:inline-block; background:#d4af37; color:#000; padding:12px 30px; text-decoration:none; border-radius:50px; font-weight:bold; margin-right:10px;">✅ 確認出席</a>
95
- <a href="{cancel_link}" style="display:inline-block; border:1px solid #555; color:#aaa; padding:11px 29px; text-decoration:none; border-radius:50px;">🚫 取消</a>
96
  </div>
97
  </div>
98
  """
99
- action_log = "確認信"
100
 
101
- # 1. 發送 Email
 
102
  if email and "@" in email and GAS_MAIL_URL:
103
  try:
104
  requests.post(GAS_MAIL_URL, json={"to": email, "subject": mail_subject, "htmlBody": mail_html, "name": "Cié Cié Taipei"})
105
- log_msg += f"✅ Mail({action_log}) "
106
  except: log_msg += "❌ MailErr "
107
 
108
- # 2. 發送 LINE
109
  if user_id and len(str(user_id)) > 10 and LINE_ACCESS_TOKEN:
110
  try:
111
  requests.post("https://api.line.me/v2/bot/message/push",
112
  headers={"Authorization": f"Bearer {LINE_ACCESS_TOKEN}"},
113
  json={"to": user_id, "messages": [{"type": "text", "text": line_text}]})
114
- log_msg += f"✅ LINE({action_log}) "
115
  except: log_msg += "❌ LINEErr"
116
 
117
- # 3. 狀態更新
118
- # 如果本來就是已確認,發送提醒後狀態不變
119
- # 如果本來是待處理,發送後變更為已發確認信
120
- if not is_confirmed:
121
  supabase.table("bookings").update({"status": "已發確認信"}).eq("id", booking_id).execute()
122
 
123
  return log_msg
124
  except Exception as e: return f"Error: {e}"
125
 
126
- # 🔥🔥🔥 卡片渲染 (按鈕依狀態變更文字) 🔥🔥🔥
127
  def render_booking_cards():
128
  df = get_bookings()
129
 
@@ -147,23 +168,23 @@ def render_booking_cards():
147
  status_color = "#f1c40f"; border_color = "#f1c40f"
148
 
149
  is_canceled = '取消' in status
150
- is_confirmed = '確認' in status
151
 
 
152
  btn_onclick = "" if is_canceled else f"cardAction({row['id']})"
153
 
154
- # 按鈕樣式邏輯
155
  if is_canceled:
156
  btn_style = "background: #333; color: #666; cursor: not-allowed;"
157
  btn_text = "🚫 已取消"
158
  elif is_confirmed:
159
- # 🔔 已確認狀態 -> 顯示發送提醒
160
- btn_style = "background: #2c3e50; color: #fff; border: 1px solid #555; box-shadow: 0 0 10px rgba(46, 204, 113, 0.2);"
161
- btn_text = "🔔 發送提醒"
 
162
  elif '已發' in status:
163
- btn_style = "background: #444; color: #ddd; border: 1px solid #666;"
164
  btn_text = "🔄 重發確認"
165
  else:
166
- # 待處理狀態 -> 顯示發送確認
167
  btn_style = "background: #d4af37; color: #000; font-weight:800; box-shadow: 0 4px 12px rgba(212, 175, 55, 0.5);"
168
  btn_text = "🚀 發送確認"
169
 
@@ -276,7 +297,6 @@ def check_login(user, password):
276
  js_logic = """
277
  function() {
278
  window.cardAction = function(id) {
279
- // 抓取輸入框 (兼容 Number 和 Textbox 結構)
280
  let idInput = document.querySelector('#hidden_id_input input') ||
281
  document.querySelector('#hidden_id_input textarea');
282
 
@@ -293,7 +313,7 @@ function() {
293
  }
294
  """
295
 
296
- # --- CSS (隱藏操作區的關鍵) ---
297
  custom_css = """
298
  body, .gradio-container { background-color: #0F0F0F; color: #fff; }
299
 
@@ -313,7 +333,6 @@ button:active { transform: scale(0.96); }
313
  border-radius: 10px;
314
  }
315
 
316
- /* 🔥 關鍵:隱藏操作區,但保持 visible=True 以供 JS 存取 */
317
  #hidden_ops {
318
  display: none !important;
319
  }
@@ -322,7 +341,6 @@ button:active { transform: scale(0.96); }
322
  # --- 介面 ---
323
  with gr.Blocks(title="Admin") as demo:
324
 
325
- # 1. 登入
326
  with gr.Group(visible=True) as login_row:
327
  gr.Markdown("# 🔒 Login")
328
  with gr.Row():
@@ -331,30 +349,26 @@ with gr.Blocks(title="Admin") as demo:
331
  login_btn = gr.Button("Enter", variant="primary")
332
  error_msg = gr.Markdown("")
333
 
334
- # 2. 後台
335
  with gr.Group(visible=False) as admin_row:
336
  with gr.Row(variant="panel", elem_id="header-panel"):
337
  gr.Markdown("### 🍷 Cié Cié Dashboard")
338
  refresh_btn = gr.Button("🔄 刷新列表", size="sm", variant="secondary")
339
 
340
- # 卡片顯示區
341
  booking_display = gr.HTML(elem_id="booking_display")
342
 
343
- # 🔥 隱藏的操作區 (visible=True, CSS隱藏)
344
  with gr.Column(visible=True, elem_id="hidden_ops"):
345
  hidden_id_input = gr.Number(elem_id="hidden_id_input", precision=0)
346
  hidden_send_btn = gr.Button("Send", elem_id="hidden_send_btn")
347
 
348
  log_output = gr.Textbox(label="系統日誌 (System Log)", lines=1, interactive=False)
349
 
350
- # 綁定事件
351
  login_btn.click(check_login, inputs=[username_input, password_input], outputs=[login_row, admin_row, error_msg]).then(
352
  render_booking_cards, outputs=booking_display
353
  )
354
 
355
  refresh_btn.click(render_booking_cards, outputs=booking_display)
356
 
357
- # 發送事件
358
  hidden_send_btn.click(
359
  send_confirmation_hybrid,
360
  inputs=hidden_id_input,
@@ -364,7 +378,6 @@ with gr.Blocks(title="Admin") as demo:
364
  outputs=booking_display
365
  )
366
 
367
- # 載入 JS 與 CSS
368
  demo.launch(css=custom_css, js=js_logic)
369
 
370
  if __name__ == "__main__":
 
29
  except:
30
  return pd.DataFrame()
31
 
32
+ # 🔥🔥🔥 核心修正:這裡決定「按下去要發什麼」 🔥🔥🔥
33
  def send_confirmation_hybrid(booking_id):
34
  if not booking_id: return "❌ 無效 ID"
35
  try:
36
+ # 1. 先去資料庫抓最新的狀態
37
  res = supabase.table("bookings").select("*").eq("id", booking_id).execute()
38
  if not res.data: return "❌ 找不到訂單"
39
  booking = res.data[0]
 
 
40
 
41
+ email = booking.get('email')
42
+ user_id = booking.get('user_id')
43
+ current_status = booking.get('status', '') # 抓取當前狀態
 
44
 
45
  confirm_link = f"{PUBLIC_SPACE_URL}/?id={booking_id}&action=confirm"
46
  cancel_link = f"{PUBLIC_SPACE_URL}/?id={booking_id}&action=cancel"
47
+
48
+ log_msg = f"🆔 {booking_id}: "
49
 
50
+ # 2. 判斷邏輯:是「已確認」還是「未確認」?
51
+ if "顧客已確認" in current_status:
52
+ # ==========================================
53
+ # 🔔 模式 A:發送「行前提醒」 (Reminder)
54
+ # ==========================================
55
+ action_type = "提醒"
56
+ mail_subject = f"🔔 行前提醒: {booking['date']} - Cié Cié Taipei"
57
 
58
+ # LINE 內容:強調期待光臨,沒有確認按鈕
59
+ line_text = (
60
+ f"🔔 行前提醒\n\n"
61
+ f"{booking['name']} 您好,期待今晚與您相見!\n\n"
62
+ f"📅 日期:{booking['date']}\n"
63
+ f"⏰ 時間:{booking['time']}\n"
64
+ f"👥 人數:{booking['pax']} 位\n\n"
65
+ f"若需更改行程,請聯繫我們或點擊下方連結取消。"
66
+ )
67
+
68
+ # Email 內容:純提醒,只留取消按鈕
69
  mail_html = f"""
70
  <div style="background:#1a1a1a; color:#d4af37; padding:20px; border-radius:8px; font-family:sans-serif; border:1px solid #333;">
71
  <h2 style="text-align:center; border-bottom:1px solid #444; padding-bottom:15px;">Cié Cié Taipei</h2>
72
+ <p style="color:#eee; font-size:16px;"><strong>{booking['name']}</strong> 您好,這是您的訂位提醒:</p>
73
+ <div style="background:#2a2a2a; padding:15px; border-radius:8px; margin:20px 0; border-left:4px solid #d4af37;">
 
74
  <ul style="color:#ddd; padding-left:20px; line-height:1.8;">
75
  <li>📅 日期:<strong style="color:#fff;">{booking['date']}</strong></li>
76
  <li>⏰ 時間:<strong style="color:#fff;">{booking['time']}</strong></li>
 
78
  </ul>
79
  </div>
80
  <div style="text-align:center; margin-top:30px;">
81
+ <span style="color:#888;">我們已為您準備好座位,期待您的光臨。</span><br><br>
82
+ <a href="{cancel_link}" style="display:inline-block; border:1px solid #555; color:#aaa; padding:10px 20px; text-decoration:none; border-radius:50px; font-size:12px;">若無法前來,請點此取消</a>
83
  </div>
84
  </div>
85
  """
86
+
87
  else:
88
+ # ==========================================
89
+ # 🚀 模式 B:發送「訂位確認」 (Confirmation)
90
+ # ==========================================
91
+ action_type = "確認"
92
  mail_subject = f"訂位確認: {booking['date']} - Cié Cié Taipei"
 
93
 
94
+ # LINE 內容:包含確認請求
95
+ line_text = (
96
+ f"✅ 訂位確認\n\n"
97
+ f"{booking['name']} 您好,已收到您的預約。\n\n"
98
+ f"📅 日期:{booking['date']}\n"
99
+ f"⏰ 時間:{booking['time']}\n"
100
+ f"👥 人數:{booking['pax']} 位\n\n"
101
+ f"請務必查收 Email 確認信並點擊「確認出席」按鈕,謝謝!"
102
+ )
103
+
104
+ # Email 內容:包含巨大的確認按鈕
105
  mail_html = f"""
106
  <div style="background:#1a1a1a; color:#d4af37; padding:20px; border-radius:8px; font-family:sans-serif; border:1px solid #333;">
107
  <h2 style="text-align:center; border-bottom:1px solid #444; padding-bottom:15px;">Cié Cié Taipei</h2>
108
+ <p style="color:#eee; font-size:16px;"><strong>{booking['name']}</strong> 您好,請確認您的訂位:</p>
109
  <div style="background:#2a2a2a; padding:15px; border-radius:8px; margin:20px 0; border-left:4px solid #d4af37;">
110
  <ul style="color:#ddd; padding-left:20px; line-height:1.8;">
111
  <li>📅 日期:<strong style="color:#fff;">{booking['date']}</strong></li>
 
114
  </ul>
115
  </div>
116
  <div style="text-align:center; margin-top:30px;">
117
+ <a href="{confirm_link}" style="display:inline-block; background:#d4af37; color:#000; padding:12px 30px; text-decoration:none; border-radius:50px; font-weight:bold; margin-right:10px;">✅ 確認出席 (Confirm)</a>
118
+ <a href="{cancel_link}" style="display:inline-block; border:1px solid #555; color:#aaa; padding:11px 29px; text-decoration:none; border-radius:50px;">🚫 取消 (Cancel)</a>
119
  </div>
120
  </div>
121
  """
 
122
 
123
+ # 3. 執行發送 (共用發送模組)
124
+ # Send Email
125
  if email and "@" in email and GAS_MAIL_URL:
126
  try:
127
  requests.post(GAS_MAIL_URL, json={"to": email, "subject": mail_subject, "htmlBody": mail_html, "name": "Cié Cié Taipei"})
128
+ log_msg += f"✅ Mail({action_type}) "
129
  except: log_msg += "❌ MailErr "
130
 
131
+ # Send LINE
132
  if user_id and len(str(user_id)) > 10 and LINE_ACCESS_TOKEN:
133
  try:
134
  requests.post("https://api.line.me/v2/bot/message/push",
135
  headers={"Authorization": f"Bearer {LINE_ACCESS_TOKEN}"},
136
  json={"to": user_id, "messages": [{"type": "text", "text": line_text}]})
137
+ log_msg += f"✅ LINE({action_type}) "
138
  except: log_msg += "❌ LINEErr"
139
 
140
+ # 4. 更新資料庫狀態 (只有在非確認狀態下才更新,避免覆蓋「已確認」)
141
+ if action_type == "確認":
 
 
142
  supabase.table("bookings").update({"status": "已發確認信"}).eq("id", booking_id).execute()
143
 
144
  return log_msg
145
  except Exception as e: return f"Error: {e}"
146
 
147
+ # 🔥🔥🔥 卡片渲染 (介面邏輯:決定按鈕長怎樣) 🔥🔥🔥
148
  def render_booking_cards():
149
  df = get_bookings()
150
 
 
168
  status_color = "#f1c40f"; border_color = "#f1c40f"
169
 
170
  is_canceled = '取消' in status
171
+ is_confirmed = '確認' in status # 判斷是否已確認
172
 
173
+ # 🟢 修正重點:只要不是已取消,按鈕永遠可以點擊,只是文字不同
174
  btn_onclick = "" if is_canceled else f"cardAction({row['id']})"
175
 
 
176
  if is_canceled:
177
  btn_style = "background: #333; color: #666; cursor: not-allowed;"
178
  btn_text = "🚫 已取消"
179
  elif is_confirmed:
180
+ # 🔔 狀態是「顧客已確認」時,按鈕變成「發送提醒」
181
+ # 點擊後 -> 觸發 send_confirmation_hybrid -> 函式內會判斷狀態 -> 發送「提醒信」
182
+ btn_style = "background: #2c3e50; color: #fff; border: 1px solid #555; box-shadow: 0 0 8px rgba(46, 204, 113, 0.3);"
183
+ btn_text = "🔔 發送提醒"
184
  elif '已發' in status:
185
+ btn_style = "background: #2c3e50; color: #ddd; border: 1px solid #555;"
186
  btn_text = "🔄 重發確認"
187
  else:
 
188
  btn_style = "background: #d4af37; color: #000; font-weight:800; box-shadow: 0 4px 12px rgba(212, 175, 55, 0.5);"
189
  btn_text = "🚀 發送確認"
190
 
 
297
  js_logic = """
298
  function() {
299
  window.cardAction = function(id) {
 
300
  let idInput = document.querySelector('#hidden_id_input input') ||
301
  document.querySelector('#hidden_id_input textarea');
302
 
 
313
  }
314
  """
315
 
316
+ # --- CSS (修復元件隱藏問題) ---
317
  custom_css = """
318
  body, .gradio-container { background-color: #0F0F0F; color: #fff; }
319
 
 
333
  border-radius: 10px;
334
  }
335
 
 
336
  #hidden_ops {
337
  display: none !important;
338
  }
 
341
  # --- 介面 ---
342
  with gr.Blocks(title="Admin") as demo:
343
 
 
344
  with gr.Group(visible=True) as login_row:
345
  gr.Markdown("# 🔒 Login")
346
  with gr.Row():
 
349
  login_btn = gr.Button("Enter", variant="primary")
350
  error_msg = gr.Markdown("")
351
 
 
352
  with gr.Group(visible=False) as admin_row:
353
  with gr.Row(variant="panel", elem_id="header-panel"):
354
  gr.Markdown("### 🍷 Cié Cié Dashboard")
355
  refresh_btn = gr.Button("🔄 刷新列表", size="sm", variant="secondary")
356
 
 
357
  booking_display = gr.HTML(elem_id="booking_display")
358
 
359
+ # 🔥 隱藏的操作區 (確保 visible=True)
360
  with gr.Column(visible=True, elem_id="hidden_ops"):
361
  hidden_id_input = gr.Number(elem_id="hidden_id_input", precision=0)
362
  hidden_send_btn = gr.Button("Send", elem_id="hidden_send_btn")
363
 
364
  log_output = gr.Textbox(label="系統日誌 (System Log)", lines=1, interactive=False)
365
 
 
366
  login_btn.click(check_login, inputs=[username_input, password_input], outputs=[login_row, admin_row, error_msg]).then(
367
  render_booking_cards, outputs=booking_display
368
  )
369
 
370
  refresh_btn.click(render_booking_cards, outputs=booking_display)
371
 
 
372
  hidden_send_btn.click(
373
  send_confirmation_hybrid,
374
  inputs=hidden_id_input,
 
378
  outputs=booking_display
379
  )
380
 
 
381
  demo.launch(css=custom_css, js=js_logic)
382
 
383
  if __name__ == "__main__":