| import streamlit as st |
| import pandas as pd |
| import plotly.express as px |
| import plotly.graph_objects as go |
| from datetime import datetime, timedelta |
|
|
| |
| st.set_page_config( |
| page_title="Supply Chain Intelligence", |
| page_icon="π", |
| layout="wide", |
| initial_sidebar_state="expanded" |
| ) |
|
|
| |
| 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) |
|
|
| |
| @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 |
| } |
|
|
| |
| 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) |
|
|
| |
| 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) |
| |
| |
| 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") |
|
|
| |
| 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) |
|
|
| |
| material_df = get_material_data() |
| metrics = get_enhanced_metrics() |
|
|
| |
| 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) |
|
|
| |
| st.markdown('<div class="section-header">Material Group Performance</div>', unsafe_allow_html=True) |
|
|
| |
| st.dataframe(material_df, use_container_width=True, hide_index=True) |
|
|
| |
| st.markdown('<div class="section-header">Performance Analytics</div>', unsafe_allow_html=True) |
|
|
| col1, col2 = st.columns(2) |
|
|
| with col1: |
| |
| 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: |
| |
| 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) |
|
|
| |
| 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) |
|
|