Ultronprime commited on
Commit
30ee0c1
·
verified ·
1 Parent(s): 8204e2a

Create ui.js

Browse files
Files changed (1) hide show
  1. ui.js +116 -0
ui.js ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ui.js - Renders all UI components
2
+
3
+ import { appState } from './state.js';
4
+ import { attachAllListeners } from './events.js';
5
+
6
+ // Master function to update the entire UI
7
+ export function refreshUI() {
8
+ renderProductInputs();
9
+ renderInventory();
10
+ renderProductionLog();
11
+ renderAnalytics();
12
+ renderReorderList();
13
+ renderDailyProductionSummary();
14
+ attachAllListeners(); // Re-attach listeners to new/updated elements
15
+ }
16
+
17
+ export function showToast(message, type = 'info') {
18
+ const container = document.getElementById('toast-container');
19
+ const toast = document.createElement('div');
20
+ const icon = type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-times-circle' : 'fa-info-circle';
21
+ toast.className = `toast toast-${type}`;
22
+ toast.innerHTML = `<i class="fas ${icon}"></i><span>${message}</span>`;
23
+ container.appendChild(toast);
24
+ setTimeout(() => toast.classList.add('show'), 10);
25
+ setTimeout(() => {
26
+ toast.classList.remove('show');
27
+ toast.addEventListener('transitionend', () => toast.remove());
28
+ }, 3000);
29
+ }
30
+
31
+ // --- Internal Rendering Functions ---
32
+
33
+ function renderProductInputs() {
34
+ const container = document.getElementById('product-cards');
35
+ container.innerHTML = '';
36
+ for (const productName in appState.productRecipes) {
37
+ 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>`;
38
+ container.insertAdjacentHTML('beforeend', cardHTML);
39
+ }
40
+ }
41
+
42
+ function renderInventory() {
43
+ const container = document.getElementById('material-cards');
44
+ container.innerHTML = '';
45
+ appState.materials.forEach(material => {
46
+ const stockPercentage = (material.currentStock / material.maxStock) * 100;
47
+ let statusClass = 'status-ok', progressClass = 'progress-ok';
48
+ if (stockPercentage <= 50 && stockPercentage > 20) { statusClass = 'status-warning'; progressClass = 'progress-warning'; }
49
+ else if (stockPercentage <= 20) { statusClass = 'status-critical'; progressClass = 'progress-critical'; }
50
+ 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>`;
51
+ container.insertAdjacentHTML('beforeend', cardHTML);
52
+ });
53
+ }
54
+
55
+ function renderProductionLog() {
56
+ const list = document.getElementById('production-log-list');
57
+ list.innerHTML = '';
58
+ if (appState.productionLog.length === 0) { list.innerHTML = `<li class="text-gray-500 text-center pt-4">No production recorded yet.</li>`; return; }
59
+ [...appState.productionLog].reverse().forEach(entry => {
60
+ const date = new Date(entry.date);
61
+ const formattedDate = `${date.toLocaleDateString()} ${date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}`;
62
+ 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>`;
63
+ list.insertAdjacentHTML('beforeend', logHTML);
64
+ });
65
+ }
66
+
67
+ function renderAnalytics() {
68
+ const container = document.getElementById('analytics-content');
69
+ container.innerHTML = '';
70
+ const totalValue = appState.materials.reduce((sum, mat) => sum + (mat.currentStock * (mat.costPerUnit || 0)), 0);
71
+ let productCostsHTML = '';
72
+ for (const productName in appState.productRecipes) {
73
+ const recipe = appState.productRecipes[productName];
74
+ const cost = Object.keys(recipe).reduce((sum, matName) => {
75
+ const material = appState.materials.find(m => m.name === matName);
76
+ const quantity = recipe[matName];
77
+ return sum + (quantity * (material.costPerUnit || 0));
78
+ }, 0);
79
+ 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>`;
80
+ }
81
+ 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>`;
82
+ container.innerHTML = analyticsHTML;
83
+ }
84
+
85
+ function renderReorderList() {
86
+ const list = document.getElementById('reorder-list');
87
+ list.innerHTML = '';
88
+ const itemsToReorder = appState.materials.filter(m => m.currentStock <= (m.reorderPoint || 0));
89
+ if (itemsToReorder.length === 0) { list.innerHTML = `<li class="text-gray-500 text-center pt-4">All stock levels are healthy.</li>`; return; }
90
+ itemsToReorder.forEach(item => {
91
+ const needed = item.maxStock - item.currentStock;
92
+ 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>`;
93
+ list.insertAdjacentHTML('beforeend', itemHTML);
94
+ });
95
+ }
96
+
97
+ function renderDailyProductionSummary() {
98
+ const container = document.getElementById('daily-production-summary');
99
+ container.innerHTML = '';
100
+ if (appState.productionLog.length === 0) { container.innerHTML = `<p class="text-gray-500 text-center pt-4">No production recorded yet.</p>`; return; }
101
+ const dailySummary = {};
102
+ appState.productionLog.forEach(entry => {
103
+ const date = new Date(entry.date).toLocaleDateString();
104
+ if (!dailySummary[date]) dailySummary[date] = {};
105
+ if (!dailySummary[date][entry.productName]) dailySummary[date][entry.productName] = 0;
106
+ dailySummary[date][entry.productName] += entry.quantity;
107
+ });
108
+ const sortedDates = Object.keys(dailySummary).sort((a, b) => new Date(b) - new Date(a));
109
+ sortedDates.forEach(date => {
110
+ let dailyEntryHTML = `<div class="p-3 rounded-md bg-blue-50 border border-blue-200 text-sm">`;
111
+ dailyEntryHTML += `<h5 class="font-semibold text-blue-800 mb-1">${date}</h5>`;
112
+ for (const productName in dailySummary[date]) { dailyEntryHTML += `<p class="text-gray-700 ml-2">${productName}: <span class="font-bold">${dailySummary[date][productName]} units</span></p>`; }
113
+ dailyEntryHTML += `</div>`;
114
+ container.insertAdjacentHTML('beforeend', dailyEntryHTML);
115
+ });
116
+ }