PD03 commited on
Commit
106eeca
·
verified ·
1 Parent(s): 5b3bb10

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +295 -450
src/streamlit_app.py CHANGED
@@ -1,30 +1,30 @@
1
- #Stable version for Yazaki India
2
  import streamlit as st
3
  import pandas as pd
4
- import numpy as np
5
  import plotly.express as px
6
  import plotly.graph_objects as go
7
  from datetime import datetime, timedelta
8
- import random
9
 
10
- # Page configuration
11
  st.set_page_config(
12
- page_title="Yazaki India - Complete Supply Chain Hub",
13
  page_icon="🔌",
14
  layout="wide",
15
  initial_sidebar_state="expanded"
16
  )
17
 
18
- # Custom CSS (keeping the same professional styling)
19
  st.markdown("""
20
  <style>
 
21
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
22
 
 
23
  .stApp {
24
  font-family: 'Inter', sans-serif;
25
  background-color: #f8fafc;
26
  }
27
 
 
28
  .main-container {
29
  background: white;
30
  border-radius: 12px;
@@ -34,13 +34,14 @@ st.markdown("""
34
  border: 1px solid #e2e8f0;
35
  }
36
 
 
37
  .modern-header {
38
  background: #1e293b;
39
  color: white;
40
  padding: 2rem;
41
  border-radius: 12px;
42
  margin-bottom: 2rem;
43
- border-left: 4px solid #dc2626; /* Yazaki red accent */
44
  }
45
 
46
  .header-title {
@@ -56,6 +57,7 @@ st.markdown("""
56
  color: #94a3b8;
57
  }
58
 
 
59
  .metric-card {
60
  background: white;
61
  padding: 1.5rem;
@@ -87,481 +89,324 @@ st.markdown("""
87
  margin-bottom: 0.5rem;
88
  }
89
 
90
- .alert-container {
91
- background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);
92
- border-left: 4px solid #dc2626;
93
- padding: 1rem;
94
- margin: 1rem 0;
95
- border-radius: 8px;
96
  }
97
 
98
- .critical-alert {
99
- background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);
 
100
  }
101
 
