Aryanshh commited on
Commit
802c5f2
·
1 Parent(s): 7877689

feat: Add Live Impact Preview to HUD Control Center

Browse files
Files changed (3) hide show
  1. dashboard/app.js +37 -7
  2. dashboard/index.html +7 -0
  3. dashboard/style.css +26 -0
dashboard/app.js CHANGED
@@ -1,5 +1,12 @@
1
  const API_BASE = window.location.origin;
2
 
 
 
 
 
 
 
 
3
  // State Management
4
  async function updateState() {
5
  try {
@@ -11,15 +18,11 @@ async function updateState() {
11
  status.textContent = 'ONLINE';
12
  status.className = 'status-badge status-online';
13
 
14
- // Update Multi-objective Metrics
15
  document.getElementById('carbon-value').textContent = data.carbon.toFixed(0);
16
  document.getElementById('cash-value').textContent = data.cash.toLocaleString();
17
-
18
- // Update Inventory UI
19
  document.getElementById('chips-count').textContent = data.inventory.chips;
20
  document.getElementById('sensors-count').textContent = data.inventory.sensors;
21
 
22
- // Update News
23
  const newsAlert = document.getElementById('news-alert');
24
  if (data.news) {
25
  document.getElementById('news-text').textContent = data.news;
@@ -28,7 +31,6 @@ async function updateState() {
28
  newsAlert.classList.add('hidden');
29
  }
30
 
31
- // Update Shipments
32
  const container = document.getElementById('shipments-container');
33
  if (data.orders && data.orders.length > 0) {
34
  container.innerHTML = data.orders.map(order => `
@@ -51,17 +53,44 @@ async function updateState() {
51
 
52
  // UI Controls
53
  const actionSelect = document.getElementById('action-select');
 
 
54
  const orderOptions = document.getElementById('order-options');
55
  const productionOptions = document.getElementById('production-options');
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  actionSelect.addEventListener('change', () => {
58
  orderOptions.classList.add('hidden');
59
  productionOptions.classList.add('hidden');
60
 
61
  if (actionSelect.value === 'order_parts') orderOptions.classList.remove('hidden');
62
  if (actionSelect.value === 'produce') productionOptions.classList.remove('hidden');
 
63
  });
64
 
 
 
65
  function log(message, type = 'system') {
66
  const box = document.getElementById('activity-log');
67
  const entry = document.createElement('p');
@@ -85,8 +114,8 @@ async function executeAction(overrideType = null) {
85
  let actionObj = { action_type: type };
86
 
87
  if (type === 'order_parts') {
88
- actionObj.part_type = document.getElementById('part-select').value;
89
- actionObj.mode = document.getElementById('mode-select').value;
90
  actionObj.quantity = 5;
91
  } else if (type === 'produce') {
92
  actionObj.product = document.getElementById('product-select').value;
@@ -151,3 +180,4 @@ document.getElementById('reset-btn').addEventListener('click', manualReset);
151
  // Start
152
  updateState();
153
  setInterval(updateState, 3000);
 
 
1
  const API_BASE = window.location.origin;
2
 
3
+ // Simulation Specs (Mirrors env.py)
4
+ const SPECS = {
5
+ sea: { cost: 1.0, carbon: 0.1 },
6
+ air: { cost: 5.0, carbon: 2.0 },
7
+ rail: { cost: 2.5, carbon: 0.5 }
8
+ };
9
+
10
  // State Management
11
  async function updateState() {
12
  try {
 
18
  status.textContent = 'ONLINE';
19
  status.className = 'status-badge status-online';
20
 
 
21
  document.getElementById('carbon-value').textContent = data.carbon.toFixed(0);
22
  document.getElementById('cash-value').textContent = data.cash.toLocaleString();
 
 
23
  document.getElementById('chips-count').textContent = data.inventory.chips;
24
  document.getElementById('sensors-count').textContent = data.inventory.sensors;
25
 
 
26
  const newsAlert = document.getElementById('news-alert');
27
  if (data.news) {
28
  document.getElementById('news-text').textContent = data.news;
 
31
  newsAlert.classList.add('hidden');
32
  }
33
 
 
34
  const container = document.getElementById('shipments-container');
35
  if (data.orders && data.orders.length > 0) {
36
  container.innerHTML = data.orders.map(order => `
 
53
 
54
  // UI Controls
55
  const actionSelect = document.getElementById('action-select');
56
+ const partSelect = document.getElementById('part-select');
57
+ const modeSelect = document.getElementById('mode-select');
58
  const orderOptions = document.getElementById('order-options');
59
  const productionOptions = document.getElementById('production-options');
60
 
61
+ function updatePreview() {
62
+ const type = actionSelect.value;
63
+ let cost = 0;
64
+ let carbon = 0;
65
+
66
+ if (type === 'order_parts') {
67
+ const mode = modeSelect.value;
68
+ const s = SPECS[mode];
69
+ cost = 10 * 5 * s.cost; // Base $10 * 5 qty * mode mult
70
+ carbon = 5 * s.carbon; // 5 qty * mode carbon
71
+ } else if (type === 'produce') {
72
+ cost = 0; // Production is free but uses parts
73
+ carbon = 5.0; // Minimal factory footprint
74
+ } else if (type === 'offset') {
75
+ cost = 100 * 2.0; // $2 per unit
76
+ carbon = -100;
77
+ }
78
+
79
+ document.getElementById('preview-cost').textContent = `$${cost.toFixed(0)}`;
80
+ document.getElementById('preview-carbon').textContent = `${carbon > 0 ? '+' : ''}${carbon.toFixed(1)}kg CO2`;
81
+ }
82
+
83
  actionSelect.addEventListener('change', () => {
84
  orderOptions.classList.add('hidden');
85
  productionOptions.classList.add('hidden');
86
 
87
  if (actionSelect.value === 'order_parts') orderOptions.classList.remove('hidden');
88
  if (actionSelect.value === 'produce') productionOptions.classList.remove('hidden');
89
+ updatePreview();
90
  });
91
 
92
+ modeSelect.addEventListener('change', updatePreview);
93
+
94
  function log(message, type = 'system') {
95
  const box = document.getElementById('activity-log');
96
  const entry = document.createElement('p');
 
114
  let actionObj = { action_type: type };
115
 
116
  if (type === 'order_parts') {
117
+ actionObj.part_type = partSelect.value;
118
+ actionObj.mode = modeSelect.value;
119
  actionObj.quantity = 5;
120
  } else if (type === 'produce') {
121
  actionObj.product = document.getElementById('product-select').value;
 
180
  // Start
181
  updateState();
182
  setInterval(updateState, 3000);
183
+ updatePreview();
dashboard/index.html CHANGED
@@ -50,6 +50,13 @@
50
  </select>
51
  </div>
52
 
 
 
 
 
 
 
 
53
  <div class="action-buttons">
54
  <button id="step-btn" class="btn btn-primary">Execute Action</button>
55
  <button id="skip-btn" class="btn btn-secondary">Skip Day</button>
 
50
  </select>
51
  </div>
52
 
53
+ <div id="impact-preview" class="impact-preview">
54
+ <span class="preview-label">Live Estimate:</span>
55
+ <span id="preview-cost" class="val">$0</span>
56
+ <span class="sep">|</span>
57
+ <span id="preview-carbon" class="val">0kg CO2</span>
58
+ </div>
59
+
60
  <div class="action-buttons">
61
  <button id="step-btn" class="btn btn-primary">Execute Action</button>
62
  <button id="skip-btn" class="btn btn-secondary">Skip Day</button>
dashboard/style.css CHANGED
@@ -275,6 +275,32 @@ select {
275
  100% { border-color: #FFC1C1; }
276
  }
277
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  footer {
279
  text-align: center;
280
  margin-top: 4rem;
 
275
  100% { border-color: #FFC1C1; }
276
  }
277
 
278
+ .impact-preview {
279
+ margin: 1.5rem 0;
280
+ padding: 1rem;
281
+ background: #F0F4F0;
282
+ border-radius: 8px;
283
+ border-left: 4px solid var(--primary-green);
284
+ font-size: 0.9rem;
285
+ display: flex;
286
+ gap: 0.75rem;
287
+ align-items: center;
288
+ }
289
+
290
+ .impact-preview .preview-label {
291
+ font-weight: 600;
292
+ color: var(--text-muted);
293
+ }
294
+
295
+ .impact-preview .val {
296
+ font-weight: 600;
297
+ color: var(--deep-forest);
298
+ }
299
+
300
+ .impact-preview .sep {
301
+ color: var(--border-tan);
302
+ }
303
+
304
  footer {
305
  text-align: center;
306
  margin-top: 4rem;