| import gradio as gr |
| import pandas as pd |
| import os |
|
|
| |
| DB_FILE = "unistay_neuro_v3.csv" |
| BRAND = "UNISTAY" |
|
|
| def init_db(): |
| if not os.path.exists(DB_FILE): |
| data = { |
| "ID": ["1", "2", "3", "4"], |
| "Name": ["Velaa Azure Estate", "Soneva Aqua Reserve", "Gili Lankanfushi", "Maafushi Luxe"], |
| "Location": ["Noonu Atoll", "Baa Atoll", "North Malé", "South Malé"], |
| "Price": [45000, 22000, 18500, 2400], |
| "Inventory": [2, 1, 4, 0], |
| "Transit": ["Private Jet", "Seaplane: 15:45", "Speedboat: 10:15", "Ferry: 14:00"], |
| "Img": [ |
| "https://images.unsplash.com/photo-1514282401047-d79a71a590e8?w=800&q=80", |
| "https://images.unsplash.com/photo-1540202404-a2f290328295?w=800&q=80", |
| "https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=800&q=80", |
| "https://images.unsplash.com/photo-1506929113675-b55f9d3bb838?w=800&q=80" |
| ], |
| "Token": ["9601", "9602", "9603", "9604"] |
| } |
| pd.DataFrame(data).to_csv(DB_FILE, index=False) |
|
|
| init_db() |
|
|
| |
| theme_css = """ |
| @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;600;800&family=Space+Grotesk:wght@300;500;700&display=swap'); |
| |
| :root { |
| --carbon-12: #050505; /* Prevents Halation (#2) */ |
| --onyx-deep: #0d0d0d; |
| --luxury-blue: #00D1FF; /* Re-balanced saturation (#7) */ |
| --text-primary: #F2F2F7; |
| --text-muted: #8E8E93; |
| --gold-ratio: 1.618; |
| } |
| |
| /* Base Container - Reducing Ocular Fatigue */ |
| .gradio-container { |
| background-color: var(--carbon-12) !important; |
| color: var(--text-primary) !important; |
| font-family: 'Plus Jakarta Sans', sans-serif !important; |
| } |
| |
| /* Hide Navigation Tabs (#13) */ |
| .tabs .tab-nav { display: none !important; } |
| |
| /* Orbital Navigation Hub (#11, #4) */ |
| .orbital-hub { |
| position: fixed; bottom: 0; left: 0; right: 0; |
| background: rgba(13, 13, 13, 0.9); |
| backdrop-filter: blur(40px); |
| border-top: 1px solid rgba(255,255,255,0.08); |
| padding: 24px 24px 48px 24px; |
| z-index: 1000; |
| } |
| |
| /* Card Hierarchy & Layered Shadows (#1, #5, #10) */ |
| .sovereign-grid { |
| display: grid; gap: 40px; padding: 20px 20px 280px 20px; |
| } |
| |
| .luxe-card { |
| background: var(--onyx-deep); |
| border-radius: 32px; |
| overflow: hidden; |
| border: 1px solid rgba(255,255,255,0.05); |
| /* Multi-layered shadows for depth (#1) */ |
| box-shadow: 0 4px 6px -1px rgba(0,0,0,0.5), 0 10px 20px -5px rgba(0,0,0,0.8); |
| transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); |
| } |
| |
| /* Status Indicator - Trust Signaling (#30, #22) */ |
| .sync-badge { |
| position: absolute; top: 25px; left: 25px; |
| background: rgba(0,0,0,0.6); backdrop-filter: blur(15px); |
| padding: 8px 18px; border-radius: 100px; font-size: 11px; |
| font-weight: 800; color: var(--luxury-blue); |
| border: 1px solid var(--luxury-blue); |
| } |
| |
| /* Prime Haptic-Ready Buttons (#12, #8) */ |
| .btn-prime { |
| background: #FFFFFF !important; color: #000000 !important; |
| height: 62px !important; border-radius: 20px !important; |
| font-weight: 800 !important; font-size: 1.05rem !important; |
| border: none !important; transition: all 0.4s ease; |
| } |
| .btn-prime:hover { background: var(--luxury-blue) !important; transform: scale(1.02); } |
| |
| /* Host Interface Styling (#47) */ |
| .host-panel { |
| background: var(--onyx-deep) !important; |
| padding: 40px !important; |
| border-radius: 40px !important; |
| border: 1px solid rgba(255,255,255,0.05) !important; |
| } |
| """ |
|
|
| def render_discovery(query=""): |
| df = pd.read_csv(DB_FILE) |
| html = '<div class="sovereign-grid">' |
| |
| if query: |
| df = df[df['Name'].str.contains(query, case=False) | df['Location'].str.contains(query, case=False)] |
| |
| for _, row in df.iterrows(): |
| is_avail = row['Inventory'] > 0 |
| status_color = "#00D1FF" if is_avail else "#FF453A" |
| status_label = "SOVEREIGN LINK" if is_avail else "CAPACITY FULL" |
| |
| html += f''' |
| <div class="luxe-card" style="opacity: {1 if is_avail else 0.6};"> |
| <div style="position: relative;"> |
| <img src="{row['Img']}" style="width:100%; height:320px; object-fit:cover; filter:brightness(0.9);"> |
| <div class="sync-badge" style="border-color:{status_color}; color:{status_color};">● {status_label}</div> |
| </div> |
| <div style="padding: 35px;"> |
| <div style="color:var(--luxury-blue); font-size:0.75rem; font-weight:800; letter-spacing:2px; margin-bottom:12px;">{row['Transit'].upper()}</div> |
| <h2 style="margin: 0; font-family:'Space Grotesk'; font-size:2.2rem; letter-spacing:-1px;">{row['Name']}</h2> |
| <p style="color:var(--text-muted); margin: 12px 0 35px 0; font-size:0.95rem;">{row['Location']} • Verified Asset 💎</p> |
| |
| <div style="display:flex; justify-content:space-between; align-items:flex-end; border-top:1px solid rgba(255,255,255,0.05); padding-top:30px;"> |
| <div> |
| <span style="color:var(--text-muted); font-size:0.75rem;">Global Value / Stay</span><br> |
| <span style="font-family:'Space Grotesk'; font-size:2rem; font-weight:700;">MVR {row['Price']:,}</span> |
| </div> |
| <button class="btn-prime" style="padding: 0 45px;">{"SECURE" if is_avail else "NOTIFY"}</button> |
| </div> |
| </div> |
| </div> |
| ''' |
| html += '</div>' |
| return html |
|
|
| def update_inventory(token, count): |
| df = pd.read_csv(DB_FILE, dtype={'Token': str}) |
| token = str(token).strip() |
| if token in df['Token'].values: |
| df.loc[df['Token'] == token, 'Inventory'] = int(count) |
| df.to_csv(DB_FILE, index=False) |
| return "🛰️ **Neuro-Sync Successful.** Inventory updated." |
| return "❌ **Authentication Failed.** Invalid Node ID." |
|
|
| |
| with gr.Blocks(title="UNISTAY Sovereign") as demo: |
| |
| gr.HTML(f"<div style='text-align:center; padding: 70px 0 30px 0;'><h1 style='font-family:\"Space Grotesk\"; font-size: 3.5rem; letter-spacing: -5px;'>{BRAND}<span style='color:var(--luxury-blue)'>.</span></h1></div>") |
| |
| with gr.Tabs() as nav_tabs: |
| |
| with gr.TabItem("Discovery", id="discovery"): |
| viewport = gr.HTML(render_discovery()) |
|
|
| |
| with gr.TabItem("Host", id="host"): |
| with gr.Column(elem_classes="host-panel"): |
| gr.Markdown("### 🛰️ Sovereign Node Sync") |
| t_in = gr.Textbox(label="Access Token", placeholder="Enter ID") |
| c_in = gr.Slider(label="Available Inventory", minimum=0, maximum=10, step=1) |
| sync_btn = gr.Button("BROADCAST SYNC", variant="primary", elem_classes="btn-prime") |
| sync_out = gr.Markdown() |
|
|
| |
| with gr.Column(elem_classes="orbital-hub"): |
| search_engine = gr.Textbox(show_label=False, placeholder="Where should we take you?", container=False) |
| with gr.Row(): |
| btn_discover = gr.Button("💠 EXPLORE", elem_classes="btn-prime") |
| btn_host = gr.Button("🛰️ BROADCAST", elem_classes="btn-prime") |
|
|
| |
| search_engine.change(fn=render_discovery, inputs=search_engine, outputs=viewport) |
| sync_btn.click(fn=update_inventory, inputs=[t_in, c_in], outputs=sync_out) |
| |
| |
| btn_discover.click(fn=lambda: gr.Tabs(selected="discovery"), outputs=nav_tabs) |
| btn_host.click(fn=lambda: gr.Tabs(selected="host"), outputs=nav_tabs) |
|
|
| |
| if __name__ == "__main__": |
| demo.launch(css=theme_css) |