vincentmorando commited on
Commit
db58958
·
verified ·
1 Parent(s): adadbd7

Update the existing Executive Commercial Summary dashboard (do not rebuild or change layout). Keep all current filters and KPIs exactly as they are. Apply the following improvements and adjustments based on leadership feedback to make the dashboard more actionable while maintaining a clean, balanced layout.

Browse files

Replace the Daily Operational Throughput chart with a new chart titled “Cumulative Progress Toward Period Goal.” This should display cumulative totals for Ordered, Shipped, and Produced pounds across the selected time range, along with additional dashed lines for cumulative Forecast and cumulative Budget to show pacing toward goals. The tooltip for each date should show cumulative totals for all five series as well as the variance from Forecast and Budget. Keep a clear legend and consistent line styling (solid for actuals, dashed for targets). This chart should provide a broader, period-level view of operational throughput rather than day-to-day noise.

Next to this, keep the Performance vs Forecast chart but update it so that the Budget line is always included alongside Actual and Forecast. Do not add Budget as a selectable metric—display all three together. The chart should continue allowing drilldown by metric (Ordered, Shipped, Produced). Ensure consistent axes and formatting with the new cumulative chart.

Improve the Inventory vs Order Backlog section by adding a new table next to it titled “Product Balance Watchlist (Next 5 Days).” This table should display products that are projected to face constraints within the next five days. Include columns for Product, FG Inventory (today), Backlog (total next 5 days), Scheduled Production (next 5 days), and Projected Shortfall (Backlog − [FG Inventory + Scheduled Production]). Only show products where the shortfall is greater than zero. Add simple risk-level color cues based on shortfall severity and make each row expandable or hoverable to reveal a 5-day mini-trend for that product. Clicking a product filters the main Inventory vs Backlog chart to that product.

Next to the Order Lifecycle Analysis chart, add a table titled “Customer Order Pipeline Summary.” This table should list each customer with columns for Open, Shipped, Invoiced, Cancelled, and % Open = Open ÷ (Open + Shipped + Invoiced + Cancelled). Sort customers by % Open descending by default. Clicking a customer filters the lifecycle chart to that customer, and clicking chart elements filters the table.

Add a compact “At-Risk Orders” section below these lifecycle visuals. On the left, include a stacked bar chart where the x-axis is Promise Date and each bar shows late open orders segmented into 1–3 days late, 4–7 days late, and 8+ days late using light amber to red. On the right, include a collapsible table of those late orders showing Order #, Customer, Promise Date, Days Late, Current Status, Order Pounds, and Branch. Clicking any bar filters the table; default the table collapsed to save space.

Keep the Detailed Data View at the bottom unchanged, but make it respond to filtering from any of the new tables and charts. Ensure spacing and card proportions remain consistent so the dashboard remains well-balanced on a single scroll view.

Files changed (1) hide show
  1. index.html +269 -133
index.html CHANGED
@@ -263,23 +263,22 @@
263
  </div>
264
  <!-- First Chart Row -->
265
  <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
266
- <!-- Daily Operational Throughput -->
267
  <div class="bg-white p-4 rounded-lg shadow-md">
268
  <div class="flex justify-between items-center mb-4">
269
- <h2 class="text-lg font-semibold text-gray-800">Daily Operational Throughput</h2>
270
  <div class="flex space-x-2">
271
  <button class="px-2 py-1 text-xs bg-gray-100 rounded hover:bg-gray-200">Reset</button>
272
  </div>
273
  </div>
274
  <div class="chart-container">
275
- <canvas id="throughputChart"></canvas>
276
  </div>
277
  </div>
278
-
279
- <!-- Performance vs Forecast -->
280
  <div class="bg-white p-4 rounded-lg shadow-md">
281
  <div class="flex justify-between items-center mb-4">
282
- <h2 class="text-lg font-semibold text-gray-800">Performance vs Forecast</h2>
283
  <div class="flex space-x-2">
284
  <select id="metricToggle" class="text-xs border border-gray-300 rounded px-2 py-1">
285
  <option>Ordered</option>
@@ -293,24 +292,57 @@
293
  <canvas id="forecastChart"></canvas>
294
  </div>
295
  </div>
296
- </div>
297
 
298
  <!-- Second Chart Row -->
299
  <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
