Ultronprime commited on
Commit
e9ca488
·
verified ·
1 Parent(s): f9c88e5

Update ui.js

Browse files
Files changed (1) hide show
  1. ui.js +15 -45
ui.js CHANGED
@@ -1,27 +1,23 @@
1
- // ui.js - Renders all UI components (Corrected Chart Logic)
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;
9
  let inventoryChart = null;
10
 
11
- // Set Chart.js defaults for our dark theme
12
  Chart.defaults.color = 'hsl(210, 14%, 66%)';
13
  Chart.defaults.borderColor = 'hsl(220, 13%, 30%)';
14
 
15
- // Master function to update the entire UI
16
  export function refreshUI() {
17
  renderKpiCards();
18
  renderCharts();
19
  renderProductInputs();
20
  renderInventory();
21
  renderProductionLog();
22
- renderAnalytics();
23
- renderReorderList();
24
  renderModals();
 
25
  attachAllListeners();
26
  }
27
 
@@ -39,13 +35,13 @@ export function showToast(message, type = 'info') {
39
  }, 3000);
40
  }
41
 
42
- function animateValue(element, start, end, duration, prefix = '') {
43
  let startTimestamp = null;
44
  const step = (timestamp) => {
45
  if (!startTimestamp) startTimestamp = timestamp;
46
  const progress = Math.min((timestamp - startTimestamp) / duration, 1);
47
  const current = Math.floor(progress * (end - start) + start);
48
- element.textContent = `${prefix}${current.toLocaleString()}`;
49
  if (progress < 1) {
50
  window.requestAnimationFrame(step);
51
  }
@@ -57,7 +53,7 @@ function renderKpiCards() {
57
  const kpiRow = document.getElementById('kpi-row');
58
  if (!kpiRow) return;
59
 
60
- const totalValue = appState.materials.reduce((sum, mat) => sum + (mat.currentStock * (mat.costPerUnit || 0)), 0);
61
  const itemsBelowReorder = appState.materials.filter(m => m.currentStock <= m.reorderPoint).length;
62
  const oneMonthAgo = new Date(new Date().setMonth(new Date().getMonth() - 1));
63
  const unitsProducedMonth = appState.productionLog
@@ -65,20 +61,20 @@ function renderKpiCards() {
65
  .reduce((sum, entry) => sum + entry.quantity, 0);
66
 
67
  const kpis = [
68
- { id: 'kpi-value', label: 'Total Inventory Value', value: totalValue, prefix: '$', color: 'var(--accent-green)' },
69
- { id: 'kpi-units', label: 'Units Produced (Month)', value: unitsProducedMonth, prefix: '', color: 'var(--accent-blue)' },
70
- { id: 'kpi-reorder', label: 'Items Below Reorder', value: itemsBelowReorder, prefix: '', color: 'var(--accent-yellow)' },
71
- { id: 'kpi-materials', label: 'Materials to Order', value: itemsBelowReorder, prefix: '', color: 'var(--accent-red)' }
72
  ];
73
 
74
  kpiRow.innerHTML = kpis.map((kpi, index) => `
75
  <div class="dashboard-card p-4 flex items-center">
76
  <div class="kpi-icon-wrapper mr-4" style="background-color: ${kpi.color}20; color: ${kpi.color};">
77
- <i class="fas ${['fa-dollar-sign', 'fa-cogs', 'fa-exclamation-triangle', 'fa-shopping-cart'][index]} fa-lg"></i>
78
  </div>
79
  <div>
80
  <p class="text-sm text-secondary">${kpi.label}</p>
81
- <p class="text-2xl font-bold text-primary" id="${kpi.id}-${index}">${kpi.prefix}0</p>
82
  </div>
83
  </div>
84
  `).join('');
@@ -86,7 +82,7 @@ function renderKpiCards() {
86
  kpis.forEach((kpi, index) => {
87
  const element = document.getElementById(`${kpi.id}-${index}`);
88
  if (element) {
89
- animateValue(element, 0, kpi.value, 1500, kpi.prefix);
90
  }
91
  });
92
  }
@@ -112,14 +108,11 @@ function renderProductionHistoryChart() {
112
  data.push(totalProduced);
113
  }
114
 
115
- // ** BUG FIX STARTS HERE **
116
  if (productionChart) {
117
- // If chart exists, just update its data and refresh
118
  productionChart.data.labels = labels;
119
  productionChart.data.datasets[0].data = data;
120
  productionChart.update();
121
  } else {
122
- // If chart does not exist (first render), create it
123
  productionChart = new Chart(ctx, {
124
  type: 'bar',
125
  data: {
@@ -134,8 +127,7 @@ function renderProductionHistoryChart() {
134
  }]
135
  },
136
  options: {
137
- responsive: true,
138
- maintainAspectRatio: false,
139
  plugins: { legend: { display: false } },
140
  scales: {
141
  y: { beginAtZero: true, grid: { color: 'hsl(220, 13%, 30%)' } },
@@ -144,7 +136,6 @@ function renderProductionHistoryChart() {
144
  }
145
  });
146
  }
147
- // ** BUG FIX ENDS HERE **
148
  }
149
 
150
  function renderInventoryStatusChart() {
@@ -168,7 +159,6 @@ function renderInventoryStatusChart() {
168
  }]
169
  };
170
 
171
- // ** BUG FIX STARTS HERE **
172
  if (inventoryChart) {
173
  inventoryChart.data.datasets[0].data = [okCount, warningCount, criticalCount];
174
  inventoryChart.update();
@@ -177,13 +167,11 @@ function renderInventoryStatusChart() {
177
  type: 'doughnut',
178
  data: data,
179
  options: {
180
- responsive: true,
181
- maintainAspectRatio: false,
182
  plugins: { legend: { position: 'top', labels: { color: 'hsl(210, 14%, 66%)' } } }
183
  }
184
  });
185
  }
186
- // ** BUG FIX ENDS HERE **
187
  }
188
 
189
  function renderModals() {
@@ -249,24 +237,6 @@ function renderProductionLog() {
249
  });
250
  }
