Hamzavelous's picture
Enhanced UI/UX, new features, C PDC benchmarks
86369aa verified
Raw
History Blame Contribute Delete
10.9 kB
"""Design system & reusable UI components for Hostel MS."""
import streamlit as st
PALETTE = {
"primary": "#6366F1",
"primary_light": "#818CF8",
"accent": "#A855F7",
"success": "#10B981",
"warning": "#F59E0B",
"danger": "#EF4444",
"surface": "#1E293B",
"surface_alt": "#334155",
"bg": "#0B1220",
"text": "#F8FAFC",
"muted": "#94A3B8",
}
def inject_theme():
st.markdown(
f"""
<style>
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
:root {{
--primary: {PALETTE['primary']};
--accent: {PALETTE['accent']};
--surface: {PALETTE['surface']};
--bg: {PALETTE['bg']};
--text: {PALETTE['text']};
--muted: {PALETTE['muted']};
}}
html, body, [class*="css"] {{
font-family: 'Plus Jakarta Sans', sans-serif;
}}
.stApp {{
background: radial-gradient(ellipse 120% 80% at 50% -20%, rgba(99,102,241,0.22), transparent),
radial-gradient(ellipse 80% 50% at 100% 50%, rgba(168,85,247,0.1), transparent),
radial-gradient(ellipse 60% 40% at 0% 80%, rgba(16,185,129,0.06), transparent),
linear-gradient(180deg, #0B1220 0%, #0F172A 50%, #0B1220 100%);
}}
.block-container {{
padding-top: 1.25rem;
max-width: 1320px;
}}
div[data-testid="stSidebar"] {{
background: linear-gradient(180deg, #0B1220 0%, #1E293B 100%);
border-right: 1px solid rgba(255,255,255,0.06);
}}
div[data-testid="stSidebar"] > div:first-child {{
padding-top: 1.5rem;
}}
.sidebar-brand {{
text-align: center;
padding: 0.5rem 0 1rem;
}}
.sidebar-brand .logo {{
font-size: 2.5rem;
line-height: 1;
}}
.sidebar-brand h3 {{
margin: 0.35rem 0 0;
color: #E2E8F0;
font-weight: 800;
font-size: 1.05rem;
}}
.sidebar-brand p {{
margin: 0.15rem 0 0;
color: #64748B;
font-size: 0.72rem;
}}
div[data-testid="stSidebar"] .stRadio label {{
background: rgba(255,255,255,0.03);
border: 1px solid rgba(255,255,255,0.06);
border-radius: 12px;
padding: 0.55rem 0.75rem;
margin-bottom: 0.35rem;
transition: all 0.2s ease;
}}
div[data-testid="stSidebar"] .stRadio label:hover {{
border-color: rgba(99,102,241,0.45);
background: rgba(99,102,241,0.08);
}}
.hero {{
background: linear-gradient(135deg, #4F46E5 0%, #7C3AED 45%, #A855F7 100%);
padding: 2rem 2.5rem;
border-radius: 24px;
color: white;
margin-bottom: 1.5rem;
box-shadow: 0 24px 48px rgba(79,70,229,0.28);
border: 1px solid rgba(255,255,255,0.12);
position: relative;
overflow: hidden;
}}
.hero::before {{
content: '';
position: absolute;
inset: 0;
background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.04'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
opacity: 0.5;
}}
.hero::after {{
content: '';
position: absolute;
top: -50%;
right: -10%;
width: 280px;
height: 280px;
background: rgba(255,255,255,0.06);
border-radius: 50%;
}}
.hero h1 {{
margin: 0;
font-size: 2rem;
font-weight: 800;
letter-spacing: -0.02em;
position: relative;
z-index: 1;
}}
.hero p {{
margin: 0.5rem 0 0;
opacity: 0.92;
font-size: 1rem;
position: relative;
z-index: 1;
}}
.stat-card {{
background: linear-gradient(145deg, rgba(30,41,59,0.95), rgba(51,65,85,0.85));
border: 1px solid rgba(255,255,255,0.08);
border-radius: 18px;
padding: 1.35rem 1.5rem;
text-align: left;
box-shadow: 0 8px 32px rgba(0,0,0,0.25);
backdrop-filter: blur(8px);
height: 100%;
transition: transform 0.2s ease, border-color 0.2s ease;
}}
.stat-card:hover {{
transform: translateY(-2px);
border-color: rgba(99,102,241,0.35);
}}
.stat-card .value {{
margin: 0;
font-size: 1.85rem;
font-weight: 800;
color: #C7D2FE;
line-height: 1.2;
}}
.stat-card .label {{
margin: 0.35rem 0 0;
color: #94A3B8;
font-size: 0.82rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.04em;
}}
.stat-card .delta {{
margin-top: 0.5rem;
font-size: 0.78rem;
color: #6EE7B7;
}}
.feature-card {{
background: rgba(30,41,59,0.7);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 16px;
padding: 1.25rem;
height: 100%;
transition: border-color 0.2s ease;
}}
.feature-card:hover {{
border-color: rgba(168,85,247,0.35);
}}
.feature-card h4 {{
margin: 0 0 0.35rem;
color: #E2E8F0;
font-size: 1rem;
}}
.feature-card p {{
margin: 0;
color: #94A3B8;
font-size: 0.85rem;
line-height: 1.5;
}}
.login-shell {{
background: rgba(30,41,59,0.65);
border-radius: 24px;
padding: 2rem;
border: 1px solid rgba(99,102,241,0.25);
box-shadow: 0 16px 48px rgba(0,0,0,0.35);
backdrop-filter: blur(12px);
}}
.login-header {{
text-align: center;
margin-bottom: 1.25rem;
}}
.login-header .icon {{
font-size: 3rem;
}}
.login-header h2 {{
margin: 0.5rem 0 0.25rem;
color: #E2E8F0;
font-weight: 800;
}}
.login-header p {{
margin: 0;
color: #94A3B8;
font-size: 0.85rem;
}}
.badge {{
display: inline-block;
padding: 0.25rem 0.65rem;
border-radius: 999px;
font-size: 0.72rem;
font-weight: 600;
}}
.badge-success {{ background: rgba(16,185,129,0.2); color: #6EE7B7; }}
.badge-warning {{ background: rgba(245,158,11,0.2); color: #FCD34D; }}
.badge-danger {{ background: rgba(239,68,68,0.2); color: #FCA5A5; }}
.badge-info {{ background: rgba(99,102,241,0.2); color: #A5B4FC; }}
.activity-item {{
background: rgba(30,41,59,0.55);
border: 1px solid rgba(255,255,255,0.06);
border-radius: 14px;
padding: 0.85rem 1rem;
margin-bottom: 0.5rem;
display: flex;
gap: 0.75rem;
align-items: flex-start;
}}
.activity-item .icon {{
font-size: 1.25rem;
line-height: 1;
}}
.activity-item .body {{
flex: 1;
}}
.activity-item .title {{
color: #E2E8F0;
font-weight: 600;
font-size: 0.88rem;
margin: 0;
}}
.activity-item .meta {{
color: #64748B;
font-size: 0.75rem;
margin: 0.2rem 0 0;
}}
.room-tile {{
border-radius: 12px;
padding: 0.65rem 0.5rem;
text-align: center;
font-size: 0.72rem;
font-weight: 600;
border: 1px solid rgba(255,255,255,0.08);
}}
.room-vacant {{ background: rgba(16,185,129,0.15); color: #6EE7B7; }}
.room-partial {{ background: rgba(245,158,11,0.15); color: #FCD34D; }}
.room-full {{ background: rgba(99,102,241,0.15); color: #A5B4FC; }}
.panel {{
background: rgba(30,41,59,0.55);
border: 1px solid rgba(255,255,255,0.06);
border-radius: 18px;
padding: 1.25rem;
margin-bottom: 1rem;
}}
.panel-title {{
color: #E2E8F0;
font-weight: 700;
font-size: 0.95rem;
margin: 0 0 0.75rem;
}}
.footer-note {{
text-align: center;
color: #64748B;
font-size: 0.75rem;
padding: 1.5rem 0 0.5rem;
border-top: 1px solid rgba(255,255,255,0.05);
margin-top: 2rem;
}}
div[data-testid="stMetric"] {{
background: rgba(30,41,59,0.6);
border: 1px solid rgba(255,255,255,0.06);
border-radius: 14px;
padding: 0.75rem 1rem;
}}
.stTabs [data-baseweb="tab-list"] {{ gap: 0.5rem; }}
.stTabs [data-baseweb="tab"] {{ border-radius: 10px; padding: 0.5rem 1rem; }}
div.stButton > button[kind="primary"] {{
background: linear-gradient(135deg, #6366F1, #8B5CF6);
border: none;
font-weight: 600;
border-radius: 12px;
transition: opacity 0.2s ease;
}}
div.stButton > button[kind="primary"]:hover {{
opacity: 0.92;
border: none;
}}
</style>
""",
unsafe_allow_html=True,
)
def sidebar_brand():
st.markdown(
"""
<div class="sidebar-brand">
<div class="logo">馃彔</div>
<h3>Hostel MS</h3>
<p>UCP BSAI 路 PDC Lab</p>
</div>
""",
unsafe_allow_html=True,
)
def hero(title: str, subtitle: str, badge: str = ""):
badge_html = f'<span class="badge badge-info">{badge}</span> 路 ' if badge else ""
st.markdown(
f"""
<div class="hero">
<h1>{title}</h1>
<p>{badge_html}{subtitle}</p>
</div>
""",
unsafe_allow_html=True,
)
def metrics_row(items: list[tuple[str, str, str | None]]):
cols = st.columns(len(items))
for col, item in zip(cols, items):
val, label = item[0], item[1]
delta = item[2] if len(item) > 2 else None
delta_html = f'<div class="delta">{delta}</div>' if delta else ""
col.markdown(
f"""
<div class="stat-card">
<p class="value">{val}</p>
<p class="label">{label}</p>
{delta_html}
</div>
""",
unsafe_allow_html=True,
)
def feature_grid(features: list[tuple[str, str, str]]):
cols = st.columns(len(features))
for col, (icon, title, desc) in zip(cols, features):
col.markdown(
f"""
<div class="feature-card">
<h4>{icon} {title}</h4>
<p>{desc}</p>
</div>
""",
unsafe_allow_html=True,
)
def activity_feed(items: list[tuple[str, str, str]]):
for icon, title, meta in items:
st.markdown(
f"""
<div class="activity-item">
<span class="icon">{icon}</span>
<div class="body">
<p class="title">{title}</p>
<p class="meta">{meta}</p>
</div>
</div>
""",
unsafe_allow_html=True,
)
def room_grid(rooms: list[dict]):
cols = st.columns(min(len(rooms), 5))
for i, r in enumerate(rooms):
status = r.get("status", "Vacant")
css = "room-vacant" if status == "Vacant" else "room-partial" if status == "Partial" else "room-full"
cols[i % 5].markdown(
f"""
<div class="room-tile {css}">
{r['number']}<br>
{r['occupied']}/{r['capacity']}
</div>
""",
unsafe_allow_html=True,
)
def page_footer():
st.markdown(
"""
<div class="footer-note">
Hostel Management System 路 PDC Lab Project 路 UCP BSAI<br>
Ch Hamza Younas & Muhammad Abdullah 路 Instructor: Ma'am Khadija
</div>
""",
unsafe_allow_html=True,
)
def status_badge(text: str, kind: str = "info") -> str:
return f'<span class="badge badge-{kind}">{text}</span>'