300
- <!-- Inventory vs Order Backlog -->
301
- <div class="bg-white p-4 rounded-lg shadow-md">
302
- <div class="flex justify-between items-center mb-4">
303
- <h2 class="text-lg font-semibold text-gray-800">Inventory vs Order Backlog</h2>
304
- <div class="flex space-x-2">
305
- <button class="px-2 py-1 text-xs bg-gray-100 rounded hover:bg-gray-200">Reset</button>
 
 
 
 
 
306
  </div>
307
  </div>
308
- <div class="chart-container">
309
- <canvas id="inventoryChart"></canvas>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  </div>
311
  </div>
312
-
313
- <!-- Financial Performance Breakdown -->
314
  <div class="bg-white p-4 rounded-lg shadow-md">
315
  <div class="flex justify-between items-center mb-4">
316
  <h2 class="text-lg font-semibold text-gray-800">Financial Performance Breakdown</h2>
@@ -327,74 +359,121 @@
327
  </div>
328
  </div>
329
  </div>
330
-
331
  <!-- Order Lifecycle Analysis -->
332
- <div class="bg-white p-4 rounded-lg shadow-md mb-6">
333
- <div class="flex justify-between items-center mb-4">
334
- <h2 class="text-lg font-semibold text-gray-800">Order Lifecycle Analysis</h2>
335
- <div class="flex space-x-2">
336
- <button class="px-2 py-1 text-xs bg-gray-100 rounded hover:bg-gray-200">Reset</button>
337
- </div>
338
- </div>
339
- <div class="chart-container">
340
- <canvas id="lifecycleChart"></canvas>
341
- </div>
342
- </div>
343
- <!-- At-Risk Orders -->
344
- <div class="bg-white p-4 rounded-lg shadow-md mb-6">
345
- <h2 class="text-lg font-semibold text-gray-800 mb-4">At-Risk Orders</h2>
346
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
347
- <!-- Chart -->
348
- <div>
349
- <div class="chart-container">
350
- <canvas id="atRiskChart"></canvas>
351
  </div>
352
  </div>
353
- <!-- Table -->
 
 
 
 
 
354
  <div class="overflow-x-auto">
355
  <table class="min-w-full divide-y divide-gray-200">
356
  <thead class="bg-gray-50">
357
  <tr>
358
- <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Order #</th>
359
  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Customer</th>
360
- <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Promise Date</th>
361
- <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Days Late</th>
362
- <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
363
- <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Pounds</th>
 
364
  </tr>
365
  </thead>
366
  <tbody class="bg-white divide-y divide-gray-200">
367
- <tr>
368
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">ORD-1001</td>
369
  <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Acme Corp</td>
370
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">2023-06-05</td>
371
- <td class="px-4 py-3 whitespace-nowrap text-sm text-red-600 font-medium">3</td>
372
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Open</td>
373
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">5,200</td>
 
374
  </tr>
375
- <tr>
376
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">ORD-1002</td>
377
  <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Beta LLC</td>
378
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">2023-06-03</td>
379
- <td class="px-4 py-3 whitespace-nowrap text-sm text-red-800 font-medium">5</td>
380
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Processing</td>
381
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">8,400</td>
382
- </tr>
383
- <tr>
384
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">ORD-1003</td>
385
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Gamma Inc</td>
386
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">2023-05-30</td>
387
- <td class="px-4 py-3 whitespace-nowrap text-sm text-red-900 font-medium">9</td>
388
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Open</td>
389
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">12,700</td>
390
  </tr>
391
  </tbody>
392
  </table>
393
  </div>
394
  </div>
395
  </div>
396
-
397
- <!-- Detailed Data View -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
  <div class="bg-white rounded-lg shadow-md overflow-hidden">
399
  <button id="toggleTable" class="w-full px-4 py-3 bg-gray-100 text-left font-medium text-gray-800 hover:bg-gray-200 flex justify-between items-center">
400
  <span>Detailed Data View</span>
@@ -455,84 +534,131 @@
455
  document.getElementById('endDate').valueAsDate = today;
456
 
457
  feather.replace();
458
-
459
- // Toggle table visibility
460
  const toggleTable = document.getElementById('toggleTable');
461
  const tableContent = document.getElementById('tableContent');
462
- const chevron = toggleTable.querySelector('i');
463
 
464
  toggleTable.addEventListener('click', function() {
465
  tableContent.classList.toggle('expanded');
466
  const isExpanded = tableContent.classList.contains('expanded');
467
- chevron.style.transform = isExpanded ? 'rotate(180deg)' : 'rotate(0)';
468
  });
