PD03 commited on
Commit
f67b2bb
Β·
verified Β·
1 Parent(s): 4f19933

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +340 -118
src/streamlit_app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import streamlit as st
2
  import pandas as pd
3
  import numpy as np
@@ -16,12 +17,13 @@ st.set_page_config(
16
 
17
  # Custom CSS (same as before)
18
  st.markdown("""
 
 
19
  """, unsafe_allow_html=True)
20
 
21
  # Initialize session state
22
  if 'executed_mitigations' not in st.session_state:
23
  st.session_state.executed_mitigations = []
24
-
25
  if 'external_signals' not in st.session_state:
26
  st.session_state.external_signals = []
27
 
@@ -40,8 +42,11 @@ def generate_8week_demand_data():
40
  ]
41
 
42
  all_data = []
 
43
  for material in materials:
44
  np.random.seed(hash(material) % 1000)
 
 
45
  base_demand = np.random.normal(150, 15, 56)
46
 
47
  # First 14 days: FIRM DEMAND
@@ -54,8 +59,9 @@ def generate_8week_demand_data():
54
  external_factors = np.zeros(42)
55
  # Weather impact (weeks 3-4)
56
  external_factors[0:14] += np.random.normal(0, 5, 14)
57
- # EV policy impact (weeks 5-8), considering Yazaki is in automotive electronics
58
- external_factors[14:] += 10
 
59
  # Festive season boost (weeks 6-7)
60
  external_factors[28:42] += 8
61
 
@@ -70,6 +76,7 @@ def generate_8week_demand_data():
70
  supply_actual[15:19] = (supply_actual[15:19] * 0.8).astype(int)
71
 
72
  for i, date in enumerate(dates):
 
73
  if i < 14:
74
  demand_used = firm_demand[i]
75
  firm_val = firm_demand[i]
@@ -83,11 +90,12 @@ def generate_8week_demand_data():
83
  corrected_val = corrected_demand[i-14]
84
  demand_type = "AI-Corrected"
85
 
 
86
  shortfall = max(0, demand_used - supply_actual[i])
87
 
88
  all_data.append({
89
  'Date': date,
90
- 'Week': f"Week {(i // 7) + 1}",
91
  'Day': i + 1,
92
  'Material': material,
93
  'Firm_Demand': firm_val,
@@ -100,9 +108,10 @@ def generate_8week_demand_data():
100
  'Demand_Type': demand_type,
101
  'Gap': supply_actual[i] - demand_used
102
  })
 
103
  return pd.DataFrame(all_data)
104
 
105
- # Updated ecosystem Tier-2 suppliers for Yazaki India Ltd
106
  @st.cache_data
107
  def get_tier2_suppliers():
108
  return {
@@ -132,7 +141,7 @@ def get_tier2_suppliers():
132
  }
133
  }
134
 
135
- # Updated ecosystem data generation function for Yazaki
136
  @st.cache_data
137
  def generate_ecosystem_data():
138
  today = datetime(2025, 8, 4)
@@ -140,20 +149,22 @@ def generate_ecosystem_data():
140
  suppliers = get_tier2_suppliers()
141
 
142
  all_data = []
 
143
  for supplier_name, supplier_info in suppliers.items():
144
  for material in supplier_info['materials']:
145
  np.random.seed(hash(supplier_name + material) % 1000)
 
146
  base_capacity = supplier_info['capacity']
147
  normal_supply = np.full(14, base_capacity, dtype=int)
148
  disrupted_supply = normal_supply.copy()
149
 
150
  if supplier_name == 'Electro Components Pvt Ltd':
151
  disrupted_supply[3:7] = (disrupted_supply[3:7] * 0.3).astype(int)
152
- disruption_cause = "Port delays in Chennai"
153
  disruption_days = list(range(3, 7))
154
  elif supplier_name == 'Connectix Solutions':
155
  disrupted_supply[5:8] = (disrupted_supply[5:8] * 0.5).astype(int)
156
- disruption_cause = "Critical equipment failure"
157
  disruption_days = list(range(5, 8))
158
  elif supplier_name == 'WireCraft Industries':
159
  disrupted_supply[8:11] = (disrupted_supply[8:11] * 0.2).astype(int)
@@ -188,9 +199,10 @@ def generate_ecosystem_data():
188
  'Is_Disrupted': i in disruption_days,
189
  'Is_Yazaki_Impacted': yazaki_supply[i] < normal_supply[i]
190
  })
 
191
  return pd.DataFrame(all_data)
192
 
193
- # External signals unchanged
194
  @st.cache_data
195
  def get_external_signals():
