Aryanshh commited on
Commit
b9f25ba
·
1 Parent(s): 3a65d8a

feat: Final layout overhaul with left-pinned console and inventory forecasting

Browse files
Files changed (2) hide show
  1. dashboard/app.js +56 -17
  2. dashboard/index.html +58 -62
dashboard/app.js CHANGED
@@ -7,7 +7,15 @@ const SPECS = {
7
  rail: { cost: 2.5, carbon: 0.5 }
8
  };
9
 
10
- // Tutorial Content
 
 
 
 
 
 
 
 
11
  const TUTORIAL_STEPS = [
12
  {
13
  title: "Welcome to NetZero-Nav",
@@ -36,16 +44,6 @@ const TUTORIAL_STEPS = [
36
  }
37
  ];
38
 
39
- let currentTutStep = 0;
40
-
41
- // Global State for UI calculation
42
- let latestInventory = { chips: 0, sensors: 0 };
43
-
44
- const RECIPES = {
45
- "EcoPhone": { chips: 1, sensors: 2 },
46
- "GreenTab": { chips: 2, sensors: 1 }
47
- };
48
-
49
  // State Management
50
  async function updateState() {
51
  try {
@@ -53,11 +51,43 @@ async function updateState() {
53
  if (!response.ok) throw new Error('API Unreachable');
54
  const data = await response.json();
55
  latestInventory = data.inventory;
56
- ...
 
 
 
 
 
 
57
  document.getElementById('chips-count').textContent = data.inventory.chips;
58
  document.getElementById('sensors-count').textContent = data.inventory.sensors;
 
59
  updateProducePreview();
60
- ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
  }
63
 
@@ -65,6 +95,7 @@ function updateProducePreview() {
65
  const product = document.getElementById('product-select').value;
66
  const req = RECIPES[product];
67
  const preview = document.getElementById('req-preview');
 
68
 
69
  const chipClass = latestInventory.chips >= req.chips ? "sufficient" : "shortage";
70
  const sensorClass = latestInventory.sensors >= req.sensors ? "sufficient" : "shortage";
@@ -81,8 +112,6 @@ function updateProducePreview() {
81
  `;
82
  }
83
 
84
- document.getElementById('product-select').addEventListener('change', updateProducePreview);
85
-
86
  // UI Controls
87
  const modeSelect = document.getElementById('mode-select');
88
 
@@ -106,8 +135,18 @@ function log(message, type = 'system') {
106
  box.prepend(entry);
107
  }
108
 
109
- // Global Execute Function (exposed to onclick in HTML)
110
  window.execute = async function(type) {
 
 
 
 
 
 
 
 
 
 
111
  document.querySelector('main').classList.add('transitioning');
112
 
113
  let actionObj = { action_type: type };
@@ -156,7 +195,6 @@ function showStep(step) {
156
  document.getElementById('tut-prev').classList.toggle('hidden', step === 0);
157
  document.getElementById('tut-next').textContent = step === TUTORIAL_STEPS.length - 1 ? "Start Simulation" : "Next";
158
 
159
- // Static Centered Position
160
  document.getElementById('tutorial-modal').style.alignItems = 'center';
161
  document.getElementById('tutorial-modal').style.paddingTop = '0';
162
  }
@@ -216,6 +254,7 @@ async function triggerSuezJam() {
216
  document.getElementById('skip-btn').addEventListener('click', () => window.execute('skip'));
217
  document.getElementById('trigger-btn').addEventListener('click', triggerSuezJam);
218
  document.getElementById('reset-btn').addEventListener('click', manualReset);
 
219
 
220
  // Start
221
  updateState();
 
7
  rail: { cost: 2.5, carbon: 0.5 }
8
  };
9
 
10
+ const RECIPES = {
11
+ "EcoPhone": { chips: 1, sensors: 2 },
12
+ "GreenTab": { chips: 2, sensors: 1 }
13
+ };
14
+
15
+ // Global State for UI calculation
16
+ let latestInventory = { chips: 0, sensors: 0 };
17
+ let currentTutStep = 0;
18
+
19
  const TUTORIAL_STEPS = [
20
  {
21
  title: "Welcome to NetZero-Nav",
 
44
  }
45
  ];
46
 
 
 
 
 
 
 
 
 
 
 
47
  // State Management
48
  async function updateState() {
49
  try {
 
51
  if (!response.ok) throw new Error('API Unreachable');
52
  const data = await response.json();
53
  latestInventory = data.inventory;
54
+
55
+ const status = document.getElementById('connection-status');
56
+ status.textContent = 'ONLINE';
57
+ status.className = 'status-badge status-online';
58
+
59
+ document.getElementById('carbon-value').textContent = data.carbon.toFixed(0);
60
+ document.getElementById('cash-value').textContent = data.cash.toLocaleString();
61
  document.getElementById('chips-count').textContent = data.inventory.chips;
62
  document.getElementById('sensors-count').textContent = data.inventory.sensors;
63
+
64
  updateProducePreview();
65
+
66
+ const newsAlert = document.getElementById('news-alert');
67
+ if (data.news) {
68
+ document.getElementById('news-text').textContent = data.news;
69
+ newsAlert.classList.remove('hidden');
70
+ } else {
71
+ newsAlert.classList.add('hidden');
72
+ }
73
+
74
+ const container = document.getElementById('shipments-container');
75
+ if (data.orders && data.orders.length > 0) {
76
+ container.innerHTML = data.orders.map(order => `
77
+ <div class="shipment-item">
78
+ <span>${order.product} (Required: ${order.quantity})</span>
79
+ <small>Deadline: Day ${order.due_date}</small>
80
+ </div>
81
+ `).join('');
82
+ } else {
83
+ container.innerHTML = '<p class="placeholder">All orders fulfilled. Environment reset recommended.</p>';
84
+ }
85
+
86
+ } catch (error) {
87
+ console.error('Update failed:', error);
88
+ const status = document.getElementById('connection-status');
89
+ status.textContent = 'RECONNECTING...';
90
+ status.className = 'status-badge';
91
  }
92
  }
93
 
 
95
  const product = document.getElementById('product-select').value;
96
  const req = RECIPES[product];
97
  const preview = document.getElementById('req-preview');
98
+ if (!preview) return;
99
 
100
  const chipClass = latestInventory.chips >= req.chips ? "sufficient" : "shortage";
101
  const sensorClass = latestInventory.sensors >= req.sensors ? "sufficient" : "shortage";
 
112
  `;
113
  }
114
 
 
 
115
  // UI Controls
116
  const modeSelect = document.getElementById('mode-select');
117
 
 
135
  box.prepend(entry);
136
  }
137
 
138
+ // Global Execute Function
139
  window.execute = async function(type) {
140
+ // Inventory Guard for Production
141
+ if (type === 'produce') {
142
+ const product = document.getElementById('product-select').value;
143
+ const req = RECIPES[product];
144
+ if (latestInventory.chips < req.chips || latestInventory.sensors < req.sensors) {
145
+ log(`ERROR: Incomplete Inventory! Cannot produce ${product}.`, 'error');
146
+ return;
147
+ }
148
+ }
149
+
150
  document.querySelector('main').classList.add('transitioning');
151
 
152
  let actionObj = { action_type: type };
 
195
  document.getElementById('tut-prev').classList.toggle('hidden', step === 0);
196
  document.getElementById('tut-next').textContent = step === TUTORIAL_STEPS.length - 1 ? "Start Simulation" : "Next";
197
 
 
198
  document.getElementById('tutorial-modal').style.alignItems = 'center';
199
  document.getElementById('tutorial-modal').style.paddingTop = '0';
200
  }
 
254
  document.getElementById('skip-btn').addEventListener('click', () => window.execute('skip'));
255
  document.getElementById('trigger-btn').addEventListener('click', triggerSuezJam);
256
  document.getElementById('reset-btn').addEventListener('click', manualReset);
257
+ document.getElementById('product-select').addEventListener('change', updateProducePreview);
258
 
259
  // Start
260
  updateState();
dashboard/index.html CHANGED
@@ -33,43 +33,10 @@
33
  </div>
34
 
35
  <main>
36
- <section class="metrics-grid">
37
- <div class="card metric-card">
38
- <h3>Carbon Footprint</h3>
39
- <div class="gauge-container">
40
- <div id="carbon-gauge" class="gauge">
41
- <span id="carbon-value">0</span><small>kg</small>
42
- </div>
43
- </div>
44
- <p class="limit">Limit: <span id="carbon-limit">1000</span>kg</p>
45
- </div>
46
-
47
- <div class="card metric-card">
48
- <h3>Capital Balance</h3>
49
- <div class="value-large">$<span id="cash-value">0</span></div>
50
- <div class="trend" id="cash-trend">Fulfillment: <span id="orders-done">0</span></div>
51
- </div>
52
-
53
- <div class="card metric-card">
54
- <h3>Raw Inventory</h3>
55
- <div class="inventory-grid">
56
- <div class="inv-item">
57
- <span class="label">Chips</span>
58
- <span id="chips-count" class="count">0</span>
59
- </div>
60
- <div class="inv-item">
61
- <span class="label">Sensors</span>
62
- <span id="sensors-count" class="count">0</span>
63
- </div>
64
- </div>
65
- </div>
66
- </section>
67
-
68
- <section class="control-panel card">
69
  <h3><span class="i-icon">ℹ️</span> Command Console</h3>
70
 
71
  <div class="operation-sections">
72
- <!-- Section 1: Logistics -->
73
  <div id="section-order" class="op-section">
74
  <h4>📦 Supply Logistics</h4>
75
  <div class="op-row">
@@ -78,9 +45,9 @@
78
  <option value="sensors">Order Sensors</option>
79
  </select>
80
  <select id="mode-select">
81
- <option value="sea">SEA ($50, 10d, 0.5kg)</option>
82
- <option value="rail">RAIL ($125, 5d, 2.5kg)</option>
83
- <option value="air">AIR ($250, 2d, 10kg)</option>
84
  </select>
85
  <button onclick="execute('order_parts')" class="btn btn-primary small">Order</button>
86
  </div>
@@ -88,7 +55,6 @@
88
 
89
  <hr class="divider">
90
 
91
- <!-- Section 2: Manufacturing -->
92
  <div id="section-produce" class="op-section">
93
  <h4>🏭 Manufacturing</h4>
94
  <div class="op-row">
@@ -98,11 +64,11 @@
98
  </select>
99
  <button onclick="execute('produce')" class="btn btn-primary small">Start Run</button>
100
  </div>
 
101
  </div>
102
 
103
  <hr class="divider">
104
 
105
- <!-- Section 3: ESG / Offsetting -->
106
  <div id="section-offset" class="op-section">
107
  <h4>🌳 ESG Strategy</h4>
108
  <div class="op-row">
@@ -113,7 +79,7 @@
113
  </div>
114
 
115
  <div id="impact-preview" class="impact-preview">
116
- <span class="preview-label">Live Estimate:</span>
117
  <span id="preview-cost" class="val">$0</span>
118
  <span class="sep">|</span>
119
  <span id="preview-carbon" class="val">0kg CO2</span>
@@ -131,23 +97,57 @@
131
  </div>
132
  </section>
133
 
134
- <section class="details-section">
135
- <div class="card full-width">
136
- <h3>Active Logistics Stream</h3>
137
- <div id="shipments-container" class="shipment-list">
138
- <p class="placeholder">Awaiting shipment data...</p>
 
 
 
 
 
139
  </div>
140
- </div>
141
- </section>
142
 
143
- <section class="log-section">
144
- <div class="card full-width">
145
- <h3>Simulation Activity Log</h3>
146
- <div id="activity-log" class="log-box">
147
- <p class="log-entry system">[SYSTEM] Dashboard initialized. Awaiting user action...</p>
148
  </div>
149
- </div>
150
- </section>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
  <section class="guide-section">
153
  <div class="card full-width mission-card">
@@ -156,20 +156,16 @@
156
  <div class="guide-grid">
157
  <div class="guide-item">
158
  <h4>🎯 Objective</h4>
159
- <p>Fulfill "EcoPhone" and "GreenTab" orders by managing a global supply chain. You must keep <strong>Carbon Footprint</strong> below the quota while maximizing <strong>Capital Balance</strong>.</p>
160
  </div>
161
  <div class="guide-item">
162
  <h4>🚢 Logistics Trade-offs</h4>
163
  <ul class="guide-list">
164
- <li><strong>SEA:</strong> Cheapest ($50), cleanest (0.5kg CO2), but very slow (10 days).</li>
165
- <li><strong>RAIL:</strong> Balanced cost ($125), low CO2 (2.5kg), medium speed (5 days).</li>
166
- <li><strong>AIR:</strong> Expensive ($250), high CO2 (10kg), but ultra-fast (2 days).</li>
167
  </ul>
168
  </div>
169
- <div class="guide-item">
170
- <h4>☢️ Crisis Handling</h4>
171
- <p>Clicking <strong>Trigger Suez Jam</strong> simulates a real-world blockade. Sea routes become unavailable for 7 days, forcing you to pivot to expensive Rail/Air routes to meet your deadlines.</p>
172
- </div>
173
  </div>
174
  </div>
175
  </div>
 
33
  </div>
34
 
35
  <main>
36
+ <section id="command-console" class="control-panel card">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  <h3><span class="i-icon">ℹ️</span> Command Console</h3>
38
 
39
  <div class="operation-sections">
 
40
  <div id="section-order" class="op-section">
41
  <h4>📦 Supply Logistics</h4>
42
  <div class="op-row">
 
45
  <option value="sensors">Order Sensors</option>
46
  </select>
47
  <select id="mode-select">
48
+ <option value="sea">SEA ($50, 0.5kg)</option>
49
+ <option value="rail">RAIL ($125, 2.5kg)</option>
50
+ <option value="air">AIR ($250, 10kg)</option>
51
  </select>
52
  <button onclick="execute('order_parts')" class="btn btn-primary small">Order</button>
53
  </div>
 
55
 
56
  <hr class="divider">
57
 
 
58
  <div id="section-produce" class="op-section">
59
  <h4>🏭 Manufacturing</h4>
60
  <div class="op-row">
 
64
  </select>
65
  <button onclick="execute('produce')" class="btn btn-primary small">Start Run</button>
66
  </div>
67
+ <div id="req-preview" class="req-preview"></div>
68
  </div>
69
 
70
  <hr class="divider">
71
 
 
72
  <div id="section-offset" class="op-section">
73
  <h4>🌳 ESG Strategy</h4>
74
  <div class="op-row">
 
79
  </div>
80
 
81
  <div id="impact-preview" class="impact-preview">
82
+ <span class="preview-label">Estimated Impact:</span>
83
  <span id="preview-cost" class="val">$0</span>
84
  <span class="sep">|</span>
85
  <span id="preview-carbon" class="val">0kg CO2</span>
 
97
  </div>
98
  </section>
99
 
100
+ <div class="metrics-column">
101
+ <section class="metrics-grid">
102
+ <div class="card metric-card">
103
+ <h3>Carbon Footprint</h3>
104
+ <div class="gauge-container">
105
+ <div id="carbon-gauge" class="gauge">
106
+ <span id="carbon-value">0</span><small>kg</small>
107
+ </div>
108
+ </div>
109
+ <p class="limit">Limit: <span id="carbon-limit">1000</span>kg</p>
110
  </div>
 
 
111
 
112
+ <div class="card metric-card">
113
+ <h3>Capital Balance</h3>
114
+ <div class="value-large">$<span id="cash-value">0</span></div>
115
+ <div class="trend" id="cash-trend">Fulfillment Active</div>
 
116
  </div>
117
+
118
+ <div class="card metric-card">
119
+ <h3>Raw Inventory</h3>
120
+ <div class="inventory-grid">
121
+ <div class="inv-item">
122
+ <span class="label">Chips</span>
123
+ <span id="chips-count" class="count">0</span>
124
+ </div>
125
+ <div class="inv-item">
126
+ <span class="label">Sensors</span>
127
+ <span id="sensors-count" class="count">0</span>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ </section>
132
+
133
+ <section class="details-section">
134
+ <div class="card full-width">
135
+ <h3>Active Logistics Stream</h3>
136
+ <div id="shipments-container" class="shipment-list">
137
+ <p class="placeholder">Awaiting shipment data...</p>
138
+ </div>
139
+ </div>
140
+ </section>
141
+
142
+ <section class="log-section">
143
+ <div class="card full-width">
144
+ <h3>Simulation Activity Log</h3>
145
+ <div id="activity-log" class="log-box">
146
+ <p class="log-entry system">[SYSTEM] Dashboard initialized. Awaiting user action...</p>
147
+ </div>
148
+ </div>
149
+ </section>
150
+ </div>
151
 
152
  <section class="guide-section">
153
  <div class="card full-width mission-card">
 
156
  <div class="guide-grid">
157
  <div class="guide-item">
158
  <h4>🎯 Objective</h4>
159
+ <p>Fulfill "EcoPhone" and "GreenTab" orders by managing a global supply chain. Keep <strong>Carbon Footprint</strong> below limits while maximizing <strong>Capital Balance</strong>.</p>
160
  </div>
161
  <div class="guide-item">
162
  <h4>🚢 Logistics Trade-offs</h4>
163
  <ul class="guide-list">
164
+ <li><strong>SEA:</strong> Cheapest ($50), cleanest (0.5kg CO2).</li>
165
+ <li><strong>RAIL:</strong> Balanced ($125), low CO2 (2.5kg).</li>
166
+ <li><strong>AIR:</strong> Expensive ($250), high CO2 (10kg).</li>
167
  </ul>
168
  </div>
 
 
 
 
169
  </div>
170
  </div>
171
  </div>