469
 
470
- // Sample data for charts (in a real app, this would come from an API)
471
- const dates = Array.from({length: 30}, (_, i) => `Day ${i+1}`);
 
472
 
473
- // Throughput Chart
474
- const throughputCtx = document.getElementById('throughputChart').getContext('2d');
475
- const throughputChart = new Chart(throughputCtx, {
476
- type: 'line',
477
- data: {
478
- labels: dates,
479
- datasets: [
480
- {
481
- label: 'Ordered',
482
- data: Array.from({length: 30}, () => Math.floor(Math.random() * 50000) + 30000),
483
- borderColor: '#001f3f',
484
- backgroundColor: 'transparent',
485
- tension: 0.1
486
- },
487
- {
488
- label: 'Shipped',
489
- data: Array.from({length: 30}, () => Math.floor(Math.random() * 45000) + 28000),
490
- borderColor: '#0074D9',
491
- backgroundColor: 'transparent',
492
- tension: 0.1
493
- },
494
- {
495
- label: 'Produced',
496
- data: Array.from({length: 30}, () => Math.floor(Math.random() * 48000) + 29000),
497
- borderColor: '#7FDBFF',
498
- backgroundColor: 'transparent',
499
- tension: 0.1
500
- }
501
- ]
502
- },
503
- options: {
504
- responsive: true,
505
- maintainAspectRatio: false,
506
- scales: {
507
- y: {
508
- beginAtZero: true,
509
- ticks: {
510
- callback: function(value) {
511
- return (value / 1000) + 'k';
512
- }
513
- },
514
- grid: {
515
- color: 'rgba(0, 0, 0, 0.05)'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
516
  }
517
  },
518
- x: {
519
- grid: {
520
- display: false
521
- }
522
  }
523
  },
524
- plugins: {
525
- tooltip: {
526
- callbacks: {
527
- label: function(context) {
528
- return context.dataset.label + ': ' + context.raw.toLocaleString() + ' lbs';
 
 
 
 
 
 
 
 
 
 
 
 
 
529
  }
 
530
  }
531
  }
532
  }
533
  }
534
- });
535
-
536
  // Forecast Chart
537
  const forecastCtx = document.getElementById('forecastChart').getContext('2d');
538
  const forecastChart = new Chart(forecastCtx, {
@@ -545,6 +671,7 @@ feather.replace();
545
  data: Array.from({length: 30}, () => Math.floor(Math.random() * 50000) + 30000),
546
  borderColor: '#001f3f',
547
  backgroundColor: 'transparent',
 
548
  tension: 0.1
549
  },
550
  {
@@ -552,12 +679,22 @@ feather.replace();
552
  data: Array.from({length: 30}, () => Math.floor(Math.random() * 48000) + 32000),
553
  borderColor: '#001f3f',
554
  backgroundColor: 'transparent',
 
 
 
 
 
 
 
 
 
 
555
  borderDash: [5, 5],
556
  tension: 0.1
557
  }
558
  ]
559
  },
560
- options: {
561
  responsive: true,
562
  maintainAspectRatio: false,
563
  scales: {
@@ -876,23 +1013,23 @@ const lifecycleChart = new Chart(lifecycleCtx, {
876
  }
877
  }
878
  });
879
-
880
  // Update forecast chart when metric changes
881
  document.getElementById('metricToggle').addEventListener('change', function() {
882
  const metric = this.value;
883
 
884
- // Update dataset labels and data based on selected metric
885
  forecastChart.data.datasets[0].label = 'Actual ' + metric;
886
  forecastChart.data.datasets[1].label = 'Forecast ' + metric;
 
887
 
888
  // Generate new random data for the selected metric
889
  forecastChart.data.datasets[0].data = Array.from({length: 30}, () => Math.floor(Math.random() * 50000) + 30000);
890
  forecastChart.data.datasets[1].data = Array.from({length: 30}, () => Math.floor(Math.random() * 48000) + 32000);
 
891
 
892
  forecastChart.update();
893
  });