196
  return [
@@ -198,28 +210,33 @@ def get_external_signals():
198
  {'Source': 'Market Intelligence', 'Signal': 'EV sales up 25% this quarter', 'Impact': 'Demand Increase', 'Confidence': 88},
199
  {'Source': 'News Analytics', 'Signal': 'Upcoming festive season - historically 15% demand spike', 'Impact': 'Demand Surge', 'Confidence': 92},
200
  {'Source': 'Supplier Network', 'Signal': 'Tier-2 supplier capacity increased by 20%', 'Impact': 'Supply Boost', 'Confidence': 98},
201
- {'Source': 'Social Media', 'Signal': 'Positive sentiment around new EV models', 'Impact': 'Demand Growth', 'Confidence': 75},
202
  {'Source': 'Government Portal', 'Signal': 'New EV subsidy policy effective next week', 'Impact': 'Market Expansion', 'Confidence': 100}
203
  ]
204
 
205
- # Generate alerts for 8-week data β€” with Yazaki references and unchanged logic
206
  def generate_detailed_alerts(df):
207
  alerts = []
 
208
  for material in df['Material'].unique():
209
  material_data = df[df['Material'] == material]
210
  shortage_days = material_data[material_data['Shortfall'] > 5]
 
211
  if not shortage_days.empty:
212
  for _, row in shortage_days.iterrows():
213
  root_causes = []
 
214
  if row['Day'] > 14:
215
  if row['Corrected_Demand'] and row['Customer_Demand']:
216
  diff = row['Corrected_Demand'] - row['Customer_Demand']
217
  if diff > 10:
218
  root_causes.append(f"AI detected {diff} units additional demand from external signals")
219
- if 15 <= row['Day'] <= 18:
220
- root_causes.append("Chennai plant weather disruption reducing supply")
221
- else:
222
- root_causes.append("... Firm demand exceeding supply capacity")
 
 
223
  if not root_causes:
224
  root_causes.append("Base demand exceeding current supply capacity")
225
 
@@ -248,9 +265,10 @@ def generate_detailed_alerts(df):
248
  'mitigation_options': mitigation_options,
249
  'best_option': best_option
250
  })
 
251
  return alerts
252
 
253
- # Mitigation strategies unchanged but updated for Yazaki naming
254
  def generate_mitigation_strategies(supplier, material, impact_amount, impact_days):
