Ultronprime commited on
Commit
8ca5fda
·
verified ·
1 Parent(s): 0e58a60

Update ui.js

Browse files
Files changed (1) hide show
  1. ui.js +115 -12
ui.js CHANGED
@@ -2,6 +2,7 @@
2
 
3
  import { appState } from './state.js';
4
  import { attachAllListeners } from './events.js';
 
5
 
6
  // Chart instances to prevent re-creation
7
  let productionChart = null;
@@ -16,14 +17,25 @@ export function refreshUI() {
16
  renderProductionLog();
17
  renderAnalytics();
18
  renderReorderList();
19
- // No longer need renderDailyProductionSummary as charts are better
20
- attachAllListeners();
21
  }
22
 
23
- // ... showToast function remains the same ...
24
- export function showToast(message, type = 'info') { /* ... same as before ... */ }
 
 
 
 
 
 
 
 
 
 
 
25
 
26
- // --- NEW Dashboard Rendering Functions ---
27
 
28
  function renderKpiCards() {
29
  const kpiRow = document.getElementById('kpi-row');
@@ -157,12 +169,103 @@ function renderInventoryStatusChart() {
157
  }
158
  }
159
 
 
 
 
 
 
 
 
160
 
161
- // --- Internal Rendering Functions (Unchanged or minor tweaks) ---
 
 
162
 
163
- function renderProductInputs() { /* ... same as before ... */ }
164
- function renderInventory() { /* ... same as before ... */ }
165
- function renderProductionLog() { /* ... same as before ... */ }
166
- function renderAnalytics() { /* ... same as before ... */ }
167
- function renderReorderList() { /* ... same as before ... */ }
168
- // We are removing renderDailyProductionSummary as the charts provide better visualization
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  import { appState } from './state.js';
4
  import { attachAllListeners } from './events.js';
5
+ import { getMonthlyProductionSummary, getMonthlyMaterialUsage } from './reportService.js';
6
 
7
  // Chart instances to prevent re-creation
8
  let productionChart = null;
 
17
  renderProductionLog();
18
  renderAnalytics();
19
  renderReorderList();
20
+ renderModals();
21
+ attachAllListeners(); // Re-attach listeners to new/updated elements
22
  }
23
 
24
+ export function showToast(message, type = 'info') {
25
+ const container = document.getElementById('toast-container');
26
+ const toast = document.createElement('div');
27
+ const icon = type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-times-circle' : 'fa-info-circle';
28
+ toast.className = `toast toast-${type}`;
29
+ toast.innerHTML = `<i class="fas ${icon}"></i><span>${message}</span>`;
30
+ container.appendChild(toast);
31
+ setTimeout(() => toast.classList.add('show'), 10);
32
+ setTimeout(() => {
33
+ toast.classList.remove('show');
34
+ toast.addEventListener('transitionend', () => toast.remove());
35
+ }, 3000);
36
+ }
37
 
38
+ // --- Dashboard Rendering Functions ---
39
 
40
  function renderKpiCards() {
41
  const kpiRow = document.getElementById('kpi-row');
 
169
  }
170
  }
171
 
172
+ function renderModals() {
173
+ const resetModalContainer = document.getElementById('reset-modal');
174
+ resetModalContainer.innerHTML = `<div class="bg-white rounded-lg p-6 max-w-sm w-full mx-4 shadow-xl"><h3 class="text-lg font-semibold mb-2">Confirm Reset</h3><p class="text-gray-600 mb-4">Are you sure you want to reset all inventory data? This action cannot be undone.</p><div class="flex justify-end space-x-2"><button id="cancel-reset-btn" class="px-4 py-2 text-sm bg-gray-200 rounded hover:bg-gray-300">Cancel</button><button id="confirm-reset-btn" class="px-4 py-2 text-sm bg-red-500 text-white rounded hover:bg-red-600">Reset Data</button></div></div>`;
175
+
176
+ const reportsModalContainer = document.getElementById('reports-modal');
177
+ reportsModalContainer.innerHTML = `<div class="bg-white rounded-lg p-6 max-w-2xl w-full mx-4 shadow-xl"><div class="flex justify-between items-center mb-4"><h3 class="text-lg font-semibold">Generate Report</h3><button id="close-reports-modal-btn" class="text-gray-500 hover:text-gray-800">×</button></div><div class="flex gap-4 mb-4"><button id="report-prod-summary" class="flex-1 px-4 py-2 text-sm bg-blue-500 text-white rounded hover:bg-blue-600">Monthly Production</button><button id="report-mat-usage" class="flex-1 px-4 py-2 text-sm bg-green-500 text-white rounded hover:bg-green-600">Monthly Material Usage</button></div><div id="report-content" class="p-4 border rounded bg-gray-50 min-h-[300px]">Select a report to view data.</div></div>`;
178
+ }
179
 