894
-
895
- // Update financial chart when view changes
896
  document.getElementById('financialViewToggle').addEventListener('change', function() {
897
  const view = this.value;
898
 
@@ -916,7 +1053,6 @@ const lifecycleChart = new Chart(lifecycleCtx, {
916
 
917
  financialChart.update();
918
  });
919
-
920
  // Date range filter handler
921
  const dateInputs = document.querySelectorAll('#startDate, #endDate');
922
  dateInputs.forEach(input => {
@@ -929,7 +1065,7 @@ const lifecycleChart = new Chart(lifecycleCtx, {
929
  console.log(`Date range changed: ${startDate} to ${endDate}`);
930
 
931
  // Update all charts with new date range
932
- throughputChart.update();
933
  forecastChart.update();
934
  inventoryChart.update();
935
  financialChart.update();
@@ -938,7 +1074,7 @@ const lifecycleChart = new Chart(lifecycleCtx, {
938
  }
939
  });
940
  });
941
- });
942
  </script>
943
  </body>
944
  </html>
 
263
  </div>
264
  <!-- First Chart Row -->
265
  <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
266
+ <!-- Cumulative Progress Toward Period Goal -->
267
  <div class="bg-white p-4 rounded-lg shadow-md">
268
  <div class="flex justify-between items-center mb-4">
269
+ <h2 class="text-lg font-semibold text-gray-800">Cumulative Progress Toward Period Goal</h2>
270
  <div class="flex space-x-2">
271
  <button class="px-2 py-1 text-xs bg-gray-100 rounded hover:bg-gray-200">Reset</button>
272
  </div>
273
  </div>
274
  <div class="chart-container">
275
+ <canvas id="cumulativeChart"></canvas>
276
  </div>
277
  </div>
278
+ <!-- Performance vs Forecast & Budget -->
 
279
  <div class="bg-white p-4 rounded-lg shadow-md">
280
  <div class="flex justify-between items-center mb-4">
281
+ <h2 class="text-lg font-semibold text-gray-800">Performance vs Forecast & Budget</h2>
282
  <div class="flex space-x-2">
283
  <select id="metricToggle" class="text-xs border border-gray-300 rounded px-2 py-1">
284
  <option>Ordered</option>
 
292
  <canvas id="forecastChart"></canvas>
293
  </div>
294
  </div>
295
+ </div>
296
 
297
  <!-- Second Chart Row -->
298
  <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
299
+ <!-- Inventory Management -->
300
+ <div class="grid grid-cols-1 gap-4">
301
+ <div class="bg-white p-4 rounded-lg shadow-md">
302
+ <div class="flex justify-between items-center mb-4">
303
+ <h2 class="text-lg font-semibold text-gray-800">Inventory vs Order Backlog</h2>
304
+ <div class="flex space-x-2">
305
+ <button class="px-2 py-1 text-xs bg-gray-100 rounded hover:bg-gray-200">Reset</button>
306
+ </div>
307
+ </div>
308
+ <div class="chart-container">
309
+ <canvas id="inventoryChart"></canvas>
310
  </div>
311
  </div>
312
+ <div class="bg-white p-4 rounded-lg shadow-md">
313
+ <h2 class="text-lg font-semibold text-gray-800 mb-4">Product Balance Watchlist (Next 5 Days)</h2>
314
+ <div class="overflow-x-auto">
315
+ <table class="min-w-full divide-y divide-gray-200">
316
+ <thead class="bg-gray-50">
317
+ <tr>
318
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Product</th>
319
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">FG Inventory</th>
320
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Backlog</th>
321
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Scheduled Prod</th>
322
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Projected Shortfall</th>
323
+ </tr>
324
+ </thead>
325
+ <tbody class="bg-white divide-y divide-gray-200">
326
+ <tr class="hover:bg-gray-50 cursor-pointer" onclick="filterInventoryChart('Premium')">
327
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Premium</td>
328
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">12,500</td>
329
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">18,200</td>
330
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">4,800</td>
331
+ <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-red-700">800</td>
332
+ </tr>
333
+ <tr class="hover:bg-gray-50 cursor-pointer" onclick="filterInventoryChart('Custom')">
334
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Custom</td>
335
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">8,700</td>
336
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">11,500</td>
337
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">3,200</td>
338
+ <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-amber-600">400</td>
339
+ </tr>
340
+ </tbody>
341
+ </table>
342
+ </div>
343
  </div>
344
  </div>
345
+ <!-- Financial Performance Breakdown -->
 
346
  <div class="bg-white p-4 rounded-lg shadow-md">
347
  <div class="flex justify-between items-center mb-4">
348
  <h2 class="text-lg font-semibold text-gray-800">Financial Performance Breakdown</h2>
 
359
  </div>
360
  </div>
361
  </div>
 
362
  <!-- Order Lifecycle Analysis -->
363
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
364
+ <div class="bg-white p-4 rounded-lg shadow-md">
365
+ <div class="flex justify-between items-center mb-4">
366
+ <h2 class="text-lg font-semibold text-gray-800">Order Lifecycle Analysis</h2>
367
+ <div class="flex space-x-2">
368
+ <button class="px-2 py-1 text-xs bg-gray-100 rounded hover:bg-gray-200">Reset</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  </div>
370
  </div>
371
+ <div class="chart-container">
372
+ <canvas id="lifecycleChart"></canvas>
373
+ </div>
374
+ </div>
375
+ <div class="bg-white p-4 rounded-lg shadow-md">
376
+ <h2 class="text-lg font-semibold text-gray-800 mb-4">Customer Order Pipeline Summary</h2>
377
  <div class="overflow-x-auto">
378
  <table class="min-w-full divide-y divide-gray-200">
379
  <thead class="bg-gray-50">
380
  <tr>
 
381
  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Customer</th>
382
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Open</th>
383
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Shipped</th>
384
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Invoiced</th>
385
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Cancelled</th>
386
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">% Open</th>
387
  </tr>
388
  </thead>
389
  <tbody class="bg-white divide-y divide-gray-200">
390
+ <tr class="hover:bg-gray-50 cursor-pointer" onclick="filterLifecycleChart('Acme Corp')">
 
391
  <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Acme Corp</td>
392
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">25</td>
393
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">18</td>
394
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">15</td>
395
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">2</td>
396
+ <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-amber-600">42%</td>
397
  </tr>
398
+ <tr class="hover:bg-gray-50 cursor-pointer" onclick="filterLifecycleChart('Beta LLC')">
 
399
  <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Beta LLC</td>
400
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">12</td>
401
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">22</td>
402
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">18</td>
403
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">1</td>
404
+ <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-blue-600">23%</td>
 
 
 
 
 
 
 
405
  </tr>
406
  </tbody>
407
  </table>
408
  </div>
409
  </div>
410
  </div>
411
+ <!-- At-Risk Orders -->
412
+ <div class="bg-white p-4 rounded-lg shadow-md mb-6">
413
+ <h2 class="text-lg font-semibold text-gray-800 mb-4">At-Risk Orders</h2>
414
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
415
+ <!-- Chart -->
416
+ <div>
417
+ <div class="chart-container">
418
+ <canvas id="atRiskChart"></canvas>
419
+ </div>
420
+ </div>
421
+ <!-- Collapsible Table -->
422
+ <div>
423
+ <button id="toggleRiskTable" class="w-full px-4 py-2 bg-gray-100 text-left font-medium text-gray-800 hover:bg-gray-200 flex justify-between items-center mb-2">
424
+ <span>Late Orders Detail View</span>
425
+ <i data-feather="chevron-down" class="transform transition-transform duration-200"></i>
426
+ </button>
427
+ <div id="riskTableContent" class="collapsible-content">
428
+ <div class="overflow-x-auto">
429
+ <table class="min-w-full divide-y divide-gray-200">
430
+ <thead class="bg-gray-50">
431
+ <tr>
432
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Order #</th>
433
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Customer</th>
434
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Promise Date</th>
435
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Days Late</th>
436
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
437
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Pounds</th>
438
+ <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Branch</th>
439
+ </tr>
440
+ </thead>
441
+ <tbody class="bg-white divide-y divide-gray-200">
442
+ <tr>
443
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">ORD-1001</td>
444
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Acme Corp</td>
445
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">2023-06-05</td>
446
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-red-600 font-medium">3</td>
447
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Open</td>
448
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">5,200</td>
449
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">North</td>
450
+ </tr>
451
+ <tr>
452
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">ORD-1002</td>
453
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Beta LLC</td>
454
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">2023-06-03</td>
455
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-red-800 font-medium">5</td>
456
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Processing</td>
457
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">8,400</td>
458
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">South</td>
459
+ </tr>
460
+ <tr>
461
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">ORD-1003</td>
462
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Gamma Inc</td>
463
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">2023-05-30</td>
464
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-red-900 font-medium">9</td>
465
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">Open</td>
466
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">12,700</td>
467
+ <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">West</td>
468
+ </tr>
469
+ </tbody>
470
+ </table>
471
+ </div>
472
+ </div>
473
+ </div>
474
+ </div>
475
+ </div>
476
+ <!-- Detailed Data View -->
477
  <div class="bg-white rounded-lg shadow-md overflow-hidden">
478
  <button id="toggleTable" class="w-full px-4 py-3 bg-gray-100 text-left font-medium text-gray-800 hover:bg-gray-200 flex justify-between items-center">
479
  <span>Detailed Data View</span>
 
534
  document.getElementById('endDate').valueAsDate = today;
535
 
536
  feather.replace();
537
+ // Toggle table visibilities
 
538
  const toggleTable = document.getElementById('toggleTable');
539
  const tableContent = document.getElementById('tableContent');
540
+ const tableChevron = toggleTable.querySelector('i');
541
 
542
  toggleTable.addEventListener('click', function() {
543
  tableContent.classList.toggle('expanded');
544
  const isExpanded = tableContent.classList.contains('expanded');
545
+ tableChevron.style.transform = isExpanded ? 'rotate(180deg)' : 'rotate(0)';
546
  });
547
 
548
+ const toggleRiskTable = document.getElementById('toggleRiskTable');
549
+ const riskTableContent = document.getElementById('riskTableContent');
550
+ const riskChevron = toggleRiskTable.querySelector('i');
551
 
552
+ toggleRiskTable.addEventListener('click', function() {
553
+ riskTableContent.classList.toggle('expanded');
554
+ const isExpanded = riskTableContent.classList.contains('expanded');
555
+ riskChevron.style.transform = isExpanded ? 'rotate(180deg)' : 'rotate(0)';
556
+ });
557
+
558
+ // Filter functions
559
+ function filterInventoryChart(product) {
560
+ console.log(`Filtering inventory chart for ${product}`);
561
+ // In a real app, this would update the chart data
562
+ }
563
+
564
+ function filterLifecycleChart(customer) {
565
+ console.log(`Filtering lifecycle chart for ${customer}`);
566
+ // In a real app, this would update the chart data
567
+ }
568
+ // Sample data for charts (in a real app, this would come from an API)
569
+ const dates = Array.from({length: 30}, (_, i) => `Day ${i+1}`);
570
+ // Cumulative Progress Chart
571
+ const cumulativeCtx = document.getElementById('cumulativeChart').getContext('2d');
572
+ const cumulativeChart = new Chart(cumulativeCtx, {
573
+ type: 'line',
574
+ data: {
575
+ labels: dates,
576
+ datasets: [
577
+ {
578
+ label: 'Cumulative Ordered',
579
+ data: Array.from({length: 30}, (_, i) => 30000 * (i + 1) + Math.floor(Math.random() * 100000)),
580
+ borderColor: '#001f3f',
581
+ backgroundColor: 'transparent',
582
+ borderWidth: 2,
583
+ tension: 0.1
584
+ },
585
+ {
586
+ label: 'Cumulative Shipped',
587
+ data: Array.from({length: 30}, (_, i) => 28000 * (i + 1) + Math.floor(Math.random() * 90000)),
588
+ borderColor: '#0074D9',
589
+ backgroundColor: 'transparent',
590
+ borderWidth: 2,
591
+ tension: 0.1
592
+ },
593
+ {
594
+ label: 'Cumulative Produced',
595
+ data: Array.from({length: 30}, (_, i) => 29000 * (i + 1) + Math.floor(Math.random() * 95000)),
596
+ borderColor: '#7FDBFF',
597
+ backgroundColor: 'transparent',
598
+ borderWidth: 2,
599
+ tension: 0.1
600
+ },
601
+ {
602
+ label: 'Cumulative Forecast',
603
+ data: Array.from({length: 30}, (_, i) => 32000 * (i + 1)),
604
+ borderColor: '#001f3f',
605
+ backgroundColor: 'transparent',
606
+ borderWidth: 2,
607
+ borderDash: [5, 5],
608
+ tension: 0.1
609
+ },
610
+ {
611
+ label: 'Cumulative Budget',
612
+ data: Array.from({length: 30}, (_, i) => 30000 * (i + 1)),
613
+ borderColor: '#2ECC40',
614
+ backgroundColor: 'transparent',
615
+ borderWidth: 2,
616
+ borderDash: [5, 5],
617
+ tension: 0.1
618
+ }
619
+ ]
620
+ },
621
+ options: {
622
+ responsive: true,
623
+ maintainAspectRatio: false,
624
+ scales: {
625
+ y: {
626
+ beginAtZero: true,
627
+ ticks: {
628
+ callback: function(value) {
629
+ return (value / 1000) + 'k';
630
  }
631
  },
632
+ grid: {
633
+ color: 'rgba(0, 0, 0, 0.05)'
 
 
634
  }
635
  },
636
+ x: {
637
+ grid: {
638
+ display: false
639
+ }
640
+ }
641
+ },
642
+ plugins: {
643
+ tooltip: {
644
+ callbacks: {
645
+ label: function(context) {
646
+ let label = context.dataset.label || '';
647
+ label += ': ' + context.raw.toLocaleString() + ' lbs';
648
+ if (context.datasetIndex < 3) {
649
+ const variance = context.raw - context.chart.data.datasets[3].data[context.dataIndex];
650
+ label += '\nvs Forecast: ' + (variance >= 0 ? '+' : '') + (variance / 1000).toFixed(1) + 'k';
651
+
652
+ const budgetVariance = context.raw - context.chart.data.datasets[4].data[context.dataIndex];
653
+ label += '\nvs Budget: ' + (budgetVariance >= 0 ? '+' : '') + (budgetVariance / 1000).toFixed(1) + 'k';
654
  }
655
+ return label;
656
  }
657
  }
658
  }
659
  }
660
+ }
661
+ });
662
  // Forecast Chart
663
  const forecastCtx = document.getElementById('forecastChart').getContext('2d');
664
  const forecastChart = new Chart(forecastCtx, {
 
671
  data: Array.from({length: 30}, () => Math.floor(Math.random() * 50000) + 30000),
672
  borderColor: '#001f3f',
673
  backgroundColor: 'transparent',
674
+ borderWidth: 2,
675
  tension: 0.1
676
  },
677
  {
 
679
  data: Array.from({length: 30}, () => Math.floor(Math.random() * 48000) + 32000),
680
  borderColor: '#001f3f',
681
  backgroundColor: 'transparent',
682
+ borderWidth: 2,
683
+ borderDash: [5, 5],
684
+ tension: 0.1
685
+ },
686
+ {
687
+ label: 'Budget Ordered',
688
+ data: Array.from({length: 30}, () => Math.floor(Math.random() * 47000) + 31000),
689
+ borderColor: '#2ECC40',
690
+ backgroundColor: 'transparent',
691
+ borderWidth: 2,
692
  borderDash: [5, 5],
693
  tension: 0.1
694
  }
695
  ]
696
  },
697
+ options: {
698
  responsive: true,
699
  maintainAspectRatio: false,
700
  scales: {
 
1013
  }
1014
  }
1015
  });
 
1016
  // Update forecast chart when metric changes
1017
  document.getElementById('metricToggle').addEventListener('change', function() {
1018
  const metric = this.value;
1019
 
1020
+ // Update all dataset labels and data based on selected metric
1021
  forecastChart.data.datasets[0].label = 'Actual ' + metric;
1022
  forecastChart.data.datasets[1].label = 'Forecast ' + metric;
1023
+ forecastChart.data.datasets[2].label = 'Budget ' + metric;
1024
 
1025
  // Generate new random data for the selected metric
1026
  forecastChart.data.datasets[0].data = Array.from({length: 30}, () => Math.floor(Math.random() * 50000) + 30000);
1027
  forecastChart.data.datasets[1].data = Array.from({length: 30}, () => Math.floor(Math.random() * 48000) + 32000);
1028
+ forecastChart.data.datasets[2].data = Array.from({length: 30}, () => Math.floor(Math.random() * 47000) + 31000);
1029
 
1030
  forecastChart.update();
1031
  });
1032
+ // Update financial chart when view changes
 
1033
  document.getElementById('financialViewToggle').addEventListener('change', function() {
1034
  const view = this.value;
1035
 
 
1053
 
1054
  financialChart.update();
1055
  });
 
1056
  // Date range filter handler
1057
  const dateInputs = document.querySelectorAll('#startDate, #endDate');
1058
  dateInputs.forEach(input => {
 
1065
  console.log(`Date range changed: ${startDate} to ${endDate}`);
1066
 
1067
  // Update all charts with new date range
1068
+ cumulativeChart.update();
1069
  forecastChart.update();
1070
  inventoryChart.update();
1071
  financialChart.update();
 
1074
  }
1075
  });
1076
  });
1077
+ });
1078
  </script>
1079
  </body>
1080
  </html>