255
  base_strategies = [
256
  {
@@ -278,12 +296,14 @@ def generate_mitigation_strategies(supplier, material, impact_amount, impact_day
278
  'capacity': f'+{impact_amount * 0.6:.0f} units/day',
279
  }
280
  ]
 
281
  if impact_amount > 100:
282
  recommended = [0, 1]
283
  elif impact_amount > 50:
284
  recommended = [0, 2]
285
  else:
286
  recommended = [2]
 
287
  return base_strategies, recommended
288
 
289
  # Load data
@@ -292,10 +312,10 @@ df_ecosystem = generate_ecosystem_data()
292
  external_signals = get_external_signals()
293
  suppliers = get_tier2_suppliers()
294
 
295
- # Simple title
296
  st.title("Supply Chain Command Center - Yazaki India Ltd")
297
 
298
- # Tab Navigation
299
  st.sidebar.title("🎯 Dashboard Navigation")
300
  dashboard_tab = st.sidebar.radio(
301
  "Select Dashboard:",
@@ -303,132 +323,334 @@ dashboard_tab = st.sidebar.radio(
303
  index=0
304
  )
305
 
306
- # TAB 1: 8-WEEK DEMAND & SUPPLY FORECAST
307
  if dashboard_tab == "πŸ“Š Demand & Supply Forecast":
 
308
  st.markdown("""
309
- # 8-Week Demand & Supply Forecast for Yazaki India Ltd
310
-
311
- This dashboard provides firm and AI-corrected demand forecasts along with supply projections for critical Yazaki materials.
312
-
313
- """)
 
314
 
315
- # Select material filter
316
- material_selected = st.selectbox("Select Material", df_demand['Material'].unique())
317
-
318
- demand_data = df_demand[df_demand['Material'] == material_selected]
319
-
 
 
 
320
  fig = go.Figure()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  fig.add_trace(go.Scatter(
322
- x=demand_data['Date'], y=demand_data['Firm_Demand'], mode='lines+markers',
323
- name='Firm Demand (Days 1-14)'
 
 
 
 
324
  ))
 
 
325
  fig.add_trace(go.Scatter(
326
- x=demand_data['Date'], y=demand_data['Corrected_Demand'], mode='lines+markers',
327
- name='AI-Corrected Demand (Days 15-56)'
 
 
 
 
328
  ))
 
 
329
  fig.add_trace(go.Scatter(
330
- x=demand_data['Date'], y=demand_data['Supply_Projected'], mode='lines+markers',
331
- name='Projected Supply'
 
 
 
 
 
332
  ))
 
333
  fig.update_layout(
334
- yaxis_title='Units',
335
- xaxis_title='Date',
336
- legend_title='Legend',
337
- height=400
 
 
338
  )
 
339
  st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
 
341
- # Display alerts if any
342
- alerts = generate_detailed_alerts(demand_data)
343
- if alerts:
344
- st.markdown("### Supply Shortage Alerts")
345
- for alert in alerts:
346
- st.markdown(f"**Date:** {alert['date']} ({alert['week']})")
347
- st.markdown(f"**Material:** {alert['material']}")
348
- st.markdown(f"**Severity:** {alert['severity']}")
349
- st.markdown(f"**Shortage:** {alert['shortage']} units")
350
- st.markdown(f"**Demand Type:** {alert['demand_type']}")
351
- st.markdown("**Root Causes:**")
352
- for cause in alert['root_causes']:
353
- st.markdown(f"- {cause}")
354
- st.markdown("**Recommended Mitigation Options:**")
355
- for idx, option in enumerate(alert['mitigation_options']):
356
- recommended_marker = "βœ…" if option == alert['best_option'] else ""
357
- st.markdown(f"{recommended_marker} {option['option']} - Impact: {option['impact']}, Cost: {option['cost']}, Timeline: {option['timeline']}")
358
- st.markdown("---")
359
- else:
360
- st.success("No significant shortages detected for selected material in the next 8 weeks.")
361
-
362
- # TAB 2: ECOSYSTEM SUPPLIER IMPACT
363
  elif dashboard_tab == "🌐 Ecosystem Supplier Impact":
 
364
  st.markdown("""
365
- # Tier-2 Supplier Supply Impact and Risk Analysis for Yazaki India Ltd
 
 
 
 
 
366
 
367
- This dashboard visualizes the supply disruptions and cascading impacts within the Yazaki supply chain ecosystem.
368
- """)
369
 
370
- df_tier2 = df_ecosystem.copy()
 
 
 
 
371
 
372
- supplier_filter = st.multiselect("Select Supplier(s)", options=df_tier2['Supplier'].unique(), default=df_tier2['Supplier'].unique())
373
- material_filter = st.multiselect("Select Material(s)", options=df_tier2['Material'].unique(), default=df_tier2['Material'].unique())
374
 
375
- filtered_data = df_tier2[
376
- (df_tier2['Supplier'].isin(supplier_filter)) &
377
- (df_tier2['Material'].isin(material_filter))
378
- ]
 
 
 
 
 
 
 
 
 
 
 
 
379
 
380
- fig2 = px.line(
381
- filtered_data,
382
- x='Date',
383
- y=['Yazaki_Normal_Supply', 'Yazaki_Impacted_Supply'],
384
- color='Material',
385
- line_dash='Supplier',
386
- title='Supply Levels - Normal vs Impacted'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  )
388
- fig2.update_layout(yaxis_title='Units', height=450)
389
- st.plotly_chart(fig2, use_container_width=True)
390
-
391
- # Show table of risk factors and disruption causes
392
- st.markdown("### Supplier Disruption Details")
393
- for supplier in supplier_filter:
394
- if supplier in suppliers:
395
- info = suppliers[supplier]
396
- st.markdown(f"**{supplier}** (Location: {info['location']})")
397
- st.markdown(f"- Materials Supplied: {', '.join(info['materials'])}")
398
- st.markdown(f"- Capacity: {info['capacity']}")
399
- st.markdown(f"- Reliability: {info['reliability']}%")
400
- st.markdown(f"- Lead Time (days): {info['lead_time']}")
401
- st.markdown(f"- Risk Factors:")
402
- for factor in info['risk_factors']:
403
- st.markdown(f" - {factor}")
404
- st.markdown("---")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
 
406
- # TAB 3: BUFFER OPTIMIZER
407
  elif dashboard_tab == "πŸ›‘οΈ Buffer Optimizer":
408
- st.markdown("""
409
- # Dynamic Buffer Stock Optimization for Yazaki India Ltd
410
-
411
- This dashboard provides suggested mitigation strategies to address supply risks and optimize inventory buffers.
412
- """)
413
 
414
- supplier_selected = st.selectbox("Select Tier-2 Supplier", options=suppliers.keys())
415
- material_selected = st.selectbox("Select Material", options=suppliers[supplier_selected]['materials'])
 
 
 
 
 
416
 
417
- impact_amount = st.number_input("Estimated Impact Amount (units/day)", min_value=0, max_value=500, value=50)
418
- impact_days = st.number_input("Impact Duration (days)", min_value=1, max_value=30, value=7)
419
 
420
- if st.button("Generate Mitigation Strategies"):
421
- strategies, recommended_idxs = generate_mitigation_strategies(supplier_selected, material_selected, impact_amount, impact_days)
 
 
 
 
 
 
 
 
 
 
422
 
423
- st.markdown(f"### Mitigation Strategies for {material_selected} from {supplier_selected}")
424
- for i, strat in enumerate(strategies):
425
- recommended_mark = "βœ… Recommended" if i in recommended_idxs else ""
426
- st.markdown(f"**{strat['strategy']}** {recommended_mark}")
427
- st.markdown(f"- Description: {strat['description']}")
428
- st.markdown(f"- Timeline: {strat['timeline']}")
429
- st.markdown(f"- Cost: {strat['cost']}")
430
- st.markdown(f"- Effectiveness: {strat['effectiveness']}")
431
- st.markdown(f"- Capacity Gain: {strat['capacity']}")
432
- st.markdown("---")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
 
434
- # Footer or Additional Info can be added here if needed
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #Stable version for Yazaki India Ltd
2
  import streamlit as st
3
  import pandas as pd
4
  import numpy as np
 
17
 
18
  # Custom CSS (same as before)
19
  st.markdown("""
20
+ <style>
21
+ </style>
22
  """, unsafe_allow_html=True)
23
 
24
  # Initialize session state
25
  if 'executed_mitigations' not in st.session_state:
26
  st.session_state.executed_mitigations = []
 
27
  if 'external_signals' not in st.session_state:
28
  st.session_state.external_signals = []
29
 
 
42
  ]
43
 
44
  all_data = []
45
+
46
  for material in materials:
47
  np.random.seed(hash(material) % 1000)
48
+
49
+ # Generate base demand patterns
50
  base_demand = np.random.normal(150, 15, 56)
51
 
52
  # First 14 days: FIRM DEMAND
 
59
  external_factors = np.zeros(42)
60
  # Weather impact (weeks 3-4)
61
  external_factors[0:14] += np.random.normal(0, 5, 14)
62
+ # EV policy impact (weeks 5-8)
63
+ if 'YAZ' in material:
64
+ external_factors[14:] += 10
65
  # Festive season boost (weeks 6-7)
66
  external_factors[28:42] += 8
67
 
 
76
  supply_actual[15:19] = (supply_actual[15:19] * 0.8).astype(int)
77
 
78
  for i, date in enumerate(dates):
79
+ # Determine which demand to use
80
  if i < 14:
81
  demand_used = firm_demand[i]
82
  firm_val = firm_demand[i]
 
90
  corrected_val = corrected_demand[i-14]
91
  demand_type = "AI-Corrected"
92
 
93
+ # Calculate shortfall
94
  shortfall = max(0, demand_used - supply_actual[i])
95
 
96
  all_data.append({
97
  'Date': date,
98
+ 'Week': f"Week {(i//7)+1}",
99
  'Day': i + 1,
100
  'Material': material,
101
  'Firm_Demand': firm_val,
 
108
  'Demand_Type': demand_type,
109
  'Gap': supply_actual[i] - demand_used
110
  })
111
+
112
  return pd.DataFrame(all_data)
113
 
114
+ # Updated Tier-2 suppliers for Yazaki India
115
  @st.cache_data
116
  def get_tier2_suppliers():
117
  return {
 
141
  }
142
  }