251
 
252
- function renderAnalytics() {
253
- const container = document.getElementById('analytics-content');
254
- container.innerHTML = '';
255
- const totalValue = appState.materials.reduce((sum, mat) => sum + (mat.currentStock * (mat.costPerUnit || 0)), 0);
256
- let productCostsHTML = '';
257
- for (const productName in appState.productRecipes) {
258
- const recipe = appState.productRecipes[productName];
259
- const cost = Object.keys(recipe).reduce((sum, matName) => {
260
- const material = appState.materials.find(m => m.name === matName);
261
- const quantity = recipe[matName];
262
- return sum + (quantity * (material?.costPerUnit || 0));
263
- }, 0);
264
- productCostsHTML += `<div class="flex justify-between text-sm"><span class="text-secondary">${productName}</span><span class="font-medium">$${cost.toFixed(2)} / unit</span></div>`;
265
- }
266
- const analyticsHTML = `<div class="p-3 bg-dark rounded-lg"><div class="flex justify-between items-center"><span class="text-secondary">Total Inventory Value</span><span class="text-lg font-bold" style="color: var(--accent-green);">$${totalValue.toFixed(2)}</span></div></div><div><h4 class="font-semibold text-sm mb-2 mt-3">Material Cost Per Product</h4><div class="space-y-1">${productCostsHTML}</div></div>`;
267
- container.innerHTML = analyticsHTML;
268
- }
269
-
270
  function renderReorderList() {
271
  const list = document.getElementById('reorder-list');
272
  list.innerHTML = '';
@@ -274,7 +244,7 @@ function renderReorderList() {
274
  if (itemsToReorder.length === 0) { list.innerHTML = `<li class="text-secondary text-center pt-4">All stock levels are healthy.</li>`; return; }
275
  itemsToReorder.forEach(item => {
276
  const needed = item.maxStock - item.currentStock;
277
- const itemHTML = `<li class="p-3 rounded-md text-sm" style="background-color: #f6e05e20; border-left: 4px solid var(--accent-yellow);" data-material-name="${item.name}"><div class="flex justify-between items-center"><div><span class="font-semibold">${item.name}</span><span class="text-xs text-secondary block">Stock: ${item.currentStock} / ${item.reorderPoint} (Need ${needed})</span></div><button class="restock-btn-reorder btn btn-secondary text-xs">Restock</button></div><div class="restock-form mt-2 hidden"></div></li>`;
278
  list.insertAdjacentHTML('beforeend', itemHTML);
279
  });
280
  }
 
1
+ // ui.js - Renders all UI components
2
 
3
  import { appState } from './state.js';
4
  import { attachAllListeners } from './events.js';
5
  import { getMonthlyProductionSummary, getMonthlyMaterialUsage } from './reportService.js';
6
 
 
7
  let productionChart = null;
8
  let inventoryChart = null;
9
 
 
10
  Chart.defaults.color = 'hsl(210, 14%, 66%)';
11
  Chart.defaults.borderColor = 'hsl(220, 13%, 30%)';
12
 
 
13
  export function refreshUI() {
14
  renderKpiCards();
15
  renderCharts();
16
  renderProductInputs();
17
  renderInventory();
18
  renderProductionLog();
 
 
19
  renderModals();
20
+ renderReorderList();
21
  attachAllListeners();
22
  }
23
 
 
35
  }, 3000);
36
  }
37
 
