Aryanshh commited on
Commit
87e9783
·
1 Parent(s): 51cbaeb

feat: Enhanced Command History structural cards and metric telemetry

Browse files
Files changed (4) hide show
  1. dashboard/app.js +49 -11
  2. dashboard/index.html +9 -10
  3. dashboard/style.css +51 -4
  4. netzero_nav/env.py +18 -11
dashboard/app.js CHANGED
@@ -137,11 +137,18 @@ function spawnFeedback(text, x, y) {
137
  setTimeout(() => el.remove(), 1000);
138
  }
139
 
140
- function log(message, type = 'system') {
141
  const box = document.getElementById('activity-log');
142
- const entry = document.createElement('p');
143
- entry.className = `log-entry ${type}`;
144
- entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
 
 
 
 
 
 
 
145
  box.prepend(entry);
146
  }
147
 
@@ -179,19 +186,50 @@ window.execute = async function(type, event, shipment_id = null) {
179
  const result = await response.json();
180
 
181
  if (result.info && result.info.error) {
182
- log(`ERROR: ${result.info.error}`, 'error');
183
  spawnFeedback(`❌ ${result.info.error}`, event.clientX, event.clientY);
184
  } else {
185
- if (type === 'order_parts') spawnFeedback(`+${actionObj.quantity} ordered`, event.clientX, event.clientY);
186
- else if (type === 'produce') spawnFeedback(`Initiated x${actionObj.quantity} run`, event.clientX, event.clientY);
187
- else if (type === 'offset') spawnFeedback(`Purchased ${actionObj.offset_amount} offsets`, event.clientX, event.clientY);
188
- else if (type === 'cancel') log(`SUCCESS: Canceled Shipment`, 'action');
189
- else log(`SUCCESS: ${type === 'skip' ? 'Skipped Day' : 'Executed ' + type}`, 'action');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  }
191
 
192
  await updateState();
193
  } catch (e) {
194
- log(`CRITICAL: Connection lost`, 'error');
195
  } finally {
196
  setTimeout(() => {
197
  document.querySelector('main').classList.remove('transitioning');
 
137
  setTimeout(() => el.remove(), 1000);
138
  }
139
 
140
+ function log(title, meta = "SYSTEM", subtext = '', type = 'system') {
141
  const box = document.getElementById('activity-log');
142
+ const entry = document.createElement('div');
143
+ entry.className = `history-item ${type}`;
144
+
145
+ let html = `
146
+ <span class="history-meta">[Day ${currentDay}] ${meta}</span>
147
+ <span class="history-text">${title}</span>
148
+ `;
149
+ if (subtext) html += `<span class="history-sub">${subtext}</span>`;
150
+
151
+ entry.innerHTML = html;
152
  box.prepend(entry);
153
  }
154
 
 
186
  const result = await response.json();
187
 
188
  if (result.info && result.info.error) {
189
+ log(result.info.error, "COMMAND REJECTED", "", "alert");
190
  spawnFeedback(`❌ ${result.info.error}`, event.clientX, event.clientY);
191
  } else {
192
+ if (type === 'order_parts') {
193
+ const s = SPECS[actionObj.mode];
194
+ const cost = 10 * actionObj.quantity * s.cost;
195
+ log(`Ordered ${actionObj.quantity}x ${actionObj.part_type}`, `LOGISTICS | ${actionObj.mode.toUpperCase()}`, `-$${cost.toFixed(0)}`, 'action');
196
+ spawnFeedback(`+${actionObj.quantity} ordered`, event.clientX, event.clientY);
197
+ }
198
+ else if (type === 'produce') {
199
+ log(`Started run for ${actionObj.quantity}x ${actionObj.product}`, `MANUFACTURING`, "", 'action');
200
+ spawnFeedback(`Initiated x${actionObj.quantity} run`, event.clientX, event.clientY);
201
+ }
202
+ else if (type === 'offset') {
203
+ const cost = actionObj.offset_amount * 2;
204
+ log(`Bought ${actionObj.offset_amount} Carbon Offsets`, `ESG STRATEGY`, `-$${cost.toFixed(0)}`, 'action');
205
+ spawnFeedback(`Purchased ${actionObj.offset_amount} offsets`, event.clientX, event.clientY);
206
+ }
207
+ else if (type === 'cancel') {
208
+ log(`Canceled Shipment`, `LOGISTICS`, "", 'action');
209
+ }
210
+ else if (type === 'skip') {
211
+ log(`Advanced to next day`, `SIMULATION`, "", 'system');
212
+ }
213
+ }
214
+
215
+ // Process Arrivals from backend
216
+ if (result.info && result.info.arrivals) {
217
+ result.info.arrivals.forEach(arrival => {
218
+ log(`${arrival} received`, `INVENTORY ARRIVAL`, "+ Raw Materials", 'arrival');
219
+ });
220
+ }
221
+
222
+ if (result.info && result.info.news) {
223
+ const box = document.getElementById('news-alert');
224
+ document.getElementById('news-text').textContent = result.info.news;
225
+ box.classList.remove('hidden');
226
+ setTimeout(() => box.classList.add('hidden'), 5000);
227
+ log(result.info.news, "GLOBAL NEWS", "", 'alert');
228
  }
229
 
230
  await updateState();
231
  } catch (e) {
232
+ log("Connection lost to simulation server", "CRITICAL ERROR", "", "alert");
233
  } finally {
234
  setTimeout(() => {
235
  document.querySelector('main').classList.remove('transitioning');
dashboard/index.html CHANGED
@@ -122,7 +122,15 @@
122
  <div class="card full-width h-100">
123
  <h3>Your Orders</h3>
124
  <div id="cart-container" class="cart-list">
125
- <p class="placeholder">No active shipments in transit.</p>
 
 
 
 
 
 
 
 
126
  </div>
127
  </div>
128
  </section>
@@ -169,15 +177,6 @@
169
  </div>
170
  </section>
171
 
172
- <section class="log-section">
173
- <div class="card full-width">
174
- <h3>Simulation Activity Log</h3>
175
- <div id="activity-log" class="log-box">
176
- <p class="log-entry system">[SYSTEM] Dashboard initialized. Awaiting user action...</p>
177
- </div>
178
- </div>
179
- </section>
180
-
181
  <section class="guide-section">
182
  <div class="card full-width mission-card">
183
  <h3>Mission Briefing (Operator's Manual)</h3>
 
122
  <div class="card full-width h-100">
123
  <h3>Your Orders</h3>
124
  <div id="cart-container" class="cart-list">
125
+ </div>
126
+ </div>
127
+ </section>
128
+
129
+ <section class="history-section" style="margin-top: 2rem;">
130
+ <div class="card full-width" style="height: auto; max-height: 400px; display: flex; flex-direction: column;">
131
+ <h3>Command History</h3>
132
+ <div class="history-list" id="activity-log" style="overflow-y: auto; display: flex; flex-direction: column; gap: 0.8rem; padding-right: 0.5rem; flex: 1;">
133
+ <!-- items arrive here -->
134
  </div>
135
  </div>
136
  </section>
 
177
  </div>
178
  </section>
179
 
 
 
 
 
 
 
 
 
 
180
  <section class="guide-section">
181
  <div class="card full-width mission-card">
182
  <h3>Mission Briefing (Operator's Manual)</h3>
dashboard/style.css CHANGED
@@ -357,10 +357,57 @@ select:focus, .qty-select:focus {
357
  font-size: 0.85rem;
358
  }
359
 
360
- .log-entry { margin-bottom: 0.4rem; }
361
- .log-entry.system { color: #AAAAAA; }
362
- .log-entry.action { color: #00FF41; }
363
- .log-entry.error { color: #FF4136; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
 
365
  .inventory-grid {
366
  display: flex;
 
357
  font-size: 0.85rem;
358
  }
359
 
360
+ .log-box {
361
+ margin-top: 1rem;
362
+ background: transparent;
363
+ border-radius: 12px;
364
+ }
365
+
366
+ .history-item {
367
+ background: white;
368
+ padding: 0.8rem 1rem;
369
+ border-radius: 10px;
370
+ border: 1px solid var(--border-tan);
371
+ display: flex;
372
+ flex-direction: column;
373
+ gap: 0.3rem;
374
+ box-shadow: 0 2px 8px rgba(0,0,0,0.02);
375
+ }
376
+
377
+ .history-meta {
378
+ font-size: 0.75rem;
379
+ color: var(--text-muted);
380
+ }
381
+
382
+ .history-text {
383
+ font-size: 0.85rem;
384
+ color: var(--text-main);
385
+ font-weight: 500;
386
+ }
387
+
388
+ .history-sub {
389
+ font-size: 0.75rem;
390
+ color: var(--primary-green);
391
+ font-weight: 600;
392
+ }
393
+
394
+ .history-item.alert { border-left: 4px solid var(--primary-red); }
395
+ .history-item.action { border-left: 4px solid var(--sage-green); }
396
+ .history-item.system { border-left: 4px solid #DFDBDD; }
397
+ .history-item.arrival { border-left: 4px solid var(--primary-green); }
398
+
399
+ /* Custom Scrollbar for history */
400
+ .history-list::-webkit-scrollbar {
401
+ width: 6px;
402
+ }
403
+ .history-list::-webkit-scrollbar-track {
404
+ background: rgba(0,0,0,0.02);
405
+ border-radius: 10px;
406
+ }
407
+ .history-list::-webkit-scrollbar-thumb {
408
+ background: rgba(0,0,0,0.1);
409
+ border-radius: 10px;
410
+ }
411
 
412
  .inventory-grid {
413
  display: flex;
netzero_nav/env.py CHANGED
@@ -54,19 +54,26 @@ class AtlasEcoEnv:
54
  news = None
55
  info = {}
56
 
57
- # Day Advancement Loop
58
  if action.action_type == ActionType.SKIP:
59
  self.step_count += 1
60
- if self.step_count < self.sea_blocked_until:
61
- news = "EMERGENCY: Suez Canal blocked! Sea routes offline."
62
-
63
- # 2. Advance Shipments
64
- for ship in self.active_shipments:
65
- ship.eta -= 1
66
- if ship.eta <= 0:
67
- self._receive_shipment(ship)
68
-
69
- self.active_shipments = [s for s in self.active_shipments if s.eta > 0]
 
 
 
 
 
 
 
70
 
71
  # 3. Check Order Deadlines
72
  for order in self.pending_orders:
 
54
  news = None
55
  info = {}
56
 
57
+ # Day Advancement
58
  if action.action_type == ActionType.SKIP:
59
  self.step_count += 1
60
+ if self.sea_blocked_until > 0 and self.step_count > self.sea_blocked_until:
61
+ info["news"] = "Suez route is clear again."
62
+ self.sea_blocked_until = 0
63
+
64
+ # Process active shipments
65
+ arrivals = []
66
+ next_shipments = []
67
+ for s in self.active_shipments:
68
+ s.eta -= 1
69
+ if s.eta <= 0:
70
+ arrivals.append(f"{s.quantity}x {s.part.value}")
71
+ self._receive_shipment(s)
72
+ else:
73
+ next_shipments.append(s)
74
+ self.active_shipments = next_shipments
75
+ if arrivals:
76
+ info["arrivals"] = arrivals
77
 
78
  # 3. Check Order Deadlines
79
  for order in self.pending_orders: