HantaLytics / app.py
RangGaraga's picture
Rename main.py to app.py
b9e15db verified
Raw
History Blame Contribute Delete
9.5 kB
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)