102
- .high-alert {
103
- background: linear-gradient(135deg, #fef3c7 0%, #fed7aa 100%);
104
- border-left-color: #f59e0b;
105
  }
106
 
107
- .medium-alert {
108
- background: linear-gradient(135deg, #ddd6fe 0%, #c7d2fe 100%);
109
- border-left-color: #8b5cf6;
110
  }
111
 
112
- .mitigation-option {
113
- background: #f8fafc;
114
- padding: 0.75rem;
115
- margin: 0.5rem 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  border-radius: 8px;
117
- border-left: 3px solid #3b82f6;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  }
119
  </style>
120
  """, unsafe_allow_html=True)
121
 
122
- # Initialize session state
123
- if 'executed_mitigations' not in st.session_state:
124
- st.session_state.executed_mitigations = []
125
- if 'external_signals' not in st.session_state:
126
- st.session_state.external_signals = []
127
-
128
- # UPDATED: Generate 8-week demand data for Yazaki wiring components
129
  @st.cache_data
130
- def generate_8week_demand_data():
131
- today = datetime(2025, 8, 4)
132
- dates = [today + timedelta(days=x) for x in range(56)] # 8 weeks = 56 days
133
-
134
- # Yazaki-specific materials (wiring harnesses and electrical components)
135
- materials = [
136
- 'WH001-Engine Wiring Harness',
137
- 'WH002-Dashboard Wiring',
138
- 'CON001-Wire Connectors',
139
- 'TER001-Terminal Blocks',
140
- 'FUS001-Fuse Box Assembly'
141
- ]
142
-
143
- all_data = []
144
-
145
- for material in materials:
146
- np.random.seed(hash(material) % 1000)
147
-
148
- # Generate base demand patterns - lower volumes due to import constraints
149
- base_demand = np.random.normal(120, 20, 56)
150
-
151
- # First 14 days: FIRM DEMAND (from OEM confirmed orders)
152
- firm_demand = np.clip(base_demand[:14], 80, 180).astype(int)
153
-
154
- # Days 15-56: Customer shared demand (tentative from Maruti, Tata, etc.)
155
- customer_shared = np.clip(base_demand[14:] * (1 + 0.08 * np.sin(np.linspace(0, 3.14, 42))), 60, 200).astype(int)
156
-
157
- # Days 15-56: AI-corrected demand (with import delays and external signals)
158
- external_factors = np.zeros(42)
159
-
160
- # Import delays impact (weeks 3-4) - port congestion at JNPT
161
- external_factors[0:14] += np.random.normal(-15, 8, 14) # Negative impact from delays
162
-
163
- # EV transition impact (weeks 5-8) - different wiring needs
164
- if 'WH' in material: # Wire harnesses more affected by EV transition
165
- external_factors[14:] += 12
166
- elif 'CON' in material or 'TER' in material: # Connectors/terminals high demand in EV
167
- external_factors[14:] += 18
168
-
169
- # Festive season boost (weeks 6-7) - reduced due to import constraints
170
- external_factors[28:42] += 6
171
-
172
- corrected_demand = np.clip(customer_shared + external_factors, 40, 220).astype(int)
173
-
174
- # Generate supply plan - constrained by import lead times
175
- supply_capacity = np.random.normal(125, 18, 56)
176
- supply_plan = np.clip(supply_capacity, 90, 200).astype(int)
177
-
178
- # Apply import disruptions (longer lead times, customs delays)
179
- supply_actual = supply_plan.copy()
180
- # Import delays at Chennai port (days 15-20)
181
- supply_actual[15:21] = (supply_actual[15:21] * 0.6).astype(int)
182
- # Currency fluctuation impact (days 25-30)
183
- supply_actual[25:31] = (supply_actual[25:31] * 0.8).astype(int)
184
-
185
- for i, date in enumerate(dates):
186
- # Determine which demand to use
187
- if i < 14:
188
- demand_used = firm_demand[i]
189
- firm_val = firm_demand[i]
190
- customer_val = None
191
- corrected_val = None
192
- demand_type = "Firm (OEM Confirmed)"
193
- else:
194
- demand_used = corrected_demand[i-14]
195
- firm_val = None
196
- customer_val = customer_shared[i-14]
197
- corrected_val = corrected_demand[i-14]
198
- demand_type = "AI-Corrected (Import Adjusted)"
199
-
200
- # Calculate shortfall
201
- shortfall = max(0, demand_used - supply_actual[i])
202
-
203
- all_data.append({
204
- 'Date': date,
205
- 'Week': f"Week {(i//7)+1}",
206
- 'Day': i + 1,
207
- 'Material': material,
208
- 'Firm_Demand': firm_val,
209
- 'Customer_Demand': customer_val,
210
- 'Corrected_Demand': corrected_val,
211
- 'Demand_Used': demand_used,
212
- 'Supply_Plan': supply_plan[i],
213
- 'Supply_Projected': supply_actual[i],
214
- 'Shortfall': shortfall,
215
- 'Demand_Type': demand_type,
216
- 'Gap': supply_actual[i] - demand_used
217
- })
218
-
219
- return pd.DataFrame(all_data)
220
 
221
- # Yazaki Tier-2 suppliers (mostly international)
222
  @st.cache_data
223
- def get_tier2_suppliers():
224
  return {
225
- 'Sumitomo Electric (Japan)': {
226
- 'location': 'Tokyo, Japan',
227
- 'materials': ['WH001-Engine Wiring Harness', 'CON001-Wire Connectors'],
228
- 'capacity': 150,
229
- 'reliability': 96,
230
- 'lead_time': 12, # Import lead time in days
231
- 'risk_factors': ['Currency fluctuation', 'Shipping delays', 'Quality certification']
232
- },
233
- 'Yazaki Europe GmbH': {
234
- 'location': 'Regensburg, Germany',
235
- 'materials': ['WH002-Dashboard Wiring', 'TER001-Terminal Blocks'],
236
- 'capacity': 130,
237
- 'reliability': 94,
238
- 'lead_time': 15,
239
- 'risk_factors': ['European port strikes', 'Customs clearance', 'Exchange rate volatility']
240
- },
241
- 'Yazaki Thailand': {
242
- 'location': 'Bangkok, Thailand',
243
- 'materials': ['FUS001-Fuse Box Assembly', 'CON001-Wire Connectors'],
244
- 'capacity': 170,
245
- 'reliability': 90,
246
- 'lead_time': 8,
247
- 'risk_factors': ['Monsoon disruptions', 'Regional political instability', 'Supply chain bottlenecks']
248
- }
249
  }
250
 
251
- # Generate ecosystem data with import focus
252
- @st.cache_data
253
- def generate_ecosystem_data():
254
- today = datetime(2025, 8, 4)
255
- dates = [today + timedelta(days=x) for x in range(14)]
256
- suppliers = get_tier2_suppliers()
257
- all_data = []
258
-
259
- for supplier_name, supplier_info in suppliers.items():
260
- for material in supplier_info['materials']:
261
- np.random.seed(hash(supplier_name + material) % 1000)
262
- base_capacity = supplier_info['capacity']
263
- normal_supply = np.full(14, base_capacity, dtype=int)
264
- disrupted_supply = normal_supply.copy()
265
-
266
- # Different disruption patterns for international suppliers
267
- if 'Japan' in supplier_name:
268
- disrupted_supply[5:9] = (disrupted_supply[5:9] * 0.4).astype(int)
269
- disruption_cause = "JNPT port congestion - shipment delays"
270
- disruption_days = list(range(5, 9))
271
- elif 'Germany' in supplier_name:
272
- disrupted_supply[7:11] = (disrupted_supply[7:11] * 0.3).astype(int)
273
- disruption_cause = "European port strikes affecting shipments"
274
- disruption_days = list(range(7, 11))
275
- elif 'Thailand' in supplier_name:
276
- disrupted_supply[3:6] = (disrupted_supply[3:6] * 0.5).astype(int)
277
- disruption_cause = "Monsoon affecting Bangkok operations"
278
- disruption_days = list(range(3, 6))
279
- else:
280
- disruption_cause = "No disruption"
281
- disruption_days = []
282
-
283
- # Import lead time impact
284
- lead_time = supplier_info['lead_time']
285
- rane_supply = np.full(14, base_capacity, dtype=int)
286
-
287
- for disruption_day in disruption_days:
288
- arrival_day = disruption_day + lead_time
289
- if arrival_day < 14:
290
- reduction = normal_supply[disruption_day] - disrupted_supply[disruption_day]
291
- rane_supply[arrival_day] = max(rane_supply[arrival_day] - reduction, 0)
292
-
293
- for i, date in enumerate(dates):
294
- all_data.append({
295
- 'Date': date,
296
- 'Supplier': supplier_name,
297
- 'Material': material,
298
- 'Tier2_Normal_Supply': int(normal_supply[i]),
299
- 'Tier2_Disrupted_Supply': int(disrupted_supply[i]),
300
- 'Tier2_Impact': int(normal_supply[i] - disrupted_supply[i]),
301
- 'Rane_Normal_Supply': int(normal_supply[i]),
302
- 'Rane_Impacted_Supply': int(rane_supply[i]),
303
- 'Rane_Impact': int(normal_supply[i] - rane_supply[i]),
304
- 'Disruption_Cause': disruption_cause if i in disruption_days else "Normal Operations",
305
- 'Lead_Time_Days': lead_time,
306
- 'Is_Disrupted': i in disruption_days,
307
- 'Is_Rane_Impacted': rane_supply[i] < normal_supply[i]
308
- })
309
-
310
- return pd.DataFrame(all_data)
311
-
312
- # Yazaki-specific external signals
313
- @st.cache_data
314
- def get_external_signals():
315
- return [
316
- {'Source': 'JNPT Port Authority', 'Signal': 'Port congestion expected for next 5 days', 'Impact': 'Import Delays', 'Confidence': 94},
317
- {'Source': 'EV Market Intelligence', 'Signal': 'Electric vehicle wiring demand up 35% this quarter', 'Impact': 'Demand Surge', 'Confidence': 91},
318
- {'Source': 'Currency Markets', 'Signal': 'INR weakening against JPY - 3% in last week', 'Impact': 'Cost Increase', 'Confidence': 98},
319
- {'Source': 'Maruti Suzuki', 'Signal': 'New EV model launch - wiring harness orders expected +40%', 'Impact': 'Major Demand Increase', 'Confidence': 96},
320
- {'Source': 'Weather Services', 'Signal': 'Heavy monsoon forecasted in Thailand next week', 'Impact': 'Supply Disruption', 'Confidence': 87},
321
- {'Source': 'Government Policy', 'Signal': 'New import duty on auto components - 5% increase', 'Impact': 'Cost Impact', 'Confidence': 100}
322
- ]
323
-
324
- # Generate alerts for Yazaki materials
325
- def generate_detailed_alerts(df):
326
- alerts = []
327
- for material in df['Material'].unique():
328
- material_data = df[df['Material'] == material]
329
- shortage_days = material_data[material_data['Shortfall'] > 3] # Lower threshold due to import constraints
330
-
331
- if not shortage_days.empty:
332
- for _, row in shortage_days.iterrows():
333
- root_causes = []
334
-
335
- if row['Day'] > 14:
336
- if row['Corrected_Demand'] and row['Customer_Demand']:
337
- diff = row['Corrected_Demand'] - row['Customer_Demand']
338
- if diff > 8:
339
- root_causes.append(f"AI detected {diff} units additional demand from EV transition")
340
-
341
- if row['Day'] >= 15 and row['Day'] <= 20:
342
- root_causes.append("Chennai port customs delays affecting imports")
343
- elif row['Day'] >= 25 and row['Day'] <= 30:
344
- root_causes.append("INR currency volatility impacting import costs")
345
- else:
346
- root_causes.append("OEM firm demand exceeding import supply capacity")
347
-
348
- if not root_causes:
349
- root_causes.append("Base demand exceeding current import supply capacity")
350
-
351
- # Yazaki-specific mitigation options
352
- mitigation_options = [
353
- {"option": "Expedite air freight from Japan/Germany", "impact": "+25 units/day", "cost": "Very High", "timeline": "48 hours"},
354
- {"option": "Activate Thailand backup supplier", "impact": "+20 units/day", "cost": "High", "timeline": "72 hours"},
355
- {"option": "Reallocate inventory from other Yazaki plants", "impact": "+15 units/day", "cost": "Medium", "timeline": "24 hours"},
356
- {"option": "Request OEM demand deferral", "impact": "+30 units buffer", "cost": "Low", "timeline": "12 hours"}
357
- ]
358
-
359
- if row['Shortfall'] > 25:
360
- best_option = mitigation_options[0] # Air freight
361
- elif row['Shortfall'] > 12:
362
- best_option = mitigation_options[1] # Thailand backup
363
- else:
364
- best_option = mitigation_options[2] # Inventory reallocation
365
-
366
- alerts.append({
367
- 'material': material,
368
- 'date': row['Date'].strftime('%Y-%m-%d'),
369
- 'week': row['Week'],
370
- 'shortage': int(row['Shortfall']),
371
- 'demand_type': row['Demand_Type'],
372
- 'severity': 'Critical' if row['Shortfall'] > 25 else 'High' if row['Shortfall'] > 12 else 'Medium',
373
- 'root_causes': root_causes,
374
- 'mitigation_options': mitigation_options,
375
- 'best_option': best_option
376
- })
377
-
378
- return alerts
379
 
380
- # Keep mitigation strategies similar but Yazaki-focused
381
- def generate_mitigation_strategies(supplier, material, impact_amount, impact_days):
382
- base_strategies = [
383
- {
384
- 'strategy': 'Activate Alternate Import Source',
385
- 'description': f'Engage backup international supplier for {material}',
386
- 'timeline': '48-72 hours',
387
- 'cost': 'Very High (+25% unit cost)',
388
- 'effectiveness': '85%',
389
- 'capacity': f'+{impact_amount * 0.85:.0f} units/day',
390
- },
391
- {
392
- 'strategy': 'Emergency Air Freight',
393
- 'description': f'Air freight {material} from Japan/Germany',
394
- 'timeline': '24-48 hours',
395
- 'cost': 'Extremely High (+60% logistics cost)',
396
- 'effectiveness': '90%',
397
- 'capacity': f'+{impact_amount * 0.9:.0f} units/day',
398
- },
399
- {
400
- 'strategy': 'Inventory Reallocation',
401
- 'description': f'Reallocate {material} from other Yazaki facilities',
402
- 'timeline': '12-36 hours',
403
- 'cost': 'Medium (+8% handling cost)',
404
- 'effectiveness': '70%',
405
- 'capacity': f'+{impact_amount * 0.7:.0f} units/day',
406
- }
407
  ]
408
 
409
- if impact_amount > 80:
410
- recommended = [0, 1]
411
- elif impact_amount > 40:
412
- recommended = [0, 2]
413
- else:
414
- recommended = [2]
415
-
416
- return base_strategies, recommended
417
-
418
- # Load data
419
- df_demand = generate_8week_demand_data()
420
- df_ecosystem = generate_ecosystem_data()
421
- external_signals = get_external_signals()
422
- suppliers = get_tier2_suppliers()
423
 
424
- # Yazaki India title
425
  st.markdown("""
426
- <div class="modern-header">
427
- <div class="header-title">🔌 Yazaki India Supply Chain Command Center</div>
428
- <div class="header-subtitle">Import-Focused Supply Chain Intelligence | Automotive Wiring Systems | Global Supply Network</div>
429
  </div>
430
  """, unsafe_allow_html=True)
431
 
432
- # Tab Navigation
433
- st.sidebar.title("🎯 Yazaki Dashboard Navigation")
434
- dashboard_tab = st.sidebar.radio(
435
- "Select Dashboard:",
436
- ["📊 Import Demand Forecast", "🌐 Global Supplier Impact", "🛡️ Import Buffer Optimizer"],
437
- index=0
438
- )
439
 
440
- # TAB 1: Import Demand & Supply Forecast
441
- if dashboard_tab == "📊 Import Demand Forecast":
442
- st.markdown("""
443
- ### 📈 8-Week Import Planning Horizon | OEM Confirmed Orders (Days 1-14) | AI-Adjusted Import Forecasts (Days 15-56)
444
- """)
445
-
446
- # Key metrics for Yazaki
447
- col1, col2, col3, col4 = st.columns(4)
448
-
449
- total_shortage = df_demand['Shortfall'].sum()
450
- avg_import_lead_time = 12 # Average from suppliers
451
- import_dependency = 89 # High dependency on imports
452
- active_suppliers = len(suppliers)
453
-
454
- with col1:
455
- st.markdown(f"""
456
- <div class="metric-card">
457
- <div class="metric-number">{total_shortage}</div>
458
- <div class="metric-label">Total Import Shortage (8 weeks)</div>
459
- </div>
460
- """, unsafe_allow_html=True)
461
-
462
- with col2:
463
- st.markdown(f"""
464
- <div class="metric-card">
465
- <div class="metric-number">{avg_import_lead_time}</div>
466
- <div class="metric-label">Avg Import Lead Time (Days)</div>
467
- </div>
468
- """, unsafe_allow_html=True)
469
-
470
- with col3:
471
- st.markdown(f"""
472
- <div class="metric-card">
473
- <div class="metric-number">{import_dependency}%</div>
474
- <div class="metric-label">Import Dependency</div>
475
- </div>
476
- """, unsafe_allow_html=True)
477
-
478
- with col4:
479
- st.markdown(f"""
480
- <div class="metric-card">
481
- <div class="metric-number">{active_suppliers}</div>
482
- <div class="metric-label">Global Suppliers</div>
483
- </div>
484
- """, unsafe_allow_html=True)
485
-
486
- # Material selection
487
- selected_materials = st.multiselect(
488
- "Select Yazaki Materials:",
489
- df_demand['Material'].unique(),
490
- default=list(df_demand['Material'].unique()[:2])
491
- )
492
-
493
- if selected_materials:
494
- filtered_df = df_demand[df_demand['Material'].isin(selected_materials)]
495
-
496
- # Create visualization
497
- fig = px.line(filtered_df, x='Date', y=['Demand_Used', 'Supply_Projected'],
498
- color='Material', title="Import Demand vs Supply Projection (8 weeks)",
499
- line_dash_map={'Demand_Used': 'solid', 'Supply_Projected': 'dash'})
500
-
501
- fig.update_layout(height=500, showlegend=True)
502
- st.plotly_chart(fig, use_container_width=True)
503
-
504
- # Show detailed data
505
- st.markdown("### 📊 Import Demand & Supply Detail")
506
- display_columns = ['Date', 'Material', 'Week', 'Demand_Used', 'Supply_Projected', 'Shortfall', 'Demand_Type']
507
- st.dataframe(filtered_df[display_columns], use_container_width=True)
508
-
509
- # Generate and display alerts
510
- alerts = generate_detailed_alerts(filtered_df)
511
-
512
- if alerts:
513
- st.markdown("### 🚨 Critical Import Alerts")
514
- for alert in alerts[:5]: # Show top 5 alerts
515
- severity_class = f"{alert['severity'].lower()}-alert"
516
-
517
- st.markdown(f"""
518
- <div class="alert-container {severity_class}">
519
- <h4>⚠️ {alert['severity']} Alert: {alert['material']}</h4>
520
- <p><strong>Date:</strong> {alert['date']} ({alert['week']}) |
521
- <strong>Shortage:</strong> {alert['shortage']} units |
522
- <strong>Type:</strong> {alert['demand_type']}</p>
523
-
524
- <h5>🔍 Root Causes:</h5>
525
- <ul>{''.join([f"<li>{cause}</li>" for cause in alert['root_causes']])}</ul>
526
-
527
- <h5>💡 Recommended Mitigation:</h5>
528
- <div class="mitigation-option">
529
- <strong>{alert['best_option']['option']}</strong><br>
530
- Impact: {alert['best_option']['impact']} |
531
- Cost: {alert['best_option']['cost']} |
532
- Timeline: {alert['best_option']['timeline']}
533
- </div>
534
-
535
- {'<button style="background: #dc2626; color: white; padding: 8px 16px; border: none; border-radius: 4px; margin-top: 10px;">🚀 Execute Mitigation</button>' if alert['severity'] == 'Critical' else ''}
536
- </div>
537
- """, unsafe_allow_html=True)
538
 
539
- # TAB 2: Global Supplier Impact (same structure but Yazaki-focused)
540
- elif dashboard_tab == "🌐 Global Supplier Impact":
541
- st.markdown("""
542
- ### 🌏 Global Supplier Disruption Analysis | Import Cascade Modeling | International Supply Risk
543
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
544
 
545
- # Similar structure but with import focus...
546
- # (Implementation similar to original but with Yazaki suppliers and import-specific risks)
 
 
 
 
 
 
 
 
 
547
 
548
- # Show ecosystem data
549
- st.dataframe(df_ecosystem.head(20), use_container_width=True)
550
 
551
- # TAB 3: Import Buffer Optimizer
552
- elif dashboard_tab == "🛡️ Import Buffer Optimizer":
553
- st.markdown("""
554
- ### 🛡️ AI-Optimized Import Safety Stock | Currency Risk Buffer | Port Delay Contingency
555
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556
 
557
- # Buffer optimization specific to import constraints
558
- st.info("AI-driven safety-stock recommendations considering import lead times, currency volatility, and port disruption risks")
559
 
560
- # Footer
561
  st.markdown("""
562
- ---
563
- **🔌 Yazaki India Global Supply Chain Command Center** |
564
- Import-Focused Planning | OEM Integration + AI-Enhanced Forecasting | International Supplier Network Intelligence
565
-
566
- *Powered by Agentic AI | 8-Week Import Horizon | Comprehensive Global Supply Chain Resilience*
567
- """)
 
 
1
  import streamlit as st
2
  import pandas as pd
 
3
  import plotly.express as px
4
  import plotly.graph_objects as go
5
  from datetime import datetime, timedelta
 
6
 
7
+ # Page config
8
  st.set_page_config(
9
+ page_title="Yazaki India Supply Chain Intelligence",
10
  page_icon="🔌",
11
  layout="wide",
12
  initial_sidebar_state="expanded"
13
  )
14
 
15
+ # Clean, professional CSS styling (identical to Rane version)
16
  st.markdown("""
17
  <style>
18
+ /* Import modern font */
19
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
20
 
21
+ /* Global styling */
22
  .stApp {
23
  font-family: 'Inter', sans-serif;
24
  background-color: #f8fafc;
25
  }
26
 
27
+ /* Main container */
28
  .main-container {
29
  background: white;
30
  border-radius: 12px;
 
34
  border: 1px solid #e2e8f0;
35
  }
36
 
37
+ /* Clean header */
38
  .modern-header {
39
  background: #1e293b;
40
  color: white;
41
  padding: 2rem;
42
  border-radius: 12px;
43
  margin-bottom: 2rem;
44
+ border-left: 4px solid #3b82f6;
45
  }
46
 
47
  .header-title {
 
57
  color: #94a3b8;
58
  }
59
 
60
+ /* Clean metric cards */
61
  .metric-card {
62
  background: white;
63
  padding: 1.5rem;
 
89
  margin-bottom: 0.5rem;
90
  }
91
 
92
+ .metric-change {
93
+ font-size: 0.875rem;
94
+ font-weight: 600;
95
+ padding: 0.25rem 0.75rem;
96
+ border-radius: 6px;
97
+ display: inline-block;
98
  }
99
 
100
+ .metric-positive {
101
+ background-color: #dcfce7;
102
+ color: #166534;
103
  }
104
 
105
+ .metric-negative {
106
+ background-color: #fef2f2;
107
+ color: #dc2626;
108
  }
109
 
110
+ .metric-neutral {
111
+ background-color: #f1f5f9;
112
+ color: #475569;
113
  }
114
 
115
+ /* Clean sidebar */
116
+ .sidebar-content {
117
+ background: white;
118
+ color: #1e293b;
119
+ border-radius: 12px;
120
+ padding: 1.5rem;
121
+ margin-bottom: 1rem;
122
+ border: 1px solid #e2e8f0;
123
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
124
+ }
125
+
126
+ /* Filter section */
127
+ .filter-container {
128
+ background: white;
129
+ padding: 1.5rem;
130
+ border-radius: 12px;
131
+ margin-bottom: 2rem;
132
+ border: 1px solid #e2e8f0;
133
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
134
+ }
135
+
136
+ /* Section headers */
137
+ .section-header {
138
+ font-size: 1.25rem;
139
+ font-weight: 600;
140
+ color: #1e293b;
141
+ margin-bottom: 1.5rem;
142
+ padding-bottom: 0.75rem;
143
+ border-bottom: 2px solid #e2e8f0;
144
+ }
145
+
146
+ /* Status indicators */
147
+ .status-indicator {
148
+ display: inline-block;
149
+ width: 8px;
150
+ height: 8px;
151
+ border-radius: 50%;
152
+ margin-right: 8px;
153
+ }
154
+
155
+ .status-good { background-color: #22c55e; }
156
+ .status-warning { background-color: #f59e0b; }
157
+ .status-critical { background-color: #ef4444; }
158
+
159
+ /* Clean table styling */
160
+ .dataframe table {
161
+ border-collapse: collapse;
162
+ margin: 0;
163
+ font-size: 0.875rem;
164
+ width: 100%;
165
+ background: white;
166
  border-radius: 8px;
167
+ overflow: hidden;
168
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
169
+ }
170
+
171
+ .dataframe th {
172
+ background-color: #f8fafc;
173
+ color: #374151;
174
+ font-weight: 600;
175
+ padding: 12px;
176
+ text-align: left;
177
+ border-bottom: 1px solid #e5e7eb;
178
+ }
179
+
180
+ .dataframe td {
181
+ padding: 12px;
182
+ border-bottom: 1px solid #f3f4f6;
183
+ }
184
+
185
+ .dataframe tr:hover {
186
+ background-color: #f9fafb;
187
+ }
188
+
189
+ /* Remove default streamlit styling */
190
+ .stSelectbox > div > div {
191
+ background-color: white;
192
+ border: 1px solid #d1d5db;
193
+ border-radius: 6px;
194
+ }
195
+
196
+ .stSelectbox > div > div:focus-within {
197
+ border-color: #3b82f6;
198
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
199
  }
200
  </style>
201
  """, unsafe_allow_html=True)
202
 
203
+ # Sample data functions - ONLY changed company/material names
 
 
 
 
 
 
204
  @st.cache_data
205
+ def get_material_data():
206
+ return pd.DataFrame({
207
+ 'Material Group': ['Wire Harnesses', 'Connectors & Terminals', 'Electronic Components',
208
+ 'Fuse Box Assembly', 'Junction Blocks', 'Cable Assembly', 'Power Distribution',
209
+ 'Smart Sensors', 'Relay Components', 'Switch Components'],
210
+ 'Current Rate': [72, 68, 65, 73, 75, 72, 78, 77, 80, 82],
211
+ 'Target Rate': [75, 70, 70, 75, 78, 75, 80, 80, 82, 85],
212
+ 'Trend': ['+2.1%', '+1.8%', '+0.9%', '+2.4%', '+1.2%', '+1.8%', '+2.1%', '+1.9%', '+1.1%', '+1.2%'],
213
+ 'Risk Level': ['Medium', 'High', 'High', 'Low', 'Low', 'Medium', 'Low', 'Low', 'Low', 'Low'],
214
+ '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']
215
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
 
217
  @st.cache_data
218
+ def get_enhanced_metrics():
219
  return {
220
+ 'fulfillment': 74, # Slightly lower due to import challenges
221
+ 'mom_change': -1.2,
222
+ 'material_groups': 89,
223
+ 'skus': 8945,
224
+ 'material_groups_at_risk': 24,
225
+ 'risk_mom_change': 2.8,
226
+ 'skus_at_risk': 31,
227
+ 'sku_risk_mom_change': 3.2,
228
+ 'active_suppliers': 156,
229
+ 'on_time_delivery': 82.4, # Import delays impact
230
+ 'import_dependency': 87
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  }
232
 
233
+ # Clean, professional header - ONLY changed titles
234
+ st.markdown("""
235
+ <div class="modern-header">
236
+ <div class="header-title">🔌 Yazaki India Supply Chain Intelligence Hub</div>
237
+ <div class="header-subtitle">Real-time Import-Focused Supply Chain Dashboard Automotive Wiring Systems Control Tower</div>
238
+ </div>
239
+ """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
+ # Professional sidebar - ONLY changed company context
242
+ with st.sidebar:
243
+ st.markdown("""
244
+ <div class="sidebar-content">
245
+ <h3 style="margin-top: 0; color: #1e293b;">Yazaki Navigation</h3>
246
+ <p style="color: #64748b; font-size: 0.875rem;">Select your workspace</p>
247
+ </div>
248
+ """, unsafe_allow_html=True)
249
+
250
+ nav_options = [
251
+ "Dashboard Home",
252
+ "Import Supply Chain",
253
+ "Control Tower",
254
+ "Wire Harness Groups",
255
+ "Global Supplier Analytics",
256
+ "Import Planning",
257
+ "Port & Logistics",
258
+ "Import Alerts",
259
+ "Compliance Center"
 
 
 
 
 
 
 
 
260
  ]
261
 
262
+ selected_nav = st.selectbox("", nav_options, index=1)
263
+
264
+ # Clean alerts section - ONLY changed to import context
265
+ st.markdown("---")
266
+ st.markdown("**Import Status**")
267
+ st.error("🚢 JNPT port congestion - 4 days delay")
268
+ st.warning("💱 INR depreciation impacting costs")
269
+ st.success("✅ 82% customs clearance on time")
 
 
 
 
 
 
270
 
271
+ # Clean filters section - ONLY changed plant names and some filters
272
  st.markdown("""
273
+ <div class="filter-container">
274
+ <div class="section-header">Import & Supply Chain Filters</div>
 
275
  </div>
276
  """, unsafe_allow_html=True)
277
 
278
+ col1, col2, col3, col4 = st.columns(4)
279
+ col5, col6, col7, col8 = st.columns(4)
 
 
 
 
 
280
 
281
+ with col1:
282
+ plant_location = st.selectbox("Yazaki Plant", ["Chennai (Main)", "Bawal (Haryana)", "Kanchipuram", "All Plants"], index=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
 
284
+ with col2:
285
+ material_group = st.selectbox("Component Category", ["All Components", "Wire Harnesses", "Connectors", "Electronics"], index=0)
286
+
287
+ with col3:
288
+ time_period = st.selectbox("Time Period", ["Current Quarter", "FY2025", "Last 6 Months"], index=0)
289
+
290
+ with col4:
291
+ supplier_tier = st.selectbox("Source Country", ["All Countries", "China", "Japan", "Germany", "South Korea"], index=0)
292
+
293
+ with col5:
294
+ risk_level = st.selectbox("Import Risk", ["All Risk Levels", "High Risk Only", "Medium Risk", "Low Risk"], index=0)
295
+
296
+ with col6:
297
+ performance = st.selectbox("Port Performance", ["All Ports", "JNPT Mumbai", "Chennai Port", "ICD Bangalore"], index=0)
298
+
299
+ with col7:
300
+ geography = st.selectbox("Import Mode", ["All Modes", "Sea Freight", "Air Freight", "Express"], index=0)
301
+
302
+ with col8:
303
+ update_freq = st.selectbox("Customs Status", ["All Status", "Cleared", "In Process", "Held"], index=0)
304
+
305
+ # Get data - SAME structure
306
+ material_df = get_material_data()
307
+ metrics = get_enhanced_metrics()
308
+
309
+ # Clean metrics section - ONLY changed one metric to import-focused
310
+ st.markdown('<div class="section-header">Yazaki India Key Performance Indicators</div>', unsafe_allow_html=True)
311
+
312
+ col1, col2, col3, col4 = st.columns(4)
313
+
314
+ with col1:
315
+ st.markdown(f"""
316
+ <div class="metric-card">
317
+ <div class="metric-number">{metrics['fulfillment']}%</div>
318
+ <div class="metric-label">Overall Fulfillment</div>
319
+ <div class="metric-change metric-negative">↘ {metrics['mom_change']}% MoM</div>
320
+ </div>
321
+ """, unsafe_allow_html=True)
322
+
323
+ with col2:
324
+ st.markdown(f"""
325
+ <div class="metric-card">
326
+ <div class="metric-number">{metrics['import_dependency']}%</div>
327
+ <div class="metric-label">Import Dependency</div>
328
+ <div class="metric-change metric-neutral">Critical Factor</div>
329
+ </div>
330
+ """, unsafe_allow_html=True)
331
+
332
+ with col3:
333
+ st.markdown(f"""
334
+ <div class="metric-card">
335
+ <div class="metric-number">{metrics['material_groups_at_risk']}%</div>
336
+ <div class="metric-label">At-Risk Components</div>
337
+ <div class="metric-change metric-negative">↗ +{metrics['risk_mom_change']}% MoM</div>
338
+ </div>
339
+ """, unsafe_allow_html=True)
340
+
341
+ with col4:
342
+ st.markdown(f"""
343
+ <div class="metric-card">
344
+ <div class="metric-number">{metrics['active_suppliers']:,}</div>
345
+ <div class="metric-label">Global Suppliers</div>
346
+ <div class="metric-change metric-neutral">+12 new</div>
347
+ </div>
348
+ """, unsafe_allow_html=True)
349
+
350
+ # Clean data table - SAME structure
351
+ st.markdown('<div class="section-header">Yazaki Component Group Performance</div>', unsafe_allow_html=True)
352
+
353
+ # Display clean table
354
+ st.dataframe(material_df, use_container_width=True, hide_index=True)
355
+
356
+ # Professional charts - SAME structure, ONLY changed titles
357
+ st.markdown('<div class="section-header">Import Performance Analytics</div>', unsafe_allow_html=True)
358
+
359
+ col1, col2 = st.columns(2)
360
+
361
+ with col1:
362
+ # Clean bar chart - SAME as Rane
363
+ fig1 = px.bar(
364
+ material_df,
365
+ x='Material Group',
366
+ y=['Current Rate', 'Target Rate'],
367
+ title="Fulfillment Rates: Current vs Target",
368
+ color_discrete_sequence=['#3b82f6', '#64748b']
369
+ )
370
 
371
+ fig1.update_layout(
372
+ plot_bgcolor='white',
373
+ paper_bgcolor='white',
374
+ font_family="Inter",
375
+ title_font_size=14,
376
+ title_font_color="#1e293b",
377
+ xaxis_tickangle=-45,
378
+ height=400,
379
+ showlegend=True,
380
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1)
381
+ )
382
 
383
+ st.plotly_chart(fig1, use_container_width=True)
 
384
 
385
+ with col2:
386
+ # Clean pie chart - SAME as Rane
387
+ risk_counts = material_df['Risk Level'].value_counts()
388
+
389
+ fig2 = px.pie(
390
+ values=risk_counts.values,
391
+ names=risk_counts.index,
392
+ title="Risk Distribution",
393
+ color_discrete_sequence=['#22c55e', '#f59e0b', '#ef4444']
394
+ )
395
+
396
+ fig2.update_layout(
397
+ plot_bgcolor='white',
398
+ paper_bgcolor='white',
399
+ font_family="Inter",
400
+ title_font_size=14,
401
+ title_font_color="#1e293b",
402
+ height=400
403
+ )
404
 
405
+ st.plotly_chart(fig2, use_container_width=True)
 
406
 
407
+ # Clean footer - ONLY changed company name
408
  st.markdown("""
409
+ <div style="text-align: center; padding: 1.5rem; color: #64748b; border-top: 1px solid #e2e8f0; margin-top: 2rem; font-size: 0.875rem;">
410
+ Yazaki India Dashboard last updated: {timestamp} Auto-refresh: Every 15 minutes • Data accuracy: 99.7%
411
+ </div>
412
+ """.format(timestamp=datetime.now().strftime("%B %d, %Y at %I:%M %p")), unsafe_allow_html=True)