143
 
144
+ # Updated ecosystem generation with Yazaki-specific data
145
  @st.cache_data
146
  def generate_ecosystem_data():
147
  today = datetime(2025, 8, 4)
 
149
  suppliers = get_tier2_suppliers()
150
 
151
  all_data = []
152
+
153
  for supplier_name, supplier_info in suppliers.items():
154
  for material in supplier_info['materials']:
155
  np.random.seed(hash(supplier_name + material) % 1000)
156
+
157
  base_capacity = supplier_info['capacity']
158
  normal_supply = np.full(14, base_capacity, dtype=int)
159
  disrupted_supply = normal_supply.copy()
160
 
161
  if supplier_name == 'Electro Components Pvt Ltd':
162
  disrupted_supply[3:7] = (disrupted_supply[3:7] * 0.3).astype(int)
163
+ disruption_cause = "Port delays in Chennai affecting imports"
164
  disruption_days = list(range(3, 7))
165
  elif supplier_name == 'Connectix Solutions':
166
  disrupted_supply[5:8] = (disrupted_supply[5:8] * 0.5).astype(int)
167
+ disruption_cause = "Critical equipment failure at Ahmedabad facility"
168
  disruption_days = list(range(5, 8))
169
  elif supplier_name == 'WireCraft Industries':
170
  disrupted_supply[8:11] = (disrupted_supply[8:11] * 0.2).astype(int)
 
199
  'Is_Disrupted': i in disruption_days,
200
  'Is_Yazaki_Impacted': yazaki_supply[i] < normal_supply[i]
201
  })
202
+
203
  return pd.DataFrame(all_data)
204
 
205
+ # Keep external signals unchanged (these are general market signals)
206
  @st.cache_data
207
  def get_external_signals():
