Vstay / app.py
Jofax's picture
Rename app.y to app.py
3ab1f0a verified
import gradio as gr
import sqlite3
import pandas as pd
import os
import shutil
from datetime import datetime
# --- 1. CONFIGURATION & DATABASE ---
# For Hugging Face Persistent Storage, use /data/ prefix if available
DB_PATH = "/data/raalhu_elite_2026.db" if os.path.exists("/data") else "raalhu_elite_2026.db"
ADMIN_PASS = os.getenv("ADMIN_PASSWORD", "raalhu2026") # Set this in HF Secrets
def init_db():
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
# Rooms Table
c.execute('''CREATE TABLE IF NOT EXISTS inventory
(id INTEGER PRIMARY KEY, name TEXT, island TEXT, price REAL, stock INTEGER)''')
# Bookings Table
c.execute('''CREATE TABLE IF NOT EXISTS bookings
(id INTEGER PRIMARY KEY, guest TEXT, room_name TEXT, method TEXT,
status TEXT, slip_path TEXT, timestamp TEXT)''')
# Seed Initial Data if empty
c.execute("SELECT count(*) FROM inventory")
if c.fetchone()[0] == 0:
rooms = [
(1, 'Ocean Palm Suites', 'Maafushi', 1450, 3),
(2, 'Manta Retreat', 'Dhigurah', 2100, 1),
(3, 'Azure Horizon', 'Ukulhas', 950, 5)
]
c.executemany("INSERT INTO inventory VALUES (?,?,?,?,?)", rooms)
conn.commit()
conn.close()
# --- 2. BACKEND LOGIC ---
def get_rooms():
conn = sqlite3.connect(DB_PATH)
df = pd.read_sql_query("SELECT * FROM inventory", conn)
conn.close()
return df
def get_admin_data():
conn = sqlite3.connect(DB_PATH)
df = pd.read_sql_query("SELECT * FROM bookings ORDER BY timestamp DESC", conn)
conn.close()
return df
def handle_booking(guest_name, room_id, method, slip_file):
if not guest_name or not room_id:
return "⚠️ Error: Please provide your name and select a room."
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
# Check Stock
c.execute("SELECT name, stock FROM inventory WHERE id = ?", (room_id,))
room = c.fetchone()
if not room or room[1] <= 0:
return "❌ Sorry, this room just sold out!"
# Process Slip
path = ""
if method == "Bank Transfer (BML)" and slip_file:
os.makedirs("uploads", exist_ok=True)
path = f"uploads/{datetime.now().strftime('%m%d%H%M%S')}_{guest_name[:5]}.png"
shutil.copy(slip_file.name, path)
status = "Confirmed βœ…" if method == "Credit/Debit Card" else "Pending Review ⏳"
# Update DB
c.execute("UPDATE inventory SET stock = stock - 1 WHERE id = ?", (room_id,))
c.execute("INSERT INTO bookings (guest, room_name, method, status, slip_path, timestamp) VALUES (?,?,?,?,?,?)",
(guest_name, room[0], method, status, path, datetime.now().strftime('%Y-%m-%d %H:%M')))
conn.commit()
conn.close()
return f"✨ Success! Your booking at {room[0]} is {status}. Receipt sent to WhatsApp."
# --- 3. UI THEMING (Elite 2026 CSS) ---
elite_css = """
.gradio-container { background-color: #F0F9FF !important; }
.premium-card { border-radius: 20px; border: 1px solid #E0F2FE; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); background: white; padding: 20px; }
.admin-header { background: #0F172A; color: white; padding: 15px; border-radius: 12px; }
"""
# --- 4. THE APP STRUCTURE ---
with gr.Blocks() as demo:
init_db()
gr.HTML("<div style='text-align:center; padding: 20px;'><h1 style='font-size: 40px; font-weight: 900; color: #0369A1;'>raalhu<span style='color:#0EA5E9'>.</span></h1><p style='color:#64748B;'>The Maldives Elite Booking Engine</p></div>")
with gr.Tabs() as main_tabs:
# TAB 1: GUEST BOOKING
with gr.Tab("🏝️ Find a Room"):
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("### ⚑ Live Inventory")
room_table = gr.Dataframe(get_rooms, every=10, label="Current Available Rooms")
with gr.Column(scale=1, elem_classes="premium-card"):
gr.Markdown("### πŸ’³ Instant Checkout")
guest_name = gr.Textbox(label="Your Name", placeholder="Enter full name")
# Dynamically fetch choices
room_list = get_rooms()
room_choice = gr.Dropdown(
label="Choose Property",
choices=[(row['id'], row['name']) for _, row in room_list.iterrows()]
)
pay_method = gr.Radio(["Bank Transfer (BML)", "Credit/Debit Card"], label="Payment Method", value="Bank Transfer (BML)")
# Bank Transfer Section
with gr.Column() as bml_section:
gr.HTML("<div style='background:#F8FAFC; padding:10px; border-radius:8px; border:1px solid #CBD5E1;'><b>BML: 7730000XXXXXX</b><br>Raalhu Holdings Pvt Ltd</div>")
slip_input = gr.File(label="Upload Slip", file_types=["image"])
# Card Section (Simulated)
card_section = gr.Markdown("Redirecting to Secure Gateway on click...", visible=False)
def toggle_payment(m):
return gr.update(visible=(m == "Bank Transfer (BML)")), gr.update(visible=(m == "Credit/Debit Card"))
pay_method.change(toggle_payment, pay_method, [bml_section, card_section])
book_btn = gr.Button("CONFIRM BOOKING", variant="primary")
out_msg = gr.Markdown()
# TAB 2: SECRET OWNER PORTAL
with gr.Tab("πŸ” Admin Dashboard"):
with gr.Column() as login_screen:
password_input = gr.Textbox(label="Enter Secret Password", type="password")
login_btn = gr.Button("Login")
with gr.Column(visible=False) as admin_screen:
gr.HTML("<div class='admin-header'><h3>Owner Management Panel</h3></div>")
admin_table = gr.Dataframe(get_admin_data, label="All Bookings & Slips")
refresh_admin = gr.Button("πŸ”„ Refresh Data")
with gr.Row():
verify_id = gr.Number(label="Verify Booking ID", precision=0)
verify_btn = gr.Button("Approve Payment", variant="primary")
# --- 5. EVENT HANDLERS ---
book_btn.click(handle_booking, [guest_name, room_choice, pay_method, slip_input], out_msg)
def check_pass(p):
if p == ADMIN_PASS: return gr.update(visible=True), gr.update(visible=False)
return gr.update(visible=False), gr.update(visible=True)
login_btn.click(check_pass, password_input, [admin_screen, login_screen])
refresh_admin.click(get_admin_data, outputs=admin_table)
# Launch with Config for 2026 standards
demo.launch(
css=elite_css,
theme=gr.themes.Soft(primary_hue="sky"),
show_api=False
)