#Stable version for Yazaki India Ltd
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta
import random
# Page configuration
st.set_page_config(
page_title="Yazaki India Ltd - Complete Supply Chain Hub",
page_icon="🌐",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS (same as before)
st.markdown("""
""", unsafe_allow_html=True)
# Initialize session state
if 'executed_mitigations' not in st.session_state:
st.session_state.executed_mitigations = []
if 'external_signals' not in st.session_state:
st.session_state.external_signals = []
# UPDATED: Generate 8-week forward-looking demand data
@st.cache_data
def generate_8week_demand_data():
today = datetime(2025, 8, 4)
dates = [today + timedelta(days=x) for x in range(56)] # 8 weeks = 56 days
materials = [
'YAZ001-Wiring Harness',
'YAZ002-Connectors',
'YAZ003-Terminals',
'YAZ004-Sensors',
'YAZ005-Cable Assemblies'
]
all_data = []
for material in materials:
np.random.seed(hash(material) % 1000)
# Generate base demand patterns
base_demand = np.random.normal(150, 15, 56)
# First 14 days: FIRM DEMAND
firm_demand = np.clip(base_demand[:14], 100, 200).astype(int)
# Days 15-56: Customer shared demand (tentative)
customer_shared = np.clip(base_demand[14:] * (1 + 0.05 * np.sin(np.linspace(0, 3.14, 42))), 80, 220).astype(int)
# Days 15-56: AI-corrected demand (with external signals)
external_factors = np.zeros(42)
# Weather impact (weeks 3-4)
external_factors[0:14] += np.random.normal(0, 5, 14)
# EV policy impact (weeks 5-8)
if 'YAZ' in material:
external_factors[14:] += 10
# Festive season boost (weeks 6-7)
external_factors[28:42] += 8
corrected_demand = np.clip(customer_shared + external_factors, 60, 250).astype(int)
# Generate supply plan for 56 days
supply_capacity = np.random.normal(155, 12, 56)
supply_plan = np.clip(supply_capacity, 120, 220).astype(int)
# Apply disruptions to supply (weather impact on days 15-18)
supply_actual = supply_plan.copy()
supply_actual[15:19] = (supply_actual[15:19] * 0.8).astype(int)
for i, date in enumerate(dates):
# Determine which demand to use
if i < 14:
demand_used = firm_demand[i]
firm_val = firm_demand[i]
customer_val = None
corrected_val = None
demand_type = "Firm"
else:
demand_used = corrected_demand[i-14]
firm_val = None
customer_val = customer_shared[i-14]
corrected_val = corrected_demand[i-14]
demand_type = "AI-Corrected"
# Calculate shortfall
shortfall = max(0, demand_used - supply_actual[i])
all_data.append({
'Date': date,
'Week': f"Week {(i//7)+1}",
'Day': i + 1,
'Material': material,
'Firm_Demand': firm_val,
'Customer_Demand': customer_val,
'Corrected_Demand': corrected_val,
'Demand_Used': demand_used,
'Supply_Plan': supply_plan[i],
'Supply_Projected': supply_actual[i],
'Shortfall': shortfall,
'Demand_Type': demand_type,
'Gap': supply_actual[i] - demand_used
})
return pd.DataFrame(all_data)
# UPDATED: Tier-2 suppliers for Yazaki India
@st.cache_data
def get_tier2_suppliers():
return {
'Electro Components Pvt Ltd': {
'location': 'Chennai',
'materials': ['YAZ001-Wiring Harness', 'YAZ002-Connectors'],
'capacity': 210,
'reliability': 93,
'lead_time': 3,
'risk_factors': ['Port delays', 'Power outages', 'Labor strikes']
},
'Connectix Solutions': {
'location': 'Ahmedabad',
'materials': ['YAZ003-Terminals', 'YAZ004-Sensors'],
'capacity': 190,
'reliability': 90,
'lead_time': 2,
'risk_factors': ['Raw material shortage', 'Transportation issues', 'Equipment failure']
},
'WireCraft Industries': {
'location': 'Pune',
'materials': ['YAZ005-Cable Assemblies', 'YAZ001-Wiring Harness'],
'capacity': 230,
'reliability': 87,
'lead_time': 1,
'risk_factors': ['Quality checks', 'Capacity limits', 'Supplier disputes']
}
}
# UPDATED: Ecosystem data with Yazaki-specific naming
@st.cache_data
def generate_ecosystem_data():
today = datetime(2025, 8, 4)
dates = [today + timedelta(days=x) for x in range(14)]
suppliers = get_tier2_suppliers()
all_data = []
for supplier_name, supplier_info in suppliers.items():
for material in supplier_info['materials']:
np.random.seed(hash(supplier_name + material) % 1000)
base_capacity = supplier_info['capacity']
normal_supply = np.full(14, base_capacity, dtype=int)
disrupted_supply = normal_supply.copy()
if supplier_name == 'Electro Components Pvt Ltd':
disrupted_supply[3:7] = (disrupted_supply[3:7] * 0.3).astype(int)
disruption_cause = "Port delays in Chennai affecting imports"
disruption_days = list(range(3, 7))
elif supplier_name == 'Connectix Solutions':
disrupted_supply[5:8] = (disrupted_supply[5:8] * 0.5).astype(int)
disruption_cause = "Critical equipment failure at Ahmedabad facility"
disruption_days = list(range(5, 8))
elif supplier_name == 'WireCraft Industries':
disrupted_supply[8:11] = (disrupted_supply[8:11] * 0.2).astype(int)
disruption_cause = "Labor strike at Pune facility"
disruption_days = list(range(8, 11))
else:
disruption_cause = "No disruption"
disruption_days = []
lead_time = supplier_info['lead_time']
yazaki_supply = np.full(14, base_capacity, dtype=int)
for disruption_day in disruption_days:
arrival_day = disruption_day + lead_time
if arrival_day < 14:
reduction = normal_supply[disruption_day] - disrupted_supply[disruption_day]
yazaki_supply[arrival_day] = max(yazaki_supply[arrival_day] - reduction, 0)
for i, date in enumerate(dates):
all_data.append({
'Date': date,
'Supplier': supplier_name,
'Material': material,
'Tier2_Normal_Supply': int(normal_supply[i]),
'Tier2_Disrupted_Supply': int(disrupted_supply[i]),
'Tier2_Impact': int(normal_supply[i] - disrupted_supply[i]),
'Yazaki_Normal_Supply': int(normal_supply[i]),
'Yazaki_Impacted_Supply': int(yazaki_supply[i]),
'Yazaki_Impact': int(normal_supply[i] - yazaki_supply[i]),
'Disruption_Cause': disruption_cause if i in disruption_days else "Normal Operations",
'Lead_Time_Days': lead_time,
'Is_Disrupted': i in disruption_days,
'Is_Yazaki_Impacted': yazaki_supply[i] < normal_supply[i]
})
return pd.DataFrame(all_data)
# External signals (unchanged)
@st.cache_data
def get_external_signals():
return [
{'Source': 'Weather API', 'Signal': 'Heavy rains forecasted in Chennai for next 3 days', 'Impact': 'Supply Risk', 'Confidence': 95},
{'Source': 'Market Intelligence', 'Signal': 'EV sales up 25% this quarter', 'Impact': 'Demand Increase', 'Confidence': 88},
{'Source': 'News Analytics', 'Signal': 'Upcoming festive season - historically 15% demand spike', 'Impact': 'Demand Surge', 'Confidence': 92},
{'Source': 'Supplier Network', 'Signal': 'Tier-2 supplier capacity increased by 20%', 'Impact': 'Supply Boost', 'Confidence': 98},
{'Source': 'Social Media', 'Signal': 'Positive sentiment around new Maruti EV model', 'Impact': 'Demand Growth', 'Confidence': 75},
{'Source': 'Government Portal', 'Signal': 'New EV subsidy policy effective next week', 'Impact': 'Market Expansion', 'Confidence': 100}
]
# UPDATED: Generate alerts for 8-week data
def generate_detailed_alerts(df):
alerts = []
for material in df['Material'].unique():
material_data = df[df['Material'] == material]
shortage_days = material_data[material_data['Shortfall'] > 5]
if not shortage_days.empty:
for _, row in shortage_days.iterrows():
root_causes = []
if row['Day'] > 14:
if row['Corrected_Demand'] and row['Customer_Demand']:
diff = row['Corrected_Demand'] - row['Customer_Demand']
if diff > 10:
root_causes.append(f"AI detected {diff} units additional demand from external signals")
if row['Day'] >= 15 and row['Day'] <= 18:
root_causes.append("Chennai plant weather disruption reducing supply")
else:
root_causes.append("Firm demand exceeding supply capacity")
if not root_causes:
root_causes.append("Base demand exceeding current supply capacity")
mitigation_options = [
{"option": "Activate Pune backup production", "impact": "+30 units/day", "cost": "High", "timeline": "24 hours"},
{"option": "Expedite Tier-2 supplier shipments", "impact": "+15 units/day", "cost": "Medium", "timeline": "12 hours"},
{"option": "Emergency air freight from backup suppliers", "impact": "+40 units/day", "cost": "Very High", "timeline": "6 hours"},
{"option": "Reallocate inventory from other plants", "impact": "+20 units/day", "cost": "Low", "timeline": "18 hours"}
]
if row['Shortfall'] > 30:
best_option = mitigation_options[2]
elif row['Shortfall'] > 15:
best_option = mitigation_options[0]
else:
best_option = mitigation_options[1]
alerts.append({
'material': material,
'date': row['Date'].strftime('%Y-%m-%d'),
'week': row['Week'],
'shortage': int(row['Shortfall']),
'demand_type': row['Demand_Type'],
'severity': 'Critical' if row['Shortfall'] > 30 else 'High' if row['Shortfall'] > 15 else 'Medium',
'root_causes': root_causes,
'mitigation_options': mitigation_options,
'best_option': best_option
})
return alerts
# Keep mitigation strategies unchanged
def generate_mitigation_strategies(supplier, material, impact_amount, impact_days):
base_strategies = [
{
'strategy': 'Activate Alternate Supplier',
'description': f'Engage backup supplier for {material}',
'timeline': '24-48 hours',
'cost': 'High (+15% unit cost)',
'effectiveness': '90%',
'capacity': f'+{impact_amount * 0.9:.0f} units/day',
},
{
'strategy': 'Emergency Air Freight',
'description': f'Air freight {material} from other regions',
'timeline': '6-12 hours',
'cost': 'Very High (+40% logistics cost)',
'effectiveness': '75%',
'capacity': f'+{impact_amount * 0.75:.0f} units/day',
},
{
'strategy': 'Inventory Reallocation',
'description': f'Reallocate {material} from other plants',
'timeline': '12-24 hours',
'cost': 'Medium (+5% handling cost)',
'effectiveness': '60%',
'capacity': f'+{impact_amount * 0.6:.0f} units/day',
}
]
if impact_amount > 100:
recommended = [0, 1]
elif impact_amount > 50:
recommended = [0, 2]
else:
recommended = [2]
return base_strategies, recommended
# Load data
df_demand = generate_8week_demand_data()
df_ecosystem = generate_ecosystem_data()
external_signals = get_external_signals()
suppliers = get_tier2_suppliers()
# Simple title (header removed as requested)
st.title("Supply Chain Command Center")
# Tab Navigation (same as before)
st.sidebar.title("🎯 Dashboard Navigation")
dashboard_tab = st.sidebar.radio(
"Select Dashboard:",
["📊 Demand & Supply Forecast", "🌐 Ecosystem Supplier Impact", "🛡️ Buffer Optimizer"],
index=0
)
# UPDATED TAB 1: 8-WEEK DEMAND & SUPPLY FORECAST
if dashboard_tab == "📊 Demand & Supply Forecast":
st.markdown("""
""", unsafe_allow_html=True)
# Material selection
selected_materials_demand = st.sidebar.multiselect(
"Focus Materials:",
df_demand['Material'].unique(),
default=df_demand['Material'].unique()[:3]
)
# Week filter
week_filter = st.sidebar.selectbox(
"Focus on Weeks:",
["All 8 Weeks", "Weeks 1-2 (Firm)", "Weeks 3-4", "Weeks 5-6", "Weeks 7-8"],
index=0
)
# Filter data
filtered_df_demand = df_demand[df_demand['Material'].isin(selected_materials_demand)]
if week_filter != "All 8 Weeks":
if week_filter == "Weeks 1-2 (Firm)":
filtered_df_demand = filtered_df_demand[filtered_df_demand['Day'] <= 14]
elif week_filter == "Weeks 3-4":
filtered_df_demand = filtered_df_demand[(filtered_df_demand['Day'] > 14) & (filtered_df_demand['Day'] <= 28)]
elif week_filter == "Weeks 5-6":
filtered_df_demand = filtered_df_demand[(filtered_df_demand['Day'] > 28) & (filtered_df_demand['Day'] <= 42)]
else: # Weeks 7-8
filtered_df_demand = filtered_df_demand[filtered_df_demand['Day'] > 42]
# Generate and display alerts
st.subheader("🚨 8-Week Supply Chain Alerts")
alerts = generate_detailed_alerts(filtered_df_demand)
if alerts:
for i, alert in enumerate(alerts[:3]):
st.markdown(f"""
⚠️ {alert['material']} - {alert['severity']} Shortage Alert
Date: {alert['date']} ({alert['week']}) | Shortage: {alert['shortage']} units | Type: {alert['demand_type']}
""", unsafe_allow_html=True)
st.markdown("**🔍 Root Cause Analysis:**")
for cause in alert['root_causes']:
st.markdown(f"""
🎯 {cause}
""", unsafe_allow_html=True)
st.markdown("**⚡ Mitigation Options:**")
for option in alert['mitigation_options']:
is_best = option == alert['best_option']
option_class = "best-option" if is_best else "mitigation"
best_indicator = "🏆 **RECOMMENDED** " if is_best else ""
st.markdown(f"""
{best_indicator}{option['option']}
📈 Impact: {option['impact']} | 💰 Cost: {option['cost']} | ⏱️ Timeline: {option['timeline']}
""", unsafe_allow_html=True)
col1, col2, col3 = st.columns([2, 1, 1])
with col1:
if st.button(f"✅ Implement Solution", key=f"demand_implement_{i}"):
st.success(f"Implementing: {alert['best_option']['option']}")
st.markdown("---")
else:
st.markdown("""
✅ All Good! No critical supply shortages detected in the 8-week horizon.
""", unsafe_allow_html=True)
# UPDATED: 8-Week Detailed Planning Table
st.subheader("📋 8-Week Demand-Supply Planning Table")
# Prepare display table
display_df = filtered_df_demand.copy()
display_df['Date_Display'] = display_df['Date'].dt.strftime('%m-%d')
# Create styled table
table_cols = ['Date_Display', 'Week', 'Material', 'Firm_Demand', 'Customer_Demand',
'Corrected_Demand', 'Supply_Projected', 'Shortfall']
table_data = display_df[table_cols].copy()
table_data.columns = ['Date', 'Week', 'Material', 'Firm Demand', 'Customer Demand',
'Corrected Demand', 'Supply Plan', 'Shortfall']
# Color coding function
def highlight_shortfall(val):
if pd.isna(val):
return ''
return 'background-color: #ffcccc' if val > 0 else ''
def highlight_firm_period(row):
if pd.notna(row['Firm Demand']):
return ['background-color: #e6f3ff'] * len(row)
return [''] * len(row)
# Apply styling
styled_table = table_data.style.applymap(highlight_shortfall, subset=['Shortfall'])
styled_table = styled_table.apply(highlight_firm_period, axis=1)
st.dataframe(styled_table, use_container_width=True, height=500)
# Weekly summary
st.subheader("📊 Weekly Summary")
weekly_summary = filtered_df_demand.groupby(['Week', 'Material']).agg({
'Demand_Used': 'sum',
'Supply_Projected': 'sum',
'Shortfall': 'sum'
}).reset_index()
weekly_summary['Balance'] = weekly_summary['Supply_Projected'] - weekly_summary['Demand_Used']
st.dataframe(weekly_summary, use_container_width=True)
# Enhanced visualization
st.subheader("📈 8-Week Demand vs Supply Outlook")
for material in selected_materials_demand:
material_data = filtered_df_demand[filtered_df_demand['Material'] == material]
st.markdown(f"**{material}**")
fig = go.Figure()
# Add demand used line
fig.add_trace(go.Scatter(
x=material_data['Date'],
y=material_data['Demand_Used'],
mode='lines+markers',
name='Demand Used',
line=dict(color='blue', width=3),
marker=dict(size=6)
))
# Add supply line
fig.add_trace(go.Scatter(
x=material_data['Date'],
y=material_data['Supply_Projected'],
mode='lines+markers',
name='Supply Projected',
line=dict(color='green', width=3),
marker=dict(size=6)
))
# Highlight shortfall areas
shortage_data = material_data[material_data['Shortfall'] > 0]
if not shortage_data.empty:
fig.add_trace(go.Scatter(
x=shortage_data['Date'],
y=shortage_data['Supply_Projected'],
mode='markers',
name='Shortage Days',
marker=dict(color='red', size=10, symbol='x'),
))
# Mark firm demand period
firm_data = material_data[material_data['Day'] <= 14]
if not firm_data.empty:
fig.add_vrect(
x0=firm_data['Date'].min(),
x1=firm_data['Date'].max(),
fillcolor="lightblue",
opacity=0.2,
line_width=0,
annotation_text="Firm Demand Period",
annotation_position="top left"
)
fig.update_layout(
title=f'{material} - 8-Week Supply vs Demand Forecast',
xaxis_title='Date',
yaxis_title='Units',
height=400,
showlegend=True,
hovermode='x unified'
)
st.plotly_chart(fig, use_container_width=True)
# External demand sensing (same as before)
st.subheader("📡 Real-time External Demand Sensing")
col1, col2 = st.columns(2)
with col1:
st.write("**Active External Signals:**")
for signal in external_signals:
confidence_color = "🟢" if signal['Confidence'] > 90 else "🟡" if signal['Confidence'] > 80 else "🟠"
st.markdown(f"""
{confidence_color} {signal['Source']}
{signal['Signal']}
Impact: {signal['Impact']} | Confidence: {signal['Confidence']}%
""", unsafe_allow_html=True)
with col2:
st.write("**8-Week Scenario Planning:**")
scenario = st.selectbox("Select Scenario to Test:",
["Base Case", "Extended Monsoon", "Sustained EV Boost", "Supply Chain Strike"])
if st.button("🎮 Run 8-Week Scenario", key="demand_scenario"):
if scenario == "Extended Monsoon":
st.error("Scenario: 30% supply reduction for 3 weeks. Activating multi-tier contingency plans...")
elif scenario == "Sustained EV Boost":
st.warning("Scenario: 25% demand increase for 6 weeks. Scaling ecosystem capacity...")
elif scenario == "Supply Chain Strike":
st.info("Scenario: Multi-supplier disruption. Implementing emergency protocols...")
# Keep TAB 2 and TAB 3 unchanged from previous version, but replace Rane with Yazaki in variables and text
elif dashboard_tab == "🌐 Ecosystem Supplier Impact":
st.markdown("""
""", unsafe_allow_html=True)
selected_suppliers = st.sidebar.multiselect(
"Monitor Suppliers:",
list(suppliers.keys()),
default=list(suppliers.keys())
)
st.subheader("🚨 Live Ecosystem Supply Chain Alerts")
ecosystem_alerts = []
for supplier in selected_suppliers:
supplier_data = df_ecosystem[df_ecosystem['Supplier'] == supplier]
disrupted_data = supplier_data[supplier_data['Is_Disrupted'] == True]
if not disrupted_data.empty:
for material in disrupted_data['Material'].unique():
material_disruptions = disrupted_data[disrupted_data['Material'] == material]
total_impact = material_disruptions['Tier2_Impact'].sum()
impact_days = len(material_disruptions)
first_impact_date = material_disruptions['Date'].min()
yazaki_impacted = supplier_data[
(supplier_data['Material'] == material) &
(supplier_data['Is_Yazaki_Impacted'] == True)
]
if not yazaki_impacted.empty:
yazaki_impact_start = yazaki_impacted['Date'].min()
yazaki_impact_days = len(yazaki_impacted)
yazaki_total_impact = yazaki_impacted['Yazaki_Impact'].sum()
ecosystem_alerts.append({
'supplier': supplier,
'material': material,
'disruption_cause': material_disruptions.iloc[0]['Disruption_Cause'],
'tier2_impact_start': first_impact_date,
'tier2_impact_days': impact_days,
'tier2_total_impact': total_impact,
'yazaki_impact_start': yazaki_impact_start,
'yazaki_impact_days': yazaki_impact_days,
'yazaki_total_impact': yazaki_total_impact,
'lead_time': material_disruptions.iloc[0]['Lead_Time_Days']
})
if ecosystem_alerts:
for alert in ecosystem_alerts:
st.markdown(f"""
⚠️ Tier 2 Supplier Disruption Alert
Supplier: {alert['supplier']} | Material: {alert['material']}
Root Cause: {alert['disruption_cause']}
""", unsafe_allow_html=True)
col1, col2 = st.columns(2)
with col1:
st.markdown("**🏭 Tier 2 Supplier Impact:**")
st.markdown(f"""
📅 Impact Period: {alert['tier2_impact_start'].strftime('%Y-%m-%d')} ({alert['tier2_impact_days']} days)
📉 Total Supply Lost: {alert['tier2_total_impact']} units
🎯 Daily Impact: {alert['tier2_total_impact'] // alert['tier2_impact_days']} units/day
""", unsafe_allow_html=True)
with col2:
st.markdown("**⚙️ Yazaki India Ltd Impact (with Lead Time):**")
st.markdown(f"""
📅 Impact Period: {alert['yazaki_impact_start'].strftime('%Y-%m-%d')} ({alert['yazaki_impact_days']} days)
📉 Total Supply Lost: {alert['yazaki_total_impact']} units
⏱️ Lead Time Delay: {alert['lead_time']} days
""", unsafe_allow_html=True)
strategies, recommended_indices = generate_mitigation_strategies(
alert['supplier'],
alert['material'],
alert['yazaki_total_impact'] // alert['yazaki_impact_days'],
alert['yazaki_impact_days']
)
st.markdown("**🤖 Agentic AI Mitigation Strategies:**")
for i, strategy in enumerate(strategies):
is_recommended = i in recommended_indices
is_executed = f"eco_{alert['supplier']}_{alert['material']}_{i}" in st.session_state.executed_mitigations
if is_executed:
card_class = "mitigation-executed"
status_prefix = "✅ **EXECUTED** "
elif is_recommended:
card_class = "mitigation-recommended"
status_prefix = "🏆 **AI RECOMMENDED** "
else:
card_class = "mitigation-recommended"
status_prefix = ""
st.markdown(f"""
{status_prefix}{strategy['strategy']}
📋 {strategy['description']}
⏱️ Timeline: {strategy['timeline']} | 💰 Cost: {strategy['cost']}
📈 Effectiveness: {strategy['effectiveness']} | 🚀 Capacity: {strategy['capacity']}
""", unsafe_allow_html=True)
strategy_key = f"eco_{alert['supplier']}_{alert['material']}_{i}"
col1, col2 = st.columns([2, 1])
with col1:
if not is_executed:
if st.button(f"🚀 Execute Strategy", key=f"execute_{strategy_key}"):
st.session_state.executed_mitigations.append(strategy_key)
st.success(f"Executing: {strategy['strategy']}")
st.rerun()
else:
st.success("Strategy Active")
with col2:
if is_recommended:
st.button("🏆 Recommended", key=f"rec_{strategy_key}", disabled=True)
st.markdown("---")
else:
st.markdown("""
✅ Ecosystem Healthy! No supplier disruptions detected in the current timeframe.
""", unsafe_allow_html=True)
st.subheader("📊 Ecosystem Supply Chain Flow Visualization")
fig = go.Figure()
for supplier in selected_suppliers:
supplier_data = df_ecosystem[df_ecosystem['Supplier'] == supplier]
sample_material = supplier_data['Material'].iloc[0]
material_data = supplier_data[supplier_data['Material'] == sample_material]
fig.add_trace(go.Scatter(
x=material_data['Date'],
y=material_data['Tier2_Disrupted_Supply'],
mode='lines+markers',
name=f'{supplier} (Tier 2)',
line=dict(width=2, dash='dash'),
marker=dict(size=6)
))
fig.add_trace(go.Scatter(
x=material_data['Date'],
y=material_data['Yazaki_Impacted_Supply'],
mode='lines+markers',
name=f'Yazaki Impact from {supplier}',
line=dict(width=3),
marker=dict(size=8)
))
fig.update_layout(
title='Tier 2 Supplier Disruptions → Yazaki India Ltd Supply Impact',
xaxis_title='Date',
yaxis_title='Supply Units',
height=500,
showlegend=True,
hovermode='x unified'
)
st.plotly_chart(fig, use_container_width=True)
# TAB 3: BUFFER OPTIMIZER (same as before)
elif dashboard_tab == "🛡️ Buffer Optimizer":
st.markdown("""
""", unsafe_allow_html=True)
service_level = st.slider("Target Service Level (%)", 90, 99, 95)
review_period = st.number_input("Inventory Review Period (days)", min_value=1, max_value=14, value=1)
z_factor = {90: 1.28, 92: 1.41, 95: 1.64, 97: 1.88, 98: 2.05, 99: 2.33}
Z = z_factor.get(service_level, 1.64)
# Use 8-week demand data for buffer calculation
demand_stats = (df_demand
.groupby("Material")
.agg(DailyMean=("Demand_Used", "mean"),
Sigma=("Demand_Used", "std"))
.reset_index())
lead_times = (df_ecosystem
.groupby("Material")
.agg(LeadTime=("Lead_Time_Days", "max"))
.reset_index())
current_buffers = (df_demand[df_demand["Day"] == 1]
.loc[:, ["Material", "Supply_Projected"]]
.rename(columns={"Supply_Projected": "OnHand"}))
buffer_df = (demand_stats.merge(lead_times, on="Material")
.merge(current_buffers, on="Material", how="left"))
buffer_df["RecommendedBuffer"] = (
Z * buffer_df["Sigma"] * np.sqrt(buffer_df["LeadTime"] + review_period)
).round()
buffer_df["Delta"] = buffer_df["RecommendedBuffer"] - buffer_df["OnHand"]
buffer_df["Action"] = np.where(buffer_df["Delta"] > 50,
"Increase buffer",
np.where(buffer_df["Delta"] < -50,
"Reduce buffer", "OK"))
st.subheader("📋 Buffer Recommendations")
display_cols = ["Material", "OnHand", "RecommendedBuffer", "Delta", "Action"]
st.dataframe(buffer_df[display_cols], use_container_width=True, height=300)
st.subheader("💰 Cost Impact Analysis")
carrying_cost = st.number_input("Annual Carrying Cost (% of unit cost)", min_value=0, max_value=50, value=20)
unit_cost = 100
buffer_df["CostImpact(₹)"] = (buffer_df["Delta"] * unit_cost * (carrying_cost/100) / 12)
cost_chart_data = buffer_df.set_index("Material")["CostImpact(₹)"]
st.bar_chart(cost_chart_data)
st.subheader("⚡ Execute AI Recommendations")
for _, row in buffer_df.iterrows():
if row["Action"] != "OK":
if st.button(f"🚀 {row['Action']} for {row['Material']}", key=row["Material"]):
st.success(f"AI executed: {row['Action']} - Adjusting {int(row['Delta'])} units for {row['Material']}")
# Performance summary
st.subheader("📊 Performance Summary")
col1, col2, col3, col4 = st.columns(4)
if dashboard_tab == "📊 Demand & Supply Forecast":
filtered_df = filtered_df_demand if 'filtered_df_demand' in locals() else df_demand
total_shortage_days = len(filtered_df[filtered_df['Shortfall'] > 0])
critical_shortage_days = len(filtered_df[filtered_df['Shortfall'] > 30])
materials_at_risk = len(filtered_df[filtered_df['Shortfall'] > 5]['Material'].unique())
avg_shortfall = filtered_df['Shortfall'].mean()
with col1:
st.metric("Days with Shortages", f"{total_shortage_days}")
with col2:
st.metric("Critical Days", f"{critical_shortage_days}")
with col3:
st.metric("Materials at Risk", f"{materials_at_risk}")
with col4:
st.metric("Avg Daily Shortfall", f"{avg_shortfall:.1f} units")
elif dashboard_tab == "🌐 Ecosystem Supplier Impact":
total_suppliers_disrupted = len(df_ecosystem[df_ecosystem['Is_Disrupted'] == True]['Supplier'].unique())
total_yazaki_impact_days = len(df_ecosystem[df_ecosystem['Is_Yazaki_Impacted'] == True])
total_mitigation_strategies = len([s for s in st.session_state.executed_mitigations if 'eco_' in s])
avg_lead_time = df_ecosystem['Lead_Time_Days'].mean()
with col1:
st.metric("Suppliers Disrupted", f"{total_suppliers_disrupted}")
with col2:
st.metric("Yazaki Impact Days", f"{total_yazaki_impact_days}")
with col3:
st.metric("Active Mitigations", f"{total_mitigation_strategies}")
with col4:
st.metric("Avg Lead Time", f"{avg_lead_time:.1f} days")
else: # Buffer Optimizer
if 'buffer_df' in locals():
total_materials = len(buffer_df)
materials_need_increase = len(buffer_df[buffer_df['Action'] == 'Increase buffer'])
materials_need_decrease = len(buffer_df[buffer_df['Action'] == 'Reduce buffer'])
total_cost_impact = buffer_df['CostImpact(₹)'].sum()
with col1:
st.metric("Total Materials", f"{total_materials}")
with col2:
st.metric("Need Buffer Increase", f"{materials_need_increase}")
with col3:
st.metric("Need Buffer Reduction", f"{materials_need_decrease}")
with col4:
st.metric("Monthly Cost Impact", f"₹{total_cost_impact:,.0f}")
# Footer
st.markdown("---")
st.markdown("""
🌐 Yazaki India Ltd 8-Week Supply Chain Command Center | Firm + AI-Corrected Demand | Ecosystem Intelligence + Buffer Optimization
Powered by Agentic AI | 8-Week Planning Horizon | Comprehensive Supply Chain Resilience
""", unsafe_allow_html=True)