streamlit / src /streamlit_app.py
PD03's picture
Update src/streamlit_app.py
20a11e3 verified
import streamlit as st
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta
# Page config
st.set_page_config(
page_title="Supply Chain Intelligence",
page_icon="πŸ”—",
layout="wide",
initial_sidebar_state="expanded"
)
# Clean, professional CSS styling
st.markdown("""
<style>
/* Import modern font */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
/* Global styling */
.stApp {
font-family: 'Inter', sans-serif;
background-color: #f8fafc;
}
/* Main container */
.main-container {
background: white;
border-radius: 12px;
padding: 2rem;
margin: 1rem 0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid #e2e8f0;
}
/* Clean header */
.modern-header {
background: #1e293b;
color: white;
padding: 2rem;
border-radius: 12px;
margin-bottom: 2rem;
border-left: 4px solid #3b82f6;
}
.header-title {
font-size: 2rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: white;
}
.header-subtitle {
font-size: 1rem;
font-weight: 400;
color: #94a3b8;
}
/* Clean metric cards */
.metric-card {
background: white;
padding: 1.5rem;
border-radius: 12px;
border: 1px solid #e2e8f0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
margin-bottom: 1rem;
transition: all 0.2s ease;
}
.metric-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
border-color: #cbd5e1;
}
.metric-number {
font-size: 2.5rem;
font-weight: 700;
color: #1e293b;
margin-bottom: 0.5rem;
}
.metric-label {
color: #64748b;
font-size: 0.875rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.5rem;
}
.metric-change {
font-size: 0.875rem;
font-weight: 600;
padding: 0.25rem 0.75rem;
border-radius: 6px;
display: inline-block;
}
.metric-positive {
background-color: #dcfce7;
color: #166534;
}
.metric-negative {
background-color: #fef2f2;
color: #dc2626;
}
.metric-neutral {
background-color: #f1f5f9;
color: #475569;
}
/* Clean sidebar */
.sidebar-content {
background: white;
color: #1e293b;
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1rem;
border: 1px solid #e2e8f0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Filter section */
.filter-container {
background: white;
padding: 1.5rem;
border-radius: 12px;
margin-bottom: 2rem;
border: 1px solid #e2e8f0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Section headers */
.section-header {
font-size: 1.25rem;
font-weight: 600;
color: #1e293b;
margin-bottom: 1.5rem;
padding-bottom: 0.75rem;
border-bottom: 2px solid #e2e8f0;
}
/* Status indicators */
.status-indicator {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
}
.status-good { background-color: #22c55e; }
.status-warning { background-color: #f59e0b; }
.status-critical { background-color: #ef4444; }
/* Clean table styling */
.dataframe table {
border-collapse: collapse;
margin: 0;
font-size: 0.875rem;
width: 100%;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.dataframe th {
background-color: #f8fafc;
color: #374151;
font-weight: 600;
padding: 12px;
text-align: left;
border-bottom: 1px solid #e5e7eb;
}
.dataframe td {
padding: 12px;
border-bottom: 1px solid #f3f4f6;
}
.dataframe tr:hover {
background-color: #f9fafb;
}
/* Remove default streamlit styling */
.stSelectbox > div > div {
background-color: white;
border: 1px solid #d1d5db;
border-radius: 6px;
}
.stSelectbox > div > div:focus-within {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
</style>
""", unsafe_allow_html=True)
# Sample data functions (same as before)
@st.cache_data
def get_material_data():
return pd.DataFrame({
'Material Group': ['Engine Components', 'Hydraulic Systems', 'Interior & Seating',
'Battery Systems', 'Suspension Parts', 'Safety Components', 'Semiconductors',
'Smart Sensors', 'Brake Systems', 'Shock Absorbers'],
'Current Rate': [72, 68, 65, 73, 75, 72, 78, 77, 80, 82],
'Target Rate': [75, 70, 70, 75, 78, 75, 80, 80, 82, 85],
'Trend': ['+2.1%', '+1.8%', '+0.9%', '+2.4%', '+1.2%', '+1.8%', '+2.1%', '+1.9%', '+1.1%', '+1.2%'],
'Risk Level': ['Medium', 'High', 'High', 'Low', 'Low', 'Medium', 'Low', 'Low', 'Low', 'Low'],
'Last Updated': ['2 hrs ago', '1 hr ago', '3 hrs ago', '1 hr ago', '2 hrs ago', '1 hr ago', '30 min ago', '45 min ago', '1 hr ago', '2 hrs ago']
})
@st.cache_data
def get_enhanced_metrics():
return {
'fulfillment': 86,
'mom_change': 2.3,
'material_groups': 147,
'skus': 12847,
'material_groups_at_risk': 18,
'risk_mom_change': -1.8,
'skus_at_risk': 23,
'sku_risk_mom_change': -2.1,
'active_suppliers': 342,
'on_time_delivery': 94.2
}
# Clean, professional header
st.markdown("""
<div class="modern-header">
<div class="header-title">Supply Chain Intelligence Hub</div>
<div class="header-subtitle">Real-time Supply Chain Resilience Dashboard β€’ Control Tower Analytics</div>
</div>
""", unsafe_allow_html=True)
# Professional sidebar
with st.sidebar:
st.markdown("""
<div class="sidebar-content">
<h3 style="margin-top: 0; color: #1e293b;">Navigation</h3>
<p style="color: #64748b; font-size: 0.875rem;">Select your workspace</p>
</div>
""", unsafe_allow_html=True)
nav_options = [
"Dashboard Home",
"Supply Chain Resilience",
"Control Tower",
"Material Groups",
"Supplier Analytics",
"Demand Planning",
"Insights & Trends",
"Real-time Alerts",
"Reports Center"
]
selected_nav = st.selectbox("", nav_options, index=1)
# Clean alerts section
st.markdown("---")
st.markdown("**System Status**")
st.error("⚠️ 3 suppliers need attention")
st.warning("πŸ“‹ 12 SKUs below safety stock")
st.success("βœ… 94% on-time delivery")
# Clean filters section
st.markdown("""
<div class="filter-container">
<div class="section-header">Filters & Controls</div>
</div>
""", unsafe_allow_html=True)
col1, col2, col3, col4 = st.columns(4)
col5, col6, col7, col8 = st.columns(4)
with col1:
plant_location = st.selectbox("Plant Location", ["Chennai Hub", "Mumbai Center", "Delhi North", "Bangalore Tech"], index=0)
with col2:
material_group = st.selectbox("Material Category", ["All Categories", "Critical Components", "Standard Parts"], index=0)
with col3:
time_period = st.selectbox("Time Period", ["Current Quarter", "FY2025", "Last 6 Months"], index=0)
with col4:
supplier_tier = st.selectbox("Supplier Tier", ["All Tiers", "Tier 1 Strategic", "Tier 2 Operational"], index=0)
with col5:
risk_level = st.selectbox("Risk Level", ["All Levels", "High Risk Only", "Medium Risk", "Low Risk"], index=0)
with col6:
performance = st.selectbox("Performance", ["All Performance", "Above Target", "Below Target"], index=0)
with col7:
geography = st.selectbox("Geography", ["Global View", "Asia Pacific", "Americas", "Europe"], index=0)
with col8:
update_freq = st.selectbox("Update Frequency", ["Real-time", "Hourly", "Daily"], index=0)
# Get data
material_df = get_material_data()
metrics = get_enhanced_metrics()
# Clean metrics section
st.markdown('<div class="section-header">Key Performance Indicators</div>', unsafe_allow_html=True)
col1, col2, col3, col4 = st.columns(4)
with col1:
st.markdown(f"""
<div class="metric-card">
<div class="metric-number">{metrics['fulfillment']}%</div>
<div class="metric-label">Overall Fulfillment</div>
<div class="metric-change metric-positive">β†— +{metrics['mom_change']}% MoM</div>
</div>
""", unsafe_allow_html=True)
with col2:
st.markdown(f"""
<div class="metric-card">
<div class="metric-number">{metrics['on_time_delivery']}%</div>
<div class="metric-label">On-Time Delivery</div>
<div class="metric-change metric-positive">β†— +1.2% WoW</div>
</div>
""", unsafe_allow_html=True)
with col3:
st.markdown(f"""
<div class="metric-card">
<div class="metric-number">{metrics['material_groups_at_risk']}%</div>
<div class="metric-label">At-Risk Categories</div>
<div class="metric-change metric-positive">β†˜ {metrics['risk_mom_change']}% MoM</div>
</div>
""", unsafe_allow_html=True)
with col4:
st.markdown(f"""
<div class="metric-card">
<div class="metric-number">{metrics['active_suppliers']:,}</div>
<div class="metric-label">Active Suppliers</div>
<div class="metric-change metric-neutral">+15 new</div>
</div>
""", unsafe_allow_html=True)
# Clean data table
st.markdown('<div class="section-header">Material Group Performance</div>', unsafe_allow_html=True)
# Display clean table
st.dataframe(material_df, use_container_width=True, hide_index=True)
# Professional charts
st.markdown('<div class="section-header">Performance Analytics</div>', unsafe_allow_html=True)
col1, col2 = st.columns(2)
with col1:
# Clean bar chart
fig1 = px.bar(
material_df,
x='Material Group',
y=['Current Rate', 'Target Rate'],
title="Fulfillment Rates: Current vs Target",
color_discrete_sequence=['#3b82f6', '#64748b']
)
fig1.update_layout(
plot_bgcolor='white',
paper_bgcolor='white',
font_family="Inter",
title_font_size=14,
title_font_color="#1e293b",
xaxis_tickangle=-45,
height=400,
showlegend=True,
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1)
)
st.plotly_chart(fig1, use_container_width=True)
with col2:
# Clean pie chart
risk_counts = material_df['Risk Level'].value_counts()
fig2 = px.pie(
values=risk_counts.values,
names=risk_counts.index,
title="Risk Distribution",
color_discrete_sequence=['#22c55e', '#f59e0b', '#ef4444']
)
fig2.update_layout(
plot_bgcolor='white',
paper_bgcolor='white',
font_family="Inter",
title_font_size=14,
title_font_color="#1e293b",
height=400
)
st.plotly_chart(fig2, use_container_width=True)
# Clean footer
st.markdown("""
<div style="text-align: center; padding: 1.5rem; color: #64748b; border-top: 1px solid #e2e8f0; margin-top: 2rem; font-size: 0.875rem;">
Dashboard last updated: {timestamp} β€’ Auto-refresh: Every 15 minutes β€’ Data accuracy: 99.7%
</div>
""".format(timestamp=datetime.now().strftime("%B %d, %Y at %I:%M %p")), unsafe_allow_html=True)