38
+ function animateValue(element, start, end, duration, prefix = '', suffix = '') {
39
  let startTimestamp = null;
40
  const step = (timestamp) => {
41
  if (!startTimestamp) startTimestamp = timestamp;
42
  const progress = Math.min((timestamp - startTimestamp) / duration, 1);
43
  const current = Math.floor(progress * (end - start) + start);
44
+ element.textContent = `${prefix}${current.toLocaleString()}${suffix}`;
45
  if (progress < 1) {
46
  window.requestAnimationFrame(step);
47
  }
 
53
  const kpiRow = document.getElementById('kpi-row');
54
  if (!kpiRow) return;
55
 
56
+ const totalStockItems = appState.materials.reduce((sum, mat) => sum + mat.currentStock, 0);
57
  const itemsBelowReorder = appState.materials.filter(m => m.currentStock <= m.reorderPoint).length;
58
  const oneMonthAgo = new Date(new Date().setMonth(new Date().getMonth() - 1));
59
  const unitsProducedMonth = appState.productionLog
 
61
  .reduce((sum, entry) => sum + entry.quantity, 0);
62
 
63
  const kpis = [
64
+ { id: 'kpi-stock', label: 'Total Stock Items', value: totalStockItems, suffix: ' pcs', color: 'var(--accent-green)' },
65
+ { id: 'kpi-units', label: 'Units Produced (Month)', value: unitsProducedMonth, suffix: '', color: 'var(--accent-blue)' },
66
+ { id: 'kpi-reorder', label: 'Items Below Reorder', value: itemsBelowReorder, suffix: '', color: 'var(--accent-yellow)' },
67
+ { id: 'kpi-materials', label: 'Materials to Order', value: itemsBelowReorder, suffix: '', color: 'var(--accent-red)' }
68
  ];
69
 
70
  kpiRow.innerHTML = kpis.map((kpi, index) => `
71
  <div class="dashboard-card p-4 flex items-center">
72
  <div class="kpi-icon-wrapper mr-4" style="background-color: ${kpi.color}20; color: ${kpi.color};">
73
+ <i class="fas ${['fa-boxes', 'fa-cogs', 'fa-exclamation-triangle', 'fa-shopping-cart'][index]} fa-lg"></i>
74
  </div>
75
  <div>
76
  <p class="text-sm text-secondary">${kpi.label}</p>
77
+ <p class="text-2xl font-bold text-primary" id="${kpi.id}-${index}">0</p>
78
  </div>
79
  </div>
80
  `).join('');
 
82
  kpis.forEach((kpi, index) => {
83
  const element = document.getElementById(`${kpi.id}-${index}`);
84
  if (element) {
85
+ animateValue(element, 0, kpi.value, 1500, kpi.prefix, kpi.suffix);
86
  }
87
  });
88
  }
 
108
  data.push(totalProduced);
109
  }
110
 
 
111
  if (productionChart) {
 
112
  productionChart.data.labels = labels;
113
  productionChart.data.datasets[0].data = data;
114
  productionChart.update();
115
  } else {
 
116
  productionChart = new Chart(ctx, {
117
  type: 'bar',
118
  data: {
 
127
  }]
128
  },
129
  options: {
130
+ responsive: true, maintainAspectRatio: false,
 
131
  plugins: { legend: { display: false } },
132
  scales: {
133
  y: { beginAtZero: true, grid: { color: 'hsl(220, 13%, 30%)' } },
 
136
  }
137
  });
138
  }
 
139
  }
140
 
141
  function renderInventoryStatusChart() {
 
159
  }]
160
  };
161
 
 
162
  if (inventoryChart) {
163
  inventoryChart.data.datasets[0].data = [okCount, warningCount, criticalCount];
164
  inventoryChart.update();
 
167
  type: 'doughnut',
168
  data: data,
169
  options: {
170
+ responsive: true, maintainAspectRatio: false,
 
171
  plugins: { legend: { position: 'top', labels: { color: 'hsl(210, 14%, 66%)' } } }
172
  }
173
  });
174
  }
 
175
  }
176
 
177
  function renderModals() {
 
237
  });
238
  }
239
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  function renderReorderList() {
241
  const list = document.getElementById('reorder-list');
242
  list.innerHTML = '';
 
244
  if (itemsToReorder.length === 0) { list.innerHTML = `<li class="text-secondary text-center pt-4">All stock levels are healthy.</li>`; return; }
245
  itemsToReorder.forEach(item => {
246
  const needed = item.maxStock - item.currentStock;
247
+ const itemHTML = `<li class="p-3 rounded-md text-sm flex justify-between items-center" style="background-color: #f6e05e20;" data-material-name="${item.name}"><div class="flex-grow"><span class="font-semibold">${item.name}</span><span class="text-xs text-secondary block">Stock: ${item.currentStock} / ${item.reorderPoint} (Need ${needed})</span></div><button class="generate-po-btn btn btn-secondary text-xs">Generate PO</button></li>`;
248
  list.insertAdjacentHTML('beforeend', itemHTML);
249
  });
250
  }