208
  return [
 
210
  {'Source': 'Market Intelligence', 'Signal': 'EV sales up 25% this quarter', 'Impact': 'Demand Increase', 'Confidence': 88},
211
  {'Source': 'News Analytics', 'Signal': 'Upcoming festive season - historically 15% demand spike', 'Impact': 'Demand Surge', 'Confidence': 92},
212
  {'Source': 'Supplier Network', 'Signal': 'Tier-2 supplier capacity increased by 20%', 'Impact': 'Supply Boost', 'Confidence': 98},
213
+ {'Source': 'Social Media', 'Signal': 'Positive sentiment around new Maruti EV model', 'Impact': 'Demand Growth', 'Confidence': 75},
214
  {'Source': 'Government Portal', 'Signal': 'New EV subsidy policy effective next week', 'Impact': 'Market Expansion', 'Confidence': 100}
215
  ]
216
 
217
+ # UPDATED: Generate alerts for 8-week data
218
  def generate_detailed_alerts(df):
219
  alerts = []
220
+
221
  for material in df['Material'].unique():
222
  material_data = df[df['Material'] == material]
223
  shortage_days = material_data[material_data['Shortfall'] > 5]
224
+
225
  if not shortage_days.empty:
226
  for _, row in shortage_days.iterrows():
227
  root_causes = []
228
+
229
  if row['Day'] > 14:
230
  if row['Corrected_Demand'] and row['Customer_Demand']:
231
  diff = row['Corrected_Demand'] - row['Customer_Demand']
232
  if diff > 10:
233
  root_causes.append(f"AI detected {diff} units additional demand from external signals")
234
+
235
+ if row['Day'] >= 15 and row['Day'] <= 18:
236
+ root_causes.append("Chennai supplier weather disruption reducing supply")
237
+ else:
238
+ root_causes.append("Firm demand exceeding supply capacity")
239
+
240
  if not root_causes:
241
  root_causes.append("Base demand exceeding current supply capacity")
242
 
 
265
  'mitigation_options': mitigation_options,
266
  'best_option': best_option
267
  })
268
+
269
  return alerts
270
 
271
+ # Keep mitigation strategies unchanged
272
  def generate_mitigation_strategies(supplier, material, impact_amount, impact_days):
273
  base_strategies = [
274
  {
 
296
  'capacity': f'+{impact_amount * 0.6:.0f} units/day',
297
  }
298
  ]
299
+
300
  if impact_amount > 100:
301
  recommended = [0, 1]
302
  elif impact_amount > 50:
303
  recommended = [0, 2]
304
  else:
305
  recommended = [2]
306
+
307
  return base_strategies, recommended
308
 
309
  # Load data
 
312
  external_signals = get_external_signals()
313
  suppliers = get_tier2_suppliers()
314
 
315
+ # Simple title (header removed as requested)
316
  st.title("Supply Chain Command Center - Yazaki India Ltd")
317
 
318
+ # Tab Navigation (same as before)
319
  st.sidebar.title("🎯 Dashboard Navigation")
320
  dashboard_tab = st.sidebar.radio(
321
  "Select Dashboard:",
 
323
  index=0
324
  )
325
 
326
+ # UPDATED TAB 1: 8-WEEK DEMAND & SUPPLY FORECAST
327
  if dashboard_tab == "πŸ“Š Demand & Supply Forecast":
328
+
329
  st.markdown("""
330
+ <div style='background: linear-gradient(90deg, #1e3c72 0%, #2a5298 100%); padding: 15px; border-radius: 10px; margin-bottom: 20px;'>
331
+ <h3 style='color: white; margin: 0; text-align: center;'>
332
+ πŸ“Š 8-Week Planning Horizon | Firm Demand (Days 1-14) | AI-Corrected Demand (Days 15-56)
333
+ </h3>
334
+ </div>
335
+ """, unsafe_allow_html=True)
336
 
337
+ # Material selector
338
+ materials = df_demand['Material'].unique()
339
+ selected_material = st.selectbox("πŸ” Select Material for Analysis:", materials)
340
+
341
+ # Filter data for selected material
342
+ material_data = df_demand[df_demand['Material'] == selected_material].copy()
343
+
344
+ # Create forecast visualization
345
  fig = go.Figure()
346
+
347
+ # Add firm demand (first 14 days)
348
+ firm_data = material_data[material_data['Day'] <= 14]
349
+ fig.add_trace(go.Scatter(
350
+ x=firm_data['Date'],
351
+ y=firm_data['Demand_Used'],
352
+ mode='lines+markers',
353
+ name='Firm Demand (Days 1-14)',
354
+ line=dict(color='#2E86AB', width=3),
355
+ marker=dict(size=8)
356
+ ))
357
+
358
+ # Add customer shared demand (days 15-56)
359
+ future_data = material_data[material_data['Day'] > 14]
360
  fig.add_trace(go.Scatter(
361
+ x=future_data['Date'],
362
+ y=future_data['Customer_Demand'],
363
+ mode='lines',
364
+ name='Customer Shared Demand',
365
+ line=dict(color='#F18F01', width=2, dash='dot'),
366
+ opacity=0.7
367
  ))
