Spaces:
Runtime error
Runtime error
| import streamlit as st | |
| st.set_page_config( | |
| page_title="HantaLytics", | |
| page_icon="🧬", | |
| layout="wide", | |
| initial_sidebar_state="collapsed", | |
| ) | |
| # Global CSS | |
| st.markdown(""" | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;600&display=swap'); | |
| :root { | |
| --bg: #eef5ff; | |
| --surface: #ffffff; | |
| --surface2: #ddeeff; | |
| --border: #bdd4f0; | |
| --accent: #2563eb; | |
| --accent-hover: #1d4ed8; | |
| --accent-pale: #dbeafe; | |
| --accent2: #0ea5e9; | |
| --text: #0f172a; | |
| --text-muted: #475569; | |
| --text-light: #94a3b8; | |
| --card-bg: #ffffff; | |
| --success: #10b981; | |
| --danger: #ef4444; | |
| } | |
| html, body, [data-testid="stAppViewContainer"] { | |
| background: linear-gradient(160deg, #eef5ff 0%, #e0efff 60%, #f0f9ff 100%) !important; | |
| color: var(--text) !important; | |
| font-family: 'Plus Jakarta Sans', sans-serif !important; | |
| } | |
| [data-testid="stSidebar"] { display: none !important; } | |
| header[data-testid="stHeader"] { display: none !important; } | |
| footer { display: none !important; } | |
| #MainMenu { display: none !important; } | |
| .block-container { | |
| padding-top: 0 !important; | |
| padding-bottom: 2rem !important; | |
| padding-left: 3rem !important; | |
| padding-right: 3rem !important; | |
| max-width: 1200px !important; | |
| } | |
| h1, h2, h3, h4 { | |
| font-family: 'Plus Jakarta Sans', sans-serif !important; | |
| color: var(--text) !important; | |
| } | |
| /* ── Buttons ── */ | |
| .stButton > button { | |
| background: var(--accent) !important; | |
| border: none !important; | |
| color: #fff !important; | |
| font-family: 'Plus Jakarta Sans', sans-serif !important; | |
| font-size: 0.92rem !important; | |
| font-weight: 600 !important; | |
| padding: 0.65rem 1.5rem !important; | |
| border-radius: 10px !important; | |
| transition: all 0.2s ease !important; | |
| box-shadow: 0 2px 10px rgba(37,99,235,.28) !important; | |
| letter-spacing: 0.01em !important; | |
| width: 100% !important; | |
| } | |
| .stButton > button:hover { | |
| background: var(--accent-hover) !important; | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 6px 18px rgba(37,99,235,.38) !important; | |
| } | |
| .stButton > button:active { transform: translateY(0) !important; } | |
| /* number/text inputs */ | |
| div[data-testid="stNumberInput"] input, | |
| div[data-testid="stTextInput"] input { | |
| background: #fff !important; | |
| border: 1.5px solid var(--border) !important; | |
| color: var(--text) !important; | |
| border-radius: 8px !important; | |
| font-family: 'Plus Jakarta Sans', sans-serif !important; | |
| } | |
| div[data-testid="stNumberInput"] input:focus, | |
| div[data-testid="stTextInput"] input:focus { | |
| border-color: var(--accent) !important; | |
| box-shadow: 0 0 0 3px rgba(37,99,235,.12) !important; | |
| } | |
| /* selectbox / multiselect */ | |
| div[data-baseweb="select"] > div:first-child { | |
| background: #fff !important; | |
| border: 1.5px solid var(--border) !important; | |
| border-radius: 8px !important; | |
| color: var(--text) !important; | |
| } | |
| /* textarea */ | |
| div[data-testid="stTextArea"] textarea { | |
| background: #fff !important; | |
| border: 1.5px solid var(--border) !important; | |
| color: var(--text) !important; | |
| border-radius: 8px !important; | |
| font-family: 'JetBrains Mono', monospace !important; | |
| font-size: 0.82rem !important; | |
| } | |
| div[data-testid="stCaptionContainer"] p { color: var(--text-muted) !important; } | |
| div[data-testid="stAlert"] { border-radius: 10px !important; } | |
| ::-webkit-scrollbar { width: 6px; } | |
| ::-webkit-scrollbar-track { background: var(--surface2); } | |
| ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 10px; } | |
| ::-webkit-scrollbar-thumb:hover { background: var(--accent); } | |
| /* ── Homepage-specific ── */ | |
| .home-hero { | |
| text-align: center; | |
| padding: 3.5rem 1rem 2rem; | |
| } | |
| .home-badge { | |
| display: inline-block; | |
| background: rgba(37,99,235,.1); | |
| color: #2563eb; | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 0.7rem; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| padding: 0.35rem 0.9rem; | |
| border-radius: 100px; | |
| border: 1px solid rgba(37,99,235,.25); | |
| margin-bottom: 1.1rem; | |
| } | |
| .home-title { | |
| font-family: 'Plus Jakarta Sans', sans-serif; | |
| font-size: clamp(2rem, 4vw, 3rem); | |
| font-weight: 800; | |
| color: #0f172a; | |
| line-height: 1.15; | |
| margin-bottom: 1rem; | |
| letter-spacing: -0.02em; | |
| } | |
| .home-title span { color: #2563eb; } | |
| .home-subtitle { | |
| font-size: 1rem; | |
| color: #475569; | |
| max-width: 560px; | |
| margin: 0 auto 2.5rem; | |
| line-height: 1.7; | |
| text-align: center; | |
| } | |
| /* ── Model cards ── */ | |
| .model-card { | |
| background: #ffffff; | |
| border: 1.5px solid #bdd4f0; | |
| border-radius: 16px; | |
| padding: 1.8rem 1.6rem 1.4rem; | |
| height: 100%; | |
| box-shadow: 0 2px 12px rgba(37,99,235,.07); | |
| transition: box-shadow 0.2s, transform 0.2s; | |
| margin-bottom: 0.75rem; | |
| } | |
| .model-card:hover { | |
| box-shadow: 0 8px 28px rgba(37,99,235,.14); | |
| transform: translateY(-2px); | |
| } | |
| .card-badge { | |
| display: inline-block; | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 0.67rem; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| padding: 0.25rem 0.7rem; | |
| border-radius: 100px; | |
| margin-bottom: 1rem; | |
| } | |
| .card-badge.teal { background: #ccfbf1; color: #0f766e; } | |
| .card-icon { font-size: 2rem; margin-bottom: 0.75rem; line-height: 1; } | |
| .card-title { | |
| font-family: 'Plus Jakarta Sans', sans-serif; | |
| font-size: 1.15rem; | |
| font-weight: 700; | |
| color: #0f172a; | |
| margin-bottom: 0.5rem; | |
| } | |
| .card-desc { | |
| font-size: 0.85rem; | |
| color: #475569; | |
| line-height: 1.65; | |
| margin-bottom: 1.2rem; | |
| } | |
| .card-tags { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 0.4rem; | |
| margin-bottom: 1.4rem; | |
| } | |
| .card-tag { | |
| font-size: 0.75rem; | |
| background: #f1f5f9; | |
| color: #475569; | |
| padding: 0.2rem 0.6rem; | |
| border-radius: 6px; | |
| border: 1px solid #e2e8f0; | |
| } | |
| /* ── Stats strip ── */ | |
| .stats-strip { | |
| display: flex; | |
| justify-content: center; | |
| gap: 3rem; | |
| padding: 1.8rem 2rem; | |
| background: rgba(255,255,255,.6); | |
| border: 1px solid #bdd4f0; | |
| border-radius: 14px; | |
| margin-top: 2.5rem; | |
| backdrop-filter: blur(6px); | |
| } | |
| .stat-item { text-align: center; } | |
| .stat-num { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| color: #2563eb; | |
| } | |
| .stat-lbl { | |
| font-size: 0.77rem; | |
| color: #64748b; | |
| margin-top: 0.2rem; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Routing | |
| if "page" not in st.session_state: | |
| st.session_state.page = "home" | |
| def goto(page: str): | |
| st.session_state.page = page | |
| st.rerun() | |
| # Page dispatch | |
| if st.session_state.page == "home": | |
| # Hero | |
| st.markdown(""" | |
| <div class="home-hero"> | |
| <div class="home-badge">Hantavirus Diagnostic Platform</div> | |
| <div class="home-title">Hanta<span>Lytics</span></div> | |
| <div class="home-subtitle"> | |
| Platform diagnosis terintegrasi untuk mendeteksi Hantavirus dengan Variabel Klinis dan Sekuens DNA. | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Model cards + buttons | |
| col1, spacer, col2 = st.columns([1, 0.08, 1]) | |
| with col1: | |
| st.markdown(""" | |
| <div class="model-card"> | |
| <div class="card-badge teal">Model 01 · HantaVarNet</div> | |
| <div class="card-icon">📋</div> | |
| <div class="card-title">Analisis Variabel Klinis</div> | |
| <div class="card-desc"> | |
| Prediksi infeksi Hantavirus berdasarkan data demografi | |
| </div> | |
| <div class="card-tags"> | |
| <span class="card-tag">Variabel Klinis</span> | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| if st.button("Buka Model Variabel", key="btn_var", use_container_width=True): | |
| goto("variabel") | |
| with col2: | |
| st.markdown(""" | |
| <div class="model-card"> | |
| <div class="card-badge teal">Model 02 · HantaSeqNet</div> | |
| <div class="card-icon">🧬</div> | |
| <div class="card-title">Klasifikasi Sekuens DNA</div> | |
| <div class="card-desc"> | |
| Identifikasi varian Hantavirus melalui analisis sekuens DNA | |
| </div> | |
| <div class="card-tags"> | |
| <span class="card-tag">DNA Sequence</span> | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| if st.button("Buka Model Sekuens", key="btn_seq", use_container_width=True): | |
| goto("sequence") | |
| # Stats strip | |
| st.markdown(""" | |
| <div class="stats-strip"> | |
| <div class="stat-item"> | |
| <div class="stat-num">2</div> | |
| <div class="stat-lbl">Model Terintegrasi</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-num">8+</div> | |
| <div class="stat-lbl">Variabel Numerik & Kategorikal</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-num">DT</div> | |
| <div class="stat-lbl">Decision Tree</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-num">NLP</div> | |
| <div class="stat-lbl">DNABERT-2 & ANN</div> | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| elif st.session_state.page == "variabel": | |
| from input_variabel import render | |
| render(goto) | |
| elif st.session_state.page == "sequence": | |
| from input_sequence import render | |
| render(goto) | |