Aryanshh commited on
Commit
01c97f6
·
1 Parent(s): 495eca8

feat: Upgrade Eco-HUD to Fully Interactive Command Center

Browse files
Files changed (3) hide show
  1. dashboard/app.js +67 -22
  2. dashboard/index.html +42 -17
  3. dashboard/style.css +56 -0
dashboard/app.js CHANGED
@@ -1,13 +1,12 @@
1
  const API_BASE = window.location.origin;
2
 
 
3
  async function updateState() {
4
  try {
5
  const response = await fetch(`${API_BASE}/state`);
6
  if (!response.ok) throw new Error('API Unreachable');
7
-
8
  const data = await response.json();
9
 
10
- // Update Status
11
  const status = document.getElementById('connection-status');
12
  status.textContent = 'ONLINE';
13
  status.className = 'status-badge status-online';
@@ -16,17 +15,21 @@ async function updateState() {
16
  document.getElementById('carbon-value').textContent = data.carbon.toFixed(0);
17
  document.getElementById('cash-value').textContent = data.cash.toLocaleString();
18
 
 
 
 
 
19
  // Update Shipments
20
  const container = document.getElementById('shipments-container');
21
  if (data.orders && data.orders.length > 0) {
22
  container.innerHTML = data.orders.map(order => `
23
  <div class="shipment-item">
24
- <span>Order: ${order.product || 'Standard Set'}</span>
25
- <strong>Quantity: ${order.quantity}</strong>
26
  </div>
27
  `).join('');
28
  } else {
29
- container.innerHTML = '<p class="placeholder">Supply chain optimized. No pending orders.</p>';
30
  }
31
 
32
  } catch (error) {
@@ -37,44 +40,86 @@ async function updateState() {
37
  }
38
  }
39
 
40
- // Actions
41
- async function manualStep() {
42
- try {
43
- const btn = document.getElementById('step-btn');
44
- btn.disabled = true;
45
- btn.textContent = 'Wait...';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- // Send a default SKIP action for testing
 
 
 
 
 
 
 
 
 
 
48
  const response = await fetch(`${API_BASE}/step`, {
49
  method: 'POST',
50
  headers: { 'Content-Type': 'application/json' },
51
- body: JSON.stringify({ action_type: 'skip' })
52
  });
 
 
 
 
 
 
 
53
 
54
  await updateState();
 
 
 
55
  btn.disabled = false;
56
- btn.textContent = 'Next Day (Step)';
57
- } catch (error) {
58
- console.error('Step failed:', error);
59
  }
60
  }
61
 
62
  async function manualReset() {
 
63
  try {
64
- await fetch(`${API_BASE}/reset`, {
65
  method: 'POST',
66
  headers: { 'Content-Type': 'application/json' },
67
  body: JSON.stringify({ task: 'easy' })
68
  });
 
69
  await updateState();
70
- } catch (error) {
71
- console.error('Reset failed:', error);
72
  }
73
  }
74
 
75
- document.getElementById('step-btn').addEventListener('click', manualStep);
76
  document.getElementById('reset-btn').addEventListener('click', manualReset);
77
 
78
- // Poll every 2 seconds
79
- setInterval(updateState, 2000);
80
  updateState();
 
 
1
  const API_BASE = window.location.origin;
2
 
3
+ // State Management
4
  async function updateState() {
5
  try {
6
  const response = await fetch(`${API_BASE}/state`);
7
  if (!response.ok) throw new Error('API Unreachable');
 
8
  const data = await response.json();
9
 
 
10
  const status = document.getElementById('connection-status');
11
  status.textContent = 'ONLINE';
12
  status.className = 'status-badge status-online';
 
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
+ const inv = data.inventory;
20
+ // (Optional: add inventory display logic here)
21
+
22
  // Update Shipments
23
  const container = document.getElementById('shipments-container');
24
  if (data.orders && data.orders.length > 0) {
25
  container.innerHTML = data.orders.map(order => `
26
  <div class="shipment-item">
27
+ <span>${order.product} (Required: ${order.quantity})</span>
28
+ <small>Deadline: Day ${order.due_date}</small>
29
  </div>
30
  `).join('');
31
  } else {
32
+ container.innerHTML = '<p class="placeholder">All orders fulfilled. Environment reset recommended.</p>';
33
  }
34
 
35
  } catch (error) {
 
40
  }
41
  }
42
 
43
+ // UI Controls
44
+ const actionSelect = document.getElementById('action-select');
45
+ const orderOptions = document.getElementById('order-options');
46
+ const productionOptions = document.getElementById('production-options');
47
+
48
+ actionSelect.addEventListener('change', () => {
49
+ orderOptions.classList.add('hidden');
50
+ productionOptions.classList.add('hidden');
51
+
52
+ if (actionSelect.value === 'order_parts') orderOptions.classList.remove('hidden');
53
+ if (actionSelect.value === 'produce') productionOptions.classList.remove('hidden');
54
+ });
55
+
56
+ function log(message, type = 'system') {
57
+ const box = document.getElementById('activity-log');
58
+ const entry = document.createElement('p');
59
+ entry.className = `log-entry ${type}`;
60
+ entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
61
+ box.prepend(entry);
62
+ }
63
+
64
+ async function executeAction() {
65
+ const type = actionSelect.value;
66
+ const btn = document.getElementById('step-btn');
67
+ btn.disabled = true;
68
+ btn.textContent = 'Processing...';
69
+
70
+ let actionObj = { action_type: type };
71
 
72
+ if (type === 'order_parts') {
73
+ actionObj.part_type = document.getElementById('part-select').value;
74
+ actionObj.mode = document.getElementById('mode-select').value;
75
+ actionObj.quantity = 5; // Default for HUD
76
+ } else if (type === 'produce') {
77
+ actionObj.product = document.getElementById('product-select').value;
78
+ } else if (type === 'offset') {
79
+ actionObj.offset_amount = 100.0;
80
+ }
81
+
82
+ try {
83
  const response = await fetch(`${API_BASE}/step`, {
84
  method: 'POST',
85
  headers: { 'Content-Type': 'application/json' },
86
+ body: JSON.stringify(actionObj)
87
  });
88
+ const result = await response.json();
89
+
90
+ if (result.info && result.info.error) {
91
+ log(`ERROR: ${result.info.error}`, 'error');
92
+ } else {
93
+ log(`SUCCESS: Executed ${type}`, 'action');
94
+ }
95
 
96
  await updateState();
97
+ } catch (e) {
98
+ log(`CRITICAL: Connection lost`, 'error');
99
+ } finally {
100
  btn.disabled = false;
101
+ btn.textContent = 'Execute Action';
 
 
102
  }
103
  }
104
 
105
  async function manualReset() {
106
+ if (!confirm("Are you sure you want to reset the simulation?")) return;
107
  try {
108
+ await fetch(`${API_BASE}/reset`, {
109
  method: 'POST',
110
  headers: { 'Content-Type': 'application/json' },
111
  body: JSON.stringify({ task: 'easy' })
112
  });
113
+ log("Environment Reset to Day 0", "system");
114
  await updateState();
115
+ } catch (e) {
116
+ log("Reset Failed", "error");
117
  }
118
  }
119
 
120
+ document.getElementById('step-btn').addEventListener('click', executeAction);
121
  document.getElementById('reset-btn').addEventListener('click', manualReset);
122
 
123
+ // Start
 
124
  updateState();
125
+ setInterval(updateState, 3000);
dashboard/index.html CHANGED
@@ -9,19 +9,44 @@
9
  </head>
10
  <body>
11
  <div class="container">
12
- <header>
13
- <div class="logo">
14
- <span class="icon">🌍</span>
15
- <h1>NETZERO-NAV</h1>
16
- </div>
17
- <div class="controls">
18
- <button id="reset-btn" class="btn btn-outline">Reset</button>
19
- <button id="step-btn" class="btn btn-primary">Next Day (Step)</button>
20
- <div id="connection-status" class="status-badge">Connecting...</div>
21
- </div>
22
- </header>
23
-
24
  <main>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  <section class="metrics-grid">
26
  <div class="card metric-card">
27
  <h3>Carbon Footprint</h3>
@@ -49,11 +74,11 @@
49
  </div>
50
  </section>
51
 
52
- <section class="task-info">
53
- <div class="card">
54
- <h3>Environment Alerts</h3>
55
- <div id="news-ticker" class="ticker-box">
56
- No active disruptions detected.
57
  </div>
58
  </div>
59
  </section>
 
9
  </head>
10
  <body>
11
  <div class="container">
 
 
 
 
 
 
 
 
 
 
 
 
12
  <main>
13
+ <section class="control-panel card">
14
+ <h3>Logistics Control Center</h3>
15
+ <div class="control-inputs">
16
+ <div class="input-group">
17
+ <label>Action Type</label>
18
+ <select id="action-select">
19
+ <option value="skip">Maintain (Skip Day)</option>
20
+ <option value="order_parts">Order Parts (Logistics)</option>
21
+ <option value="produce">Manufacture (Produce)</option>
22
+ <option value="offset">Buy Carbon Offset</option>
23
+ </select>
24
+ </div>
25
+
26
+ <div id="order-options" class="sub-inputs hidden">
27
+ <select id="part-select">
28
+ <option value="chips">Microchips</option>
29
+ <option value="sensors">Sensors</option>
30
+ </select>
31
+ <select id="mode-select">
32
+ <option value="sea">SEA (Ultra-Low CO2, 10 days)</option>
33
+ <option value="rail">RAIL (Low CO2, 5 days)</option>
34
+ <option value="air">AIR (High CO2, 2 days)</option>
35
+ </select>
36
+ </div>
37
+
38
+ <div id="production-options" class="sub-inputs hidden">
39
+ <select id="product-select">
40
+ <option value="EcoPhone">EcoPhone</option>
41
+ <option value="GreenTab">GreenTab</option>
42
+ </select>
43
+ </div>
44
+
45
+ <button id="step-btn" class="btn btn-primary">Execute Action</button>
46
+ <button id="reset-btn" class="btn btn-outline">Reset Simulation</button>
47
+ </div>
48
+ </section>
49
+
50
  <section class="metrics-grid">
51
  <div class="card metric-card">
52
  <h3>Carbon Footprint</h3>
 
74
  </div>
75
  </section>
76
 
77
+ <section class="log-section">
78
+ <div class="card full-width">
79
+ <h3>Simulation Activity Log</h3>
80
+ <div id="activity-log" class="log-box">
81
+ <p class="log-entry system">[SYSTEM] Dashboard initialized. Awaiting user action...</p>
82
  </div>
83
  </div>
84
  </section>
dashboard/style.css CHANGED
@@ -167,6 +167,62 @@ header {
167
  color: var(--deep-forest);
168
  }
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  footer {
171
  text-align: center;
172
  margin-top: 4rem;
 
167
  color: var(--deep-forest);
168
  }
169
 
170
+ .control-panel {
171
+ margin-bottom: 2rem;
172
+ background: #F9F7F2;
173
+ }
174
+
175
+ .control-inputs {
176
+ display: flex;
177
+ flex-wrap: wrap;
178
+ gap: 1.5rem;
179
+ align-items: flex-end;
180
+ }
181
+
182
+ .input-group {
183
+ display: flex;
184
+ flex-direction: column;
185
+ gap: 0.5rem;
186
+ }
187
+
188
+ label {
189
+ font-size: 0.75rem;
190
+ font-weight: 600;
191
+ color: var(--text-muted);
192
+ }
193
+
194
+ select {
195
+ padding: 0.8rem;
196
+ border-radius: 8px;
197
+ border: 1px solid var(--border-tan);
198
+ background: white;
199
+ font-family: inherit;
200
+ min-width: 200px;
201
+ }
202
+
203
+ .hidden { display: none !important; }
204
+
205
+ .sub-inputs {
206
+ display: flex;
207
+ gap: 1rem;
208
+ }
209
+
210
+ .log-box {
211
+ height: 150px;
212
+ overflow-y: auto;
213
+ background: #1A1A1A;
214
+ color: #00FF41;
215
+ font-family: 'Courier New', Courier, monospace;
216
+ padding: 1rem;
217
+ border-radius: 8px;
218
+ font-size: 0.85rem;
219
+ }
220
+
221
+ .log-entry { margin-bottom: 0.4rem; }
222
+ .log-entry.system { color: #AAAAAA; }
223
+ .log-entry.action { color: #00FF41; }
224
+ .log-entry.error { color: #FF4136; }
225
+
226
  footer {
227
  text-align: center;
228
  margin-top: 4rem;