368
+
369
+ # Add AI-corrected demand (days 15-56)
370
  fig.add_trace(go.Scatter(
371
+ x=future_data['Date'],
372
+ y=future_data['Corrected_Demand'],
373
+ mode='lines+markers',
374
+ name='AI-Corrected Demand',
375
+ line=dict(color='#C73E1D', width=3),
376
+ marker=dict(size=6)
377
  ))
378
+
379
+ # Add supply projection
380
  fig.add_trace(go.Scatter(
381
+ x=material_data['Date'],
382
+ y=material_data['Supply_Projected'],
383
+ mode='lines',
384
+ name='Supply Projection',
385
+ line=dict(color='#4CAF50', width=2),
386
+ fill='tonexty',
387
+ fillcolor='rgba(76, 175, 80, 0.1)'
388
  ))
389
+
390
  fig.update_layout(
391
+ title=f"πŸ“ˆ 8-Week Demand vs Supply Forecast: {selected_material}",
392
+ xaxis_title="Date",
393
+ yaxis_title="Units",
394
+ height=500,
395
+ hovermode='x unified',
396
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1)
397
  )
398
+
399
  st.plotly_chart(fig, use_container_width=True)
400
+
401
+ # Weekly aggregation
402
+ weekly_summary = material_data.groupby('Week').agg({
403
+ 'Demand_Used': 'sum',
404
+ 'Supply_Projected': 'sum',
405
+ 'Shortfall': 'sum'
406
+ }).reset_index()
407
+
408
+ # Display weekly summary
409
+ col1, col2 = st.columns(2)
410
+
411
+ with col1:
412
+ st.subheader("πŸ“Š Weekly Summary")
413
+ for _, week_data in weekly_summary.iterrows():
414
+ gap = week_data['Supply_Projected'] - week_data['Demand_Used']
415
+ status = "βœ… Surplus" if gap > 0 else "⚠️ Shortage" if gap < 0 else "βš–οΈ Balanced"
416
+
417
+ st.markdown(f"""
418
+ **{week_data['Week']}**: {status}
419
+ - Demand: {week_data['Demand_Used']:,} units
420
+ - Supply: {week_data['Supply_Projected']:,} units
421
+ - Gap: {gap:+,} units
422
+ """)
423
+
424
+ with col2:
425
+ st.subheader("🚨 Shortage Alerts")
426
+ alerts = generate_detailed_alerts(material_data)
427
+
428
+ if alerts:
429
+ for alert in alerts[:3]: # Show top 3 alerts
430
+ severity_color = {"Critical": "#FF4444", "High": "#FF8800", "Medium": "#FFBB00"}[alert['severity']]
431
+
432
+ st.markdown(f"""
433
+ <div style='border-left: 4px solid {severity_color}; padding: 10px; margin: 10px 0; background: #f8f9fa;'>
434
+ <strong>{alert['severity']} Alert</strong><br>
435
+ <strong>Date:</strong> {alert['date']} ({alert['week']}) | <strong>Shortage:</strong> {alert['shortage']} units | <strong>Type:</strong> {alert['demand_type']}<br>
436
+ <strong>Root Cause:</strong> {alert['root_causes'][0]}<br>
437
+ <strong>Best Mitigation:</strong> {alert['best_option']['option']} ({alert['best_option']['timeline']})
438
+ </div>
439
+ """, unsafe_allow_html=True)
440
+ else:
441
+ st.success("βœ… No shortages detected for this material!")
442
 
443
+ # TAB 2: ECOSYSTEM SUPPLIER IMPACT (updated variable names)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
  elif dashboard_tab == "🌐 Ecosystem Supplier Impact":
445
+
446
  st.markdown("""
447
+ <div style='background: linear-gradient(90deg, #1e3c72 0%, #2a5298 100%); padding: 15px; border-radius: 10px; margin-bottom: 20px;'>
448
+ <h3 style='color: white; margin: 0; text-align: center;'>
449
+ 🌐 Tier 2 Supplier Disruption Analysis | Cascading Impact Modeling | Automated Mitigation Response
450
+ </h3>
451
+ </div>
452
+ """, unsafe_allow_html=True)
453
 
454
+ # Supplier overview
455
+ st.subheader("πŸ“ˆ Supplier Performance Overview")
456
 
457
+ supplier_summary = df_ecosystem.groupby('Supplier').agg({
458
+ 'Tier2_Impact': 'sum',
459
+ 'Yazaki_Impact': 'sum',
460
+ 'Is_Disrupted': 'sum'
461
+ }).reset_index()
462
 
