| import gradio as gr |
|
|
| |
| |
| |
|
|
| VESSELS = [ |
| {"id": "18365", "name": "Ice-9", "atoll": "Noonu", "type": "Supply", "contact": "7781552"}, |
| {"id": "18585", "name": "Ilaa", "atoll": "Alif Alif", "type": "Supply", "contact": "7500505"}, |
| {"id": "18231", "name": "Landaa Giraavaru 1", "atoll": "Baa", "type": "Resort", "contact": "Four Seasons"}, |
| {"id": "11054", "name": "Maafahi", "atoll": "Raa", "type": "Supply", "contact": "7775151"}, |
| {"id": "18652", "name": "Manta Cruise", "atoll": "Central", "type": "Safari", "contact": "Liveaboard"}, |
| {"id": "17316", "name": "Hope", "atoll": "Male", "type": "Supply", "contact": "7772439"}, |
| {"id": "14595", "name": "Huvafen 1", "atoll": "K. Kaafu", "type": "Speedboat", "contact": "7771715"} |
| ] |
|
|
| |
| |
| |
|
|
| |
| MOBILE_UI_SETUP = """ |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
| <style> |
| .gradio-container { max-width: 100% !important; padding: 0 !important; background-color: #f8fafc !important; } |
| .tabs { border: none !important; } |
| .tab-nav { |
| position: fixed !important; bottom: 0 !important; width: 100% !important; |
| background: white !important; z-index: 1000 !important; |
| border-top: 1px solid #e2e8f0 !important; height: 70px !important; |
| display: flex !important; justify-content: space-around !important; |
| } |
| footer { display: none !important; } |
| #vessel_list_container { padding-bottom: 80px !important; } |
| </style> |
| """ |
|
|
| def get_radar_overlay(v_id): |
| if not v_id: return "" |
| iframe_url = f"https://m.followme.mv/public/?id={v_id}" |
| return f""" |
| <div style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: #000; z-index: 99999;"> |
| <button onclick="window.location.reload();" style="position: absolute; top: 25px; right: 20px; background: #ef4444; color: white; border: none; padding: 15px 25px; border-radius: 50px; font-weight: 900; z-index: 100000; box-shadow: 0 5px 20px rgba(0,0,0,0.5);">β CLOSE</button> |
| <iframe src="{iframe_url}" width="100%" height="100%" style="border: none;"></iframe> |
| </div> |
| """ |
|
|
| def render_list(search_txt): |
| html = '<div id="vessel_list_container" style="padding: 15px; display: flex; flex-direction: column; gap: 10px;">' |
| for v in VESSELS: |
| if search_txt and search_txt.lower() not in v["name"].lower(): continue |
| html += f""" |
| <div style="background: white; border-radius: 15px; padding: 15px; border: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center;"> |
| <div> |
| <h4 style="margin: 0; color: #0c4a6e; font-size: 15px; font-weight: 900;">{v['name']}</h4> |
| <p style="margin: 0; font-size: 10px; color: #0ea5e9; font-weight: 800;">{v['atoll'].upper()} β’ {v['type']}</p> |
| </div> |
| <button onclick="document.getElementById('nav_trigger_input').querySelector('input').value='{v['id']}'; document.getElementById('nav_trigger_input').querySelector('input').dispatchEvent(new Event('input', {{bubbles: true}}));" |
| style="background: #0ea5e9; color: white; border: none; padding: 10px 15px; border-radius: 10px; font-weight: 800; font-size: 11px;"> |
| TRACK |
| </button> |
| </div> |
| """ |
| html += "</div>" |
| return html |
|
|
| |
| |
| |
|
|
| with gr.Blocks(title="Viri | Maldives Fleet") as app: |
| |
| gr.HTML(MOBILE_UI_SETUP) |
| |
| |
| nav_trigger = gr.Textbox(visible=False, elem_id="nav_trigger_input") |
| radar_container = gr.HTML() |
|
|
| with gr.Tabs() as tabs: |
| with gr.Tab("π°οΈ LIVE MAP", id="tab_home"): |
| |
| gr.HTML('<iframe src="https://m.followme.mv/public/" width="100%" height="380px" style="border:none; border-radius: 0 0 20px 20px;"></iframe>') |
| |
| with gr.Group(): |
| search = gr.Textbox(placeholder="π Search Vessel Name...", label=None, container=False) |
| list_view = gr.HTML(render_list("")) |
| |
| search.change(render_list, search, list_view) |
|
|
| with gr.Tab("π« TICKETS"): |
| gr.HTML(""" |
| <div style='padding:50px; text-align:center; color:#94a3b8;'> |
| <h3>BML Gateway</h3> |
| <p>No active sessions found.</p> |
| </div> |
| """) |
|
|
| |
| nav_trigger.change(get_radar_overlay, nav_trigger, radar_container) |
|
|
| |
| app.launch() |