180
+ export function renderReport(type) {
181
+ const content = document.getElementById('report-content');
182
+ let data, title, headers, rows;
183
 
184
+ if (type === 'production') {
185
+ data = getMonthlyProductionSummary();
186
+ title = 'Monthly Production Summary (Last 30 Days)';
187
+ headers = ['Product/Assembly', 'Total Units Produced'];
188
+ rows = Object.entries(data).map(([name, qty]) => `<tr><td class="border px-4 py-2">${name}</td><td class="border px-4 py-2 text-right">${qty}</td></tr>`).join('');
189
+ } else {
190
+ data = getMonthlyMaterialUsage();
191
+ title = 'Monthly Material Usage (Last 30 Days)';
192
+ headers = ['Material', 'Total Quantity Consumed'];
193
+ rows = Object.entries(data).map(([name, qty]) => {
194
+ const material = appState.materials.find(m => m.name === name);
195
+ return `<tr><td class="border px-4 py-2">${name}</td><td class="border px-4 py-2 text-right">${qty} ${material.unit}</td></tr>`;
196
+ }).join('');
197
+ }
198
+
199
+ if (Object.keys(data).length === 0) {
200
+ content.innerHTML = `<p class="text-center text-gray-500 pt-12">No data available for this period.</p>`;
201
+ return;
202
+ }
203
+
204
+ content.innerHTML = `<h4 class="font-bold mb-2">${title}</h4><table class="table-auto w-full text-sm"><thead><tr><th class="border px-4 py-2 text-left">${headers[0]}</th><th class="border px-4 py-2 text-right">${headers[1]}</th></tr></thead><tbody>${rows}</tbody></table>`;
205
+ }
206
+
207
+ // --- Internal Rendering Functions ---
208
+
209
+ function renderProductInputs() {
210
+ const container = document.getElementById('product-cards');
211
+ container.innerHTML = '';
212
+ for (const productName in appState.productRecipes) {
213
+ const cardHTML = `<div class="product-card bg-white rounded-lg shadow p-4 border border-gray-100" data-product-name="${productName}"><h3 class="font-medium text-gray-700 mb-3">${productName}</h3><div class="flex items-center space-x-3"><input type="number" min="0" class="input-number w-20 px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500" value="0"><button class="update-btn px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors">Produce</button></div></div>`;
214
+ container.insertAdjacentHTML('beforeend', cardHTML);
215
+ }
216
+ }
217
+
218
+ function renderInventory() {
219
+ const container = document.getElementById('material-cards');
220
+ container.innerHTML = '';
221
+ appState.materials.forEach(material => {
222
+ const stockPercentage = (material.currentStock / material.maxStock) * 100;
223
+ let statusClass = 'status-ok', progressClass = 'progress-ok';
224
+ if (stockPercentage <= 50 && stockPercentage > 20) { statusClass = 'status-warning'; progressClass = 'progress-warning'; }
225
+ else if (stockPercentage <= 20) { statusClass = 'status-critical'; progressClass = 'progress-critical'; }
226
+ const cardHTML = `<div class="material-card bg-white rounded-lg shadow p-4 border-l-4 ${statusClass}" data-material-name="${material.name}"><div class="flex justify-between items-start mb-1"><h3 class="font-medium text-gray-700">${material.name}</h3><div class="text-xs text-gray-500 flex items-center gap-3"><span>MAX:</span><span class="font-semibold max-stock-value">${material.maxStock}</span><i class="fas fa-plus-circle icon-btn restock-icon" title="Restock"></i><i class="fas fa-pencil-alt icon-btn edit-max-stock" title="Edit Max Stock"></i></div></div><div class="flex justify-between items-baseline mb-2"><div class="text-2xl font-bold current-stock">${material.currentStock}</div><span class="text-sm text-gray-500 unit">${material.unit}</span></div><div class="w-full bg-gray-200 rounded-full h-2.5"><div class="h-2.5 rounded-full ${progressClass} progress-bar" style="width: ${stockPercentage}%"></div></div><div class="restock-form mt-2 hidden"></div></div>`;
227
+ container.insertAdjacentHTML('beforeend', cardHTML);
228
+ });
229
+ }
230
+
231
+ function renderProductionLog() {
232
+ const list = document.getElementById('production-log-list');
233
+ list.innerHTML = '';
234
+ if (appState.productionLog.length === 0) { list.innerHTML = `<li class="text-gray-500 text-center pt-4">No production recorded yet.</li>`; return; }
235
+ [...appState.productionLog].reverse().forEach(entry => {
236
+ const date = new Date(entry.date);
237
+ const formattedDate = `${date.toLocaleDateString()} ${date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}`;
238
+ const logHTML = `<li class="p-2 border-b border-gray-100 flex justify-between items-center text-sm"><div><span class="font-semibold text-blue-600">${entry.quantity}x</span> <span class="text-gray-800">${entry.productName}</span></div><span class="text-xs text-gray-400">${formattedDate}</span></li>`;
239
+ list.insertAdjacentHTML('beforeend', logHTML);
240
+ });
241
+ }
242
+
243
+ function renderAnalytics() {
244
+ const container = document.getElementById('analytics-content');
245
+ container.innerHTML = '';
246
+ const totalValue = appState.materials.reduce((sum, mat) => sum + (mat.currentStock * (mat.costPerUnit || 0)), 0);
247
+ let productCostsHTML = '';
248
+ for (const productName in appState.productRecipes) {
249
+ const recipe = appState.productRecipes[productName];
250
+ const cost = Object.keys(recipe).reduce((sum, matName) => {
251
+ const material = appState.materials.find(m => m.name === matName);
252
+ const quantity = recipe[matName];
253
+ return sum + (quantity * (material.costPerUnit || 0));
254
+ }, 0);
255
+ productCostsHTML += `<div class="flex justify-between text-sm"><span class="text-gray-600">${productName}</span><span class="font-medium">$${cost.toFixed(2)} / unit</span></div>`;
256
+ }
257
+ const analyticsHTML = `<div class="p-3 bg-gray-50 rounded-lg"><div class="flex justify-between items-center"><span class="text-gray-600">Total Inventory Value</span><span class="text-lg font-bold text-green-600">$${totalValue.toFixed(2)}</span></div></div><div><h4 class="font-semibold text-sm mb-2 text-gray-700 mt-3">Material Cost Per Product</h4><div class="space-y-1">${productCostsHTML}</div></div>`;
258
+ container.innerHTML = analyticsHTML;
259
+ }
260
+
261
+ function renderReorderList() {
262
+ const list = document.getElementById('reorder-list');
263
+ list.innerHTML = '';
264
+ const itemsToReorder = appState.materials.filter(m => m.currentStock <= (m.reorderPoint || 0));
265
+ if (itemsToReorder.length === 0) { list.innerHTML = `<li class="text-gray-500 text-center pt-4">All stock levels are healthy.</li>`; return; }
266
+ itemsToReorder.forEach(item => {
267
+ const needed = item.maxStock - item.currentStock;
268
+ const itemHTML = `<li class="p-3 rounded-md bg-yellow-50 border border-yellow-200 text-sm" data-material-name="${item.name}"><div class="flex justify-between items-center"><div><span class="font-semibold text-yellow-800">${item.name}</span><span class="text-xs text-yellow-600 block">Stock: ${item.currentStock} / ${item.reorderPoint} (Need ${needed})</span></div><button class="restock-btn-reorder text-xs bg-yellow-400 text-yellow-900 font-bold py-1 px-3 rounded hover:bg-yellow-500">Restock</button></div><div class="restock-form mt-2 hidden"></div></li>`;
269
+ list.insertAdjacentHTML('beforeend', itemHTML);
270
+ });
271
+ }