463
+ supplier_summary.columns = ['Supplier', 'Total_Tier2_Impact', 'Total_Yazaki_Impact', 'Disruption_Days']
 
464
 
465
+ # Display supplier cards
466
+ cols = st.columns(len(supplier_summary))
467
+ for i, (_, supplier) in enumerate(supplier_summary.iterrows()):
468
+ with cols[i]:
469
+ supplier_info = suppliers[supplier['Supplier']]
470
+ disruption_status = "πŸ”΄ Disrupted" if supplier['Disruption_Days'] > 0 else "🟒 Normal"
471
+
472
+ st.markdown(f"""
473
+ <div style='border: 2px solid #ddd; padding: 15px; border-radius: 10px; text-align: center;'>
474
+ <h4>{supplier['Supplier']}</h4>
475
+ <p><strong>Location:</strong> {supplier_info['location']}</p>
476
+ <p><strong>Status:</strong> {disruption_status}</p>
477
+ <p><strong>Yazaki Impact:</strong> {supplier['Total_Yazaki_Impact']} units</p>
478
+ <p><strong>Reliability:</strong> {supplier_info['reliability']}%</p>
479
+ </div>
480
+ """, unsafe_allow_html=True)
481
 
482
+ # Detailed supplier analysis
483
+ st.subheader("πŸ” Detailed Supplier Analysis")
484
+
485
+ selected_supplier = st.selectbox("Select Supplier for Detailed Analysis:", df_ecosystem['Supplier'].unique())
486
+ supplier_data = df_ecosystem[df_ecosystem['Supplier'] == selected_supplier]
487
+
488
+ # Create supplier timeline
489
+ fig_supplier = go.Figure()
490
+
491
+ for material in supplier_data['Material'].unique():
492
+ material_data = supplier_data[supplier_data['Material'] == material]
493
+
494
+ fig_supplier.add_trace(go.Scatter(
495
+ x=material_data['Date'],
496
+ y=material_data['Tier2_Normal_Supply'],
497
+ mode='lines',
498
+ name=f'{material} - Normal Supply',
499
+ line=dict(dash='dot'),
500
+ opacity=0.6
501
+ ))
502
+
503
+ fig_supplier.add_trace(go.Scatter(
504
+ x=material_data['Date'],
505
+ y=material_data['Tier2_Disrupted_Supply'],
506
+ mode='lines+markers',
507
+ name=f'{material} - Actual Supply',
508
+ marker=dict(size=6)
509
+ ))
510
+
511
+ fig_supplier.update_layout(
512
+ title=f"πŸ“Š Supply Timeline: {selected_supplier}",
513
+ xaxis_title="Date",
514
+ yaxis_title="Supply Units",
515
+ height=400
516
  )
517
+
518
+ st.plotly_chart(fig_supplier, use_container_width=True)
519
+
520
+ # Disruption details
521
+ disrupted_data = supplier_data[supplier_data['Is_Disrupted'] == True]
522
+ if not disrupted_data.empty:
523
+ st.subheader("⚠️ Disruption Details")
524
+
525
+ for material in disrupted_data['Material'].unique():
526
+ material_disruptions = disrupted_data[disrupted_data['Material'] == material]
527
+ if not material_disruptions.empty:
528
+ disruption_info = material_disruptions.iloc[0]
529
+
530
+ col1, col2 = st.columns(2)
531
+ with col1:
532
+ st.markdown(f"""
533
+ <div style='border-left: 4px solid #FF4444; padding: 15px; background: #f8f9fa; margin: 10px 0;'>
534
+ <strong>Supplier:</strong> {disruption_info['Supplier']}<br>
535
+ <strong>Material:</strong> {disruption_info['Material']}<br>
536
+ <strong>Root Cause:</strong> {disruption_info['Disruption_Cause']}<br>
537
+ <strong>Impact Duration:</strong> {len(material_disruptions)} days<br>
538
+ <strong>Total Impact:</strong> {material_disruptions['Tier2_Impact'].sum()} units
539
+ </div>
540
+ """, unsafe_allow_html=True)
541
+
542
+ with col2:
543
+ # Generate mitigation strategies
544
+ strategies, recommended = generate_mitigation_strategies(
545
+ disruption_info['Supplier'],
546
+ disruption_info['Material'],
547
+ material_disruptions['Tier2_Impact'].sum(),
548
+ len(material_disruptions)
549
+ )
550
+
551
+ st.markdown("**πŸ› οΈ Recommended Mitigation Strategies:**")
552
+ for idx in recommended:
553
+ strategy = strategies[idx]
554
+ st.markdown(f"""
555
+ **{strategy['strategy']}**
556
+ - {strategy['description']}
557
+ - Timeline: {strategy['timeline']}
558
+ - Cost: {strategy['cost']}
559
+ - Capacity: {strategy['capacity']}
560
+ """)
561
 
