Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -22,9 +22,12 @@ REAL_ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD") or "2016-11-11"
|
|
| 22 |
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
|
| 23 |
|
| 24 |
def get_bookings():
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
def send_confirmation_hybrid(booking_id):
|
| 30 |
if not booking_id: return "❌ 無效 ID"
|
|
@@ -111,17 +114,15 @@ def render_booking_cards():
|
|
| 111 |
border_color = "#f1c40f"
|
| 112 |
|
| 113 |
# 按鈕邏輯
|
| 114 |
-
# 只有「顧客已取消」才鎖定,其他狀態 (包含已確認、已發送) 都允許重發
|
| 115 |
is_canceled = '取消' in status
|
| 116 |
btn_onclick = "" if is_canceled else f"cardAction({row['id']})"
|
| 117 |
|
| 118 |
-
# 按鈕樣式與文字
|
| 119 |
if is_canceled:
|
| 120 |
btn_style = "background: #333; color: #666; cursor: not-allowed;"
|
| 121 |
btn_text = "🚫 已取消"
|
| 122 |
elif '已發' in status or '確認' in status:
|
| 123 |
btn_style = "background: #2c3e50; color: #fff; border: 1px solid #555; hover:background:#34495e;"
|
| 124 |
-
btn_text = "🔄 重發確認"
|
| 125 |
else:
|
| 126 |
btn_style = "background: #d4af37; color: #000; font-weight:bold; box-shadow: 0 4px 10px rgba(212, 175, 55, 0.4);"
|
| 127 |
btn_text = "🚀 發送確認"
|
|
@@ -219,7 +220,7 @@ def check_login(user, password):
|
|
| 219 |
error_msg: "<span style='color: red'>❌ 帳號或密碼錯誤</span>"
|
| 220 |
}
|
| 221 |
|
| 222 |
-
# --- JS 邏輯
|
| 223 |
js_logic = """
|
| 224 |
function() {
|
| 225 |
window.cardAction = function(id) {
|
|
@@ -236,10 +237,12 @@ function() {
|
|
| 236 |
}
|
| 237 |
"""
|
| 238 |
|
| 239 |
-
# --- CSS 優化 ---
|
|
|
|
| 240 |
custom_css = """
|
| 241 |
body, .gradio-container { background-color: #0F0F0F; color: #fff; }
|
| 242 |
-
|
|
|
|
| 243 |
#booking_display {
|
| 244 |
max-height: 85vh;
|
| 245 |
overflow-y: auto;
|
|
@@ -248,10 +251,19 @@ body, .gradio-container { background-color: #0F0F0F; color: #fff; }
|
|
| 248 |
#booking_display::-webkit-scrollbar { width: 4px; }
|
| 249 |
#booking_display::-webkit-scrollbar-thumb { background: #444; border-radius: 4px; }
|
| 250 |
button:active { transform: scale(0.98); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
"""
|
| 252 |
|
| 253 |
# --- 介面 ---
|
| 254 |
-
|
|
|
|
| 255 |
|
| 256 |
# 1. 登入
|
| 257 |
with gr.Group(visible=True) as login_row:
|
|
@@ -264,7 +276,8 @@ with gr.Blocks(title="Admin", css=custom_css) as demo:
|
|
| 264 |
|
| 265 |
# 2. 後台
|
| 266 |
with gr.Group(visible=False) as admin_row:
|
| 267 |
-
|
|
|
|
| 268 |
gr.Markdown("### 🍷 Cié Cié Dashboard")
|
| 269 |
refresh_btn = gr.Button("🔄 刷新列表", size="sm", variant="secondary")
|
| 270 |
|
|
@@ -299,4 +312,5 @@ with gr.Blocks(title="Admin", css=custom_css) as demo:
|
|
| 299 |
demo.load(None, js=js_logic)
|
| 300 |
|
| 301 |
if __name__ == "__main__":
|
| 302 |
-
|
|
|
|
|
|
| 22 |
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
|
| 23 |
|
| 24 |
def get_bookings():
|
| 25 |
+
try:
|
| 26 |
+
res = supabase.table("bookings").select("*").order("created_at", desc=True).execute()
|
| 27 |
+
if not res.data: return pd.DataFrame()
|
| 28 |
+
return pd.DataFrame(res.data)
|
| 29 |
+
except:
|
| 30 |
+
return pd.DataFrame()
|
| 31 |
|
| 32 |
def send_confirmation_hybrid(booking_id):
|
| 33 |
if not booking_id: return "❌ 無效 ID"
|
|
|
|
| 114 |
border_color = "#f1c40f"
|
| 115 |
|
| 116 |
# 按鈕邏輯
|
|
|
|
| 117 |
is_canceled = '取消' in status
|
| 118 |
btn_onclick = "" if is_canceled else f"cardAction({row['id']})"
|
| 119 |
|
|
|
|
| 120 |
if is_canceled:
|
| 121 |
btn_style = "background: #333; color: #666; cursor: not-allowed;"
|
| 122 |
btn_text = "🚫 已取消"
|
| 123 |
elif '已發' in status or '確認' in status:
|
| 124 |
btn_style = "background: #2c3e50; color: #fff; border: 1px solid #555; hover:background:#34495e;"
|
| 125 |
+
btn_text = "🔄 重發確認"
|
| 126 |
else:
|
| 127 |
btn_style = "background: #d4af37; color: #000; font-weight:bold; box-shadow: 0 4px 10px rgba(212, 175, 55, 0.4);"
|
| 128 |
btn_text = "🚀 發送確認"
|
|
|
|
| 220 |
error_msg: "<span style='color: red'>❌ 帳號或密碼錯誤</span>"
|
| 221 |
}
|
| 222 |
|
| 223 |
+
# --- JS 邏輯 ---
|
| 224 |
js_logic = """
|
| 225 |
function() {
|
| 226 |
window.cardAction = function(id) {
|
|
|
|
| 237 |
}
|
| 238 |
"""
|
| 239 |
|
| 240 |
+
# --- CSS 優化 (修復 elem_style 錯誤) ---
|
| 241 |
+
# 把原本 inline 的 elem_style 移到這裡的 header-panel
|
| 242 |
custom_css = """
|
| 243 |
body, .gradio-container { background-color: #0F0F0F; color: #fff; }
|
| 244 |
+
|
| 245 |
+
/* 讓卡片容器可以滾動但隱藏捲軸 */
|
| 246 |
#booking_display {
|
| 247 |
max-height: 85vh;
|
| 248 |
overflow-y: auto;
|
|
|
|
| 251 |
#booking_display::-webkit-scrollbar { width: 4px; }
|
| 252 |
#booking_display::-webkit-scrollbar-thumb { background: #444; border-radius: 4px; }
|
| 253 |
button:active { transform: scale(0.98); }
|
| 254 |
+
|
| 255 |
+
/* Header Panel 樣式 */
|
| 256 |
+
#header-panel {
|
| 257 |
+
background: #1a1a1a;
|
| 258 |
+
border: none;
|
| 259 |
+
padding: 10px;
|
| 260 |
+
margin-bottom: 20px;
|
| 261 |
+
}
|
| 262 |
"""
|
| 263 |
|
| 264 |
# --- 介面 ---
|
| 265 |
+
# ⚠️ 注意:css 參數從 Blocks 移到 launch
|
| 266 |
+
with gr.Blocks(title="Admin") as demo:
|
| 267 |
|
| 268 |
# 1. 登入
|
| 269 |
with gr.Group(visible=True) as login_row:
|
|
|
|
| 276 |
|
| 277 |
# 2. 後台
|
| 278 |
with gr.Group(visible=False) as admin_row:
|
| 279 |
+
# 修正:移除 elem_style,改用 elem_id
|
| 280 |
+
with gr.Row(variant="panel", elem_id="header-panel"):
|
| 281 |
gr.Markdown("### 🍷 Cié Cié Dashboard")
|
| 282 |
refresh_btn = gr.Button("🔄 刷新列表", size="sm", variant="secondary")
|
| 283 |
|
|
|
|
| 312 |
demo.load(None, js=js_logic)
|
| 313 |
|
| 314 |
if __name__ == "__main__":
|
| 315 |
+
# ⚠️ 修正:將 css 參數移至 launch()
|
| 316 |
+
demo.launch(css=custom_css)
|