562
+ # TAB 3: BUFFER OPTIMIZER (unchanged)
563
  elif dashboard_tab == "πŸ›‘οΈ Buffer Optimizer":
 
 
 
 
 
564
 
565
+ st.markdown("""
566
+ <div style='background: linear-gradient(90deg, #1e3c72 0%, #2a5298 100%); padding: 15px; border-radius: 10px; margin-bottom: 20px;'>
567
+ <h3 style='color: white; margin: 0; text-align: center;'>
568
+ πŸ›‘οΈ AI-driven safety-stock recommendations across the full network
569
+ </h3>
570
+ </div>
571
+ """, unsafe_allow_html=True)
572
 
573
+ # Buffer analysis
574
+ st.subheader("πŸ“Š Current Buffer Analysis")
575
 
576
+ # Calculate buffer recommendations
577
+ buffer_data = []
578
+ for material in df_demand['Material'].unique():
579
+ material_demand = df_demand[df_demand['Material'] == material]
580
+
581
+ avg_demand = material_demand['Demand_Used'].mean()
582
+ max_demand = material_demand['Demand_Used'].max()
583
+ demand_volatility = material_demand['Demand_Used'].std()
584
+
585
+ # Calculate recommended buffer
586
+ safety_factor = 1.5 # Can be adjusted based on service level requirements
587
+ recommended_buffer = int(demand_volatility * safety_factor)
588
 
589
+ # Current buffer (assumed)
590
+ current_buffer = int(avg_demand * 0.1) # Assuming 10% of average demand
591
+
592
+ buffer_data.append({
593
+ 'Material': material,
594
+ 'Avg_Demand': int(avg_demand),
595
+ 'Max_Demand': int(max_demand),
596
+ 'Demand_Volatility': int(demand_volatility),
597
+ 'Current_Buffer': current_buffer,
598
+ 'Recommended_Buffer': recommended_buffer,
599
+ 'Buffer_Gap': recommended_buffer - current_buffer
600
+ })
601
+
602
+ buffer_df = pd.DataFrame(buffer_data)
603
+
604
+ # Display buffer table
605
+ st.dataframe(buffer_df, use_container_width=True)
606
+
607
+ # Buffer optimization visualization
608
+ fig_buffer = go.Figure()
609
+
610
+ fig_buffer.add_trace(go.Bar(
611
+ name='Current Buffer',
612
+ x=buffer_df['Material'],
613
+ y=buffer_df['Current_Buffer'],
614
+ marker_color='lightblue'
615
+ ))
616
+
617
+ fig_buffer.add_trace(go.Bar(
618
+ name='Recommended Buffer',
619
+ x=buffer_df['Material'],
620
+ y=buffer_df['Recommended_Buffer'],
621
+ marker_color='orange'
622
+ ))
623
+
624
+ fig_buffer.update_layout(
625
+ title='πŸ“Š Buffer Stock Analysis: Current vs Recommended',
626
+ xaxis_title='Material',
627
+ yaxis_title='Buffer Units',
628
+ barmode='group',
629
+ height=400
630
+ )
631
+
632
+ st.plotly_chart(fig_buffer, use_container_width=True)
633
+
634
+ # Buffer recommendations
635
+ st.subheader("πŸ’‘ Buffer Optimization Recommendations")
636
+
637
+ for _, row in buffer_df.iterrows():
638
+ if row['Buffer_Gap'] > 0:
639
+ st.warning(f"**{row['Material']}**: Increase buffer by {row['Buffer_Gap']} units to handle demand volatility")
640
+ elif row['Buffer_Gap'] < 0:
641
+ st.info(f"**{row['Material']}**: Current buffer is {abs(row['Buffer_Gap'])} units above recommendation - consider optimization")
642
+ else:
643
+ st.success(f"**{row['Material']}**: Buffer levels are optimal")
644
 
645
+ # Footer
646
+ st.markdown("""
647
+ ---
648
+ <div style='text-align: center; padding: 20px; background: linear-gradient(90deg, #1e3c72 0%, #2a5298 100%); border-radius: 10px; margin-top: 30px;'>
649
+ <h3 style='color: white; margin: 0;'>
650
+ πŸ“Š <strong>Yazaki India Ltd 8-Week Supply Chain Command Center</strong> | Firm + AI-Corrected Demand | Ecosystem Intelligence + Buffer Optimization
651
+ </h3>
652
+ <p style='color: white; margin: 10px 0 0 0;'>
653
+ Powered by Agentic AI | 8-Week Planning Horizon | Comprehensive Supply Chain Resilience
654
+ </p>
655
+ </div>
656
+ """, unsafe_allow_html=True)