diff --git a/public/app.js b/public/app.js
index bc51368..0860135 100644
--- a/public/app.js
+++ b/public/app.js
@@ -1,6 +1,6 @@
/**
* Fridger Frontend Application
- * Tablet-optimized UI: Main -> Scan -> Manage navigation and 3-button product management.
+ * Tablet-optimized: Main -> Scan -> Manage (Scan/Search -> Add/Remove -> Packet/Amount)
*/
const state = {
@@ -16,8 +16,13 @@ const state = {
category: 'All',
status: 'all'
},
- activeGramsItem: null,
- activeGramsValue: 0,
+ manage: {
+ selectedItem: null,
+ action: null, // 'add' or 'remove'
+ qtyMode: 'packet', // 'packet' or 'amount'
+ packetCount: 1,
+ amountValue: 0
+ },
html5QrCode: null,
isScanning: false
};
@@ -79,6 +84,7 @@ async function loadItems() {
if (res.ok) {
state.items = await res.json();
renderApp();
+ renderManageQuickPicker();
}
} catch (err) {
console.error('Failed to load items:', err);
@@ -95,27 +101,33 @@ function switchTab(tab) {
const mainBtn = document.getElementById('nav-main-btn');
const manageBtn = document.getElementById('nav-manage-btn');
- const viewContainer = document.getElementById('view-tab-container');
- const manageContainer = document.getElementById('manage-tab-container');
+ const mainView = document.getElementById('main-tab-view');
+ const manageView = document.getElementById('manage-tab-view');
- const activeClasses = 'flex items-center justify-center gap-2 px-3 sm:px-6 py-2.5 sm:py-3 rounded-xl text-xs sm:text-sm font-bold transition-all bg-teal-500 text-white shadow-md active:scale-95 touch-manipulation min-h-[44px]';
- const inactiveClasses = 'flex items-center justify-center gap-2 px-3 sm:px-6 py-2.5 sm:py-3 rounded-xl text-xs sm:text-sm font-bold transition-all text-slate-300 hover:text-white hover:bg-slate-800/80 active:scale-95 touch-manipulation min-h-[44px]';
+ const activeClasses = 'flex items-center justify-center gap-2 px-4 sm:px-7 py-2.5 sm:py-3 rounded-xl text-xs sm:text-sm font-bold transition-all bg-teal-500 text-white shadow-md active:scale-95 touch-manipulation min-h-[44px]';
+ const inactiveClasses = 'flex items-center justify-center gap-2 px-4 sm:px-7 py-2.5 sm:py-3 rounded-xl text-xs sm:text-sm font-bold transition-all text-slate-300 hover:text-white hover:bg-slate-800/80 active:scale-95 touch-manipulation min-h-[44px]';
if (tab === 'main') {
mainBtn.className = activeClasses;
manageBtn.className = inactiveClasses;
- viewContainer.classList.remove('hidden');
- manageContainer.classList.add('hidden');
+ mainView.classList.remove('hidden');
+ manageView.classList.add('hidden');
+ renderApp();
} else if (tab === 'manage') {
manageBtn.className = activeClasses;
mainBtn.className = inactiveClasses;
- manageContainer.classList.remove('hidden');
- viewContainer.classList.add('hidden');
+ manageView.classList.remove('hidden');
+ mainView.classList.add('hidden');
+ renderManageQuickPicker();
}
- renderApp();
+ initLucide();
}
+// ----------------------------------------------------
+// Filters & Search (Main Tab)
+// ----------------------------------------------------
+
let searchDebounceTimer = null;
function handleSearch(val) {
state.filters.search = val.trim();
@@ -197,35 +209,21 @@ function populateModalSelects() {
}
// ----------------------------------------------------
-// UI Rendering
+// UI Rendering (Main Tab: Fridge Cards)
// ----------------------------------------------------
function renderApp() {
const emptyState = document.getElementById('empty-state');
+ const container = document.getElementById('fridge-view-content');
if (state.items.length === 0) {
emptyState.classList.remove('hidden');
- document.getElementById('fridge-view-content').innerHTML = '';
- document.getElementById('manage-items-list').innerHTML = '';
+ container.innerHTML = '';
return;
}
emptyState.classList.add('hidden');
- if (state.currentTab === 'main') {
- renderFridgeView();
- } else {
- renderManageView();
- }
-
- initLucide();
-}
-
-// MAIN TAB: Fridge View (Tablet-optimized cards)
-function renderFridgeView() {
- const container = document.getElementById('fridge-view-content');
- if (!container) return;
-
container.innerHTML = state.items.map(item => {
const expiryInfo = getExpiryStatus(item.expiry_date);
const percent = item.initial_quantity > 0
@@ -284,282 +282,288 @@ function renderFridgeView() {
-
-
-
-
-
-
+
+
`;
}).join('');
-}
-// MANAGE TAB: Manage Products Interface (Optimized for Tablet with Large Controls)
-function renderManageView() {
- const container = document.getElementById('manage-items-list');
- if (!container) return;
-
- container.innerHTML = state.items.map(item => {
- const expiryInfo = getExpiryStatus(item.expiry_date);
- const percent = item.initial_quantity > 0
- ? Math.min(100, Math.max(0, Math.round((item.quantity / item.initial_quantity) * 100)))
- : 100;
-
- return `
-
-
-
-
-
-
- ${item.image_url ? `
-
})
- ` : `
-
-
-
- `}
-
-
-
-
- ${item.brand ? `${escapeHtml(item.brand)}` : ''}
-
-
- ${item.quantity} ${item.unit}
- (Pack size: ${item.initial_quantity} ${item.unit})
- •
- ${expiryInfo.label}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
- }).join('');
-}
-
-// ----------------------------------------------------
-// 3 Primary Action Handlers
-// ----------------------------------------------------
-
-async function addWholePacket(itemId) {
- try {
- const res = await fetch(`/api/items/${itemId}/add-packet`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ count: 1 })
- });
-
- if (res.ok) {
- const updated = await res.json();
- showToast(`+1 Pack of ${updated.name} (Total: ${updated.quantity} ${updated.unit})`, 'success');
- await loadStats();
- await loadItems();
- } else {
- showToast('Failed to add packet', 'error');
- }
- } catch (err) {
- console.error(err);
- showToast('Network error', 'error');
- }
-}
-
-async function removeWholePacket(itemId) {
- try {
- const res = await fetch(`/api/items/${itemId}/remove-packet`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ count: 1 })
- });
-
- if (res.ok) {
- const updated = await res.json();
- showToast(`-1 Pack of ${updated.name} (Remaining: ${updated.quantity} ${updated.unit})`, 'info');
- await loadStats();
- await loadItems();
- } else {
- showToast('Failed to remove packet', 'error');
- }
- } catch (err) {
- console.error(err);
- showToast('Network error', 'error');
- }
-}
-
-// ----------------------------------------------------
-// Manage Grams Modal Logic
-// ----------------------------------------------------
-
-function openGramsModal(itemId) {
- const item = state.items.find(i => i.id === itemId);
- if (!item) return;
-
- state.activeGramsItem = item;
- state.activeGramsValue = item.quantity;
-
- document.getElementById('grams-modal-title').textContent = item.name;
- document.getElementById('grams-modal-sub').textContent = `1 Full Packet = ${item.initial_quantity} ${item.unit}`;
- document.getElementById('grams-modal-unit').textContent = item.unit;
- document.getElementById('grams-current-val').textContent = item.quantity;
- document.getElementById('grams-exact-input').value = item.quantity;
-
- const slider = document.getElementById('grams-slider');
- slider.max = Math.max(item.initial_quantity * 2, item.quantity, 100);
- slider.step = (item.unit === 'g' || item.unit === 'ml') ? '5' : '1';
- slider.value = item.quantity;
-
- updateGramsGauge();
- document.getElementById('grams-modal').classList.remove('hidden');
initLucide();
}
-function closeGramsModal() {
- document.getElementById('grams-modal').classList.add('hidden');
- state.activeGramsItem = null;
+// ----------------------------------------------------
+// MANAGE TAB: 3-Step Flow (Scan/Search -> Add/Remove -> Packet/Amount)
+// ----------------------------------------------------
+
+function renderManageQuickPicker(filterQuery = '') {
+ const container = document.getElementById('manage-quick-picker');
+ if (!container) return;
+
+ const query = filterQuery.toLowerCase().trim();
+ const matched = state.items.filter(i => {
+ if (!query) return true;
+ return (i.name && i.name.toLowerCase().includes(query)) ||
+ (i.brand && i.brand.toLowerCase().includes(query)) ||
+ (i.barcode && i.barcode.includes(query));
+ });
+
+ if (matched.length === 0) {
+ container.innerHTML = `
+
+ No matching fridge items found. Scan barcode or add as new product!
+
+ `;
+ return;
+ }
+
+ container.innerHTML = matched.map(item => `
+
+ `).join('');
+
+ initLucide();
}
-function onGramsSliderChange(val) {
- state.activeGramsValue = parseFloat(val);
- document.getElementById('grams-exact-input').value = state.activeGramsValue;
- updateGramsGauge();
-}
-
-function onGramsExactInput(val) {
- const num = parseFloat(val);
- if (!isNaN(num) && num >= 0) {
- state.activeGramsValue = num;
- document.getElementById('grams-slider').value = num;
- updateGramsGauge();
+function handleManageSearch(val) {
+ renderManageQuickPicker(val);
+
+ // If barcode entered directly (e.g. 8-14 digits) and matches an item
+ const clean = val.trim();
+ if (/^\d{8,14}$/.test(clean)) {
+ const found = state.items.find(i => i.barcode === clean);
+ if (found) {
+ selectManageItem(found);
+ }
}
}
-function applyGramsDelta(delta) {
- state.activeGramsValue = Math.max(0, state.activeGramsValue + delta);
- document.getElementById('grams-slider').value = state.activeGramsValue;
- document.getElementById('grams-exact-input').value = state.activeGramsValue;
- updateGramsGauge();
+function selectManageItemById(id) {
+ const item = state.items.find(i => i.id === id);
+ if (item) selectManageItem(item);
}
-function setGramsPercent(ratio) {
- if (!state.activeGramsItem) return;
- state.activeGramsValue = Math.round(state.activeGramsItem.initial_quantity * ratio);
- document.getElementById('grams-slider').value = state.activeGramsValue;
- document.getElementById('grams-exact-input').value = state.activeGramsValue;
- updateGramsGauge();
-}
+function selectManageItem(item) {
+ state.manage.selectedItem = item;
+ state.manage.action = null; // reset step 2
+ state.manage.packetCount = 1;
+ state.manage.amountValue = item.quantity;
-function updateGramsGauge() {
- document.getElementById('grams-current-val').textContent = state.activeGramsValue;
- if (state.activeGramsItem && state.activeGramsItem.initial_quantity > 0) {
- const pct = Math.round((state.activeGramsValue / state.activeGramsItem.initial_quantity) * 100);
- document.getElementById('grams-percent-text').textContent = `${pct}% remaining of a full ${state.activeGramsItem.initial_quantity}${state.activeGramsItem.unit} packet`;
+ // Populate Selected Product Banner
+ document.getElementById('manage-selected-name').textContent = item.name;
+ document.getElementById('manage-selected-details').textContent = `Currently: ${item.quantity} ${item.unit} in fridge • 1 Full Pack = ${item.initial_quantity} ${item.unit}`;
+ document.getElementById('manage-pack-size-label').textContent = `${item.initial_quantity} ${item.unit}`;
+ document.getElementById('manage-amount-unit').textContent = item.unit;
+ document.getElementById('manage-amount-val').textContent = item.quantity;
+
+ const slider = document.getElementById('manage-amount-slider');
+ slider.max = Math.max(item.initial_quantity * 2, item.quantity + item.initial_quantity, 100);
+ slider.value = item.quantity;
+ slider.step = (item.unit === 'g' || item.unit === 'ml') ? '5' : '1';
+
+ const imgBox = document.getElementById('manage-selected-img');
+ if (item.image_url) {
+ imgBox.innerHTML = `
`;
+ } else {
+ imgBox.innerHTML = ``;
}
+
+ // Reveal Action Card
+ document.getElementById('manage-action-card').classList.remove('hidden');
+ document.getElementById('manage-qty-type-section').classList.add('hidden');
+
+ // Reset Action Button Styles
+ resetActionButtonsStyle();
+
+ renderManageQuickPicker();
+ initLucide();
+
+ // Scroll smoothly to action card on tablet
+ document.getElementById('manage-action-card').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
-async function saveGramsAdjustment() {
- if (!state.activeGramsItem) return;
- const itemId = state.activeGramsItem.id;
- const newQty = state.activeGramsValue;
+function clearSelectedManageItem() {
+ state.manage.selectedItem = null;
+ state.manage.action = null;
+ document.getElementById('manage-action-card').classList.add('hidden');
+ renderManageQuickPicker();
+}
+
+function goToManageItem(itemId) {
+ switchTab('manage');
+ selectManageItemById(itemId);
+}
+
+function setManageAction(action) {
+ state.manage.action = action; // 'add' or 'remove'
+ const btnAdd = document.getElementById('manage-btn-add');
+ const btnRemove = document.getElementById('manage-btn-remove');
+ const qtySection = document.getElementById('manage-qty-type-section');
+ const confirmBtn = document.getElementById('manage-confirm-btn');
+ const confirmLabel = document.getElementById('manage-confirm-label');
+ const step3Title = document.getElementById('manage-step3-title');
+
+ if (action === 'add') {
+ btnAdd.className = 'p-4 sm:p-5 rounded-2xl border-2 text-center font-extrabold transition-all active:scale-95 touch-manipulation flex flex-col items-center justify-center gap-2 min-h-[80px] bg-emerald-500/20 border-emerald-500 text-white shadow-lg shadow-emerald-500/20 ring-2 ring-emerald-500/40';
+ btnRemove.className = 'p-4 sm:p-5 rounded-2xl border-2 text-center font-extrabold transition-all active:scale-95 touch-manipulation flex flex-col items-center justify-center gap-2 min-h-[80px] bg-slate-950 border-slate-800 text-slate-400 hover:border-slate-700';
+ step3Title.textContent = `How much ${state.manage.selectedItem.name} to ADD?`;
+ confirmBtn.className = 'w-full py-4 rounded-2xl bg-emerald-500 hover:bg-emerald-400 text-white font-black text-base sm:text-lg shadow-xl shadow-emerald-500/25 active:scale-95 transition-all touch-manipulation flex items-center justify-center gap-2';
+ confirmLabel.textContent = `Confirm Add to Fridge`;
+ } else {
+ btnRemove.className = 'p-4 sm:p-5 rounded-2xl border-2 text-center font-extrabold transition-all active:scale-95 touch-manipulation flex flex-col items-center justify-center gap-2 min-h-[80px] bg-rose-500/20 border-rose-500 text-white shadow-lg shadow-rose-500/20 ring-2 ring-rose-500/40';
+ btnAdd.className = 'p-4 sm:p-5 rounded-2xl border-2 text-center font-extrabold transition-all active:scale-95 touch-manipulation flex flex-col items-center justify-center gap-2 min-h-[80px] bg-slate-950 border-slate-800 text-slate-400 hover:border-slate-700';
+ step3Title.textContent = `How much ${state.manage.selectedItem.name} to REMOVE?`;
+ confirmBtn.className = 'w-full py-4 rounded-2xl bg-rose-500 hover:bg-rose-400 text-white font-black text-base sm:text-lg shadow-xl shadow-rose-500/25 active:scale-95 transition-all touch-manipulation flex items-center justify-center gap-2';
+ confirmLabel.textContent = `Confirm Remove from Fridge`;
+ }
+
+ qtySection.classList.remove('hidden');
+ initLucide();
+}
+
+function resetActionButtonsStyle() {
+ const btnAdd = document.getElementById('manage-btn-add');
+ const btnRemove = document.getElementById('manage-btn-remove');
+ const defClasses = 'p-4 sm:p-5 rounded-2xl border-2 text-center font-extrabold transition-all active:scale-95 touch-manipulation flex flex-col items-center justify-center gap-2 min-h-[80px] bg-slate-950 border-slate-800 text-slate-300 hover:border-slate-700';
+ btnAdd.className = defClasses;
+ btnRemove.className = defClasses;
+}
+
+function setQtyMode(mode) {
+ state.manage.qtyMode = mode; // 'packet' or 'amount'
+ const btnPack = document.getElementById('qty-mode-packet-btn');
+ const btnAmount = document.getElementById('qty-mode-amount-btn');
+ const packControls = document.getElementById('qty-packet-controls');
+ const amountControls = document.getElementById('qty-amount-controls');
+
+ const active = 'py-3 px-3 rounded-xl font-bold text-xs sm:text-sm transition bg-teal-500 text-white shadow-md touch-manipulation flex items-center justify-center gap-2';
+ const inactive = 'py-3 px-3 rounded-xl font-bold text-xs sm:text-sm transition text-slate-300 hover:text-white touch-manipulation flex items-center justify-center gap-2';
+
+ if (mode === 'packet') {
+ btnPack.className = active;
+ btnAmount.className = inactive;
+ packControls.classList.remove('hidden');
+ amountControls.classList.add('hidden');
+ } else {
+ btnAmount.className = active;
+ btnPack.className = inactive;
+ amountControls.classList.remove('hidden');
+ packControls.classList.add('hidden');
+ }
+
+ initLucide();
+}
+
+// Packet count stepper
+function changePacketCount(delta) {
+ state.manage.packetCount = Math.max(1, state.manage.packetCount + delta);
+ document.getElementById('manage-pack-count').textContent = state.manage.packetCount;
+}
+
+function setPacketCount(num) {
+ state.manage.packetCount = num;
+ document.getElementById('manage-pack-count').textContent = num;
+}
+
+// Specific amount slider & delta
+function onManageAmountSlider(val) {
+ state.manage.amountValue = parseFloat(val);
+ document.getElementById('manage-amount-val').textContent = state.manage.amountValue;
+}
+
+function applyManageDelta(delta) {
+ state.manage.amountValue = Math.max(0, state.manage.amountValue + delta);
+ document.getElementById('manage-amount-slider').value = state.manage.amountValue;
+ document.getElementById('manage-amount-val').textContent = state.manage.amountValue;
+}
+
+function setManageAmountRatio(ratio) {
+ if (!state.manage.selectedItem) return;
+ state.manage.amountValue = Math.round(state.manage.selectedItem.initial_quantity * ratio);
+ document.getElementById('manage-amount-slider').value = state.manage.amountValue;
+ document.getElementById('manage-amount-val').textContent = state.manage.amountValue;
+}
+
+// ----------------------------------------------------
+// EXECUTION: Commit Add / Remove
+// ----------------------------------------------------
+
+async function confirmManageExecution() {
+ const item = state.manage.selectedItem;
+ const action = state.manage.action;
+ const mode = state.manage.qtyMode;
+
+ if (!item || !action) {
+ showToast('Please select Add or Remove action first', 'error');
+ return;
+ }
try {
- const res = await fetch(`/api/items/${itemId}/quantity`, {
- method: 'PATCH',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ newQuantity: newQty, markOpened: true })
- });
+ if (mode === 'packet') {
+ const endpoint = action === 'add' ? `/api/items/${item.id}/add-packet` : `/api/items/${item.id}/remove-packet`;
+ const res = await fetch(endpoint, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ count: state.manage.packetCount })
+ });
- if (res.ok) {
- showToast(`Updated to ${newQty} ${state.activeGramsItem.unit}`, 'success');
- closeGramsModal();
- await loadStats();
- await loadItems();
- } else {
- showToast('Failed to update quantity', 'error');
- }
- } catch (err) {
- console.error('Save grams error:', err);
- showToast('Network error while updating quantity', 'error');
- }
-}
-
-async function consumeAllAndRemove() {
- if (!state.activeGramsItem) return;
- const itemId = state.activeGramsItem.id;
-
- if (confirm(`Remove "${state.activeGramsItem.name}" from fridge?`)) {
- try {
- const res = await fetch(`/api/items/${itemId}`, { method: 'DELETE' });
if (res.ok) {
- showToast('Item removed', 'info');
- closeGramsModal();
+ const updated = await res.json();
+ playBeepSound();
+ showToast(`✓ ${action === 'add' ? 'Added' : 'Removed'} ${state.manage.packetCount} packet(s) of ${updated.name} (Now: ${updated.quantity} ${updated.unit})`, 'success');
+ clearSelectedManageItem();
await loadStats();
await loadItems();
+ } else {
+ showToast('Failed to update packet', 'error');
+ }
+ } else {
+ // Specific Grams / Amount mode
+ const newQty = state.manage.amountValue;
+ const res = await fetch(`/api/items/${item.id}/quantity`, {
+ method: 'PATCH',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ newQuantity: newQty, markOpened: true })
+ });
+
+ if (res.ok) {
+ const updated = await res.json();
+ playBeepSound();
+ showToast(`✓ Set ${updated.name} to ${updated.quantity} ${updated.unit}`, 'success');
+ clearSelectedManageItem();
+ await loadStats();
+ await loadItems();
+ } else {
+ showToast('Failed to update amount', 'error');
}
- } catch (err) {
- console.error(err);
}
+ } catch (err) {
+ console.error('Execution error:', err);
+ showToast('Network error while updating item', 'error');
}
}
// ----------------------------------------------------
-// Item Add / Edit Modal
+// Add / Edit Product Modal Logic
// ----------------------------------------------------
function openItemModal(itemId = null) {
@@ -690,10 +694,14 @@ async function handleItemFormSubmit(event) {
});
if (res.ok) {
+ const saved = await res.json();
showToast(itemId ? 'Product updated' : 'Product added to fridge', 'success');
closeItemModal();
await loadStats();
await loadItems();
+ // Auto-select in manage tab
+ selectManageItem(saved);
+ switchTab('manage');
} else {
const err = await res.json();
showToast(err.error || 'Failed to save product', 'error');
@@ -704,22 +712,6 @@ async function handleItemFormSubmit(event) {
}
}
-async function confirmDeleteItem(itemId, name) {
- if (confirm(`Remove "${name}" from fridge?`)) {
- try {
- const res = await fetch(`/api/items/${itemId}`, { method: 'DELETE' });
- if (res.ok) {
- showToast(`Removed "${name}"`, 'info');
- await loadStats();
- await loadItems();
- }
- } catch (err) {
- console.error(err);
- showToast('Failed to delete product', 'error');
- }
- }
-}
-
// ----------------------------------------------------
// Tablet Live Camera Barcode Scanner
// ----------------------------------------------------
@@ -785,17 +777,25 @@ async function stopCameraScanner() {
async function onBarcodeScannedSuccess(decodedText) {
playBeepSound();
stopCameraScanner();
- document.getElementById('scanner-status').textContent = `Scanned ${decodedText}!`;
+ closeScannerModal();
+ // Check if item exists in fridge
+ const existing = state.items.find(i => i.barcode === decodedText);
+ if (existing) {
+ switchTab('manage');
+ selectManageItem(existing);
+ showToast(`Scanned: ${existing.name}`, 'success');
+ return;
+ }
+
+ // Otherwise fetch from API / Open Food Facts
try {
const res = await fetch(`/api/barcode/${encodeURIComponent(decodedText)}`);
- closeScannerModal();
-
if (res.ok) {
const data = await res.json();
openItemModal();
applyProductDataToForm(data);
- showToast(`Scanned: ${data.name}`, 'success');
+ showToast(`Scanned: ${data.name}. Click Save to add!`, 'success');
} else {
openItemModal();
document.getElementById('modal-barcode').value = decodedText;
@@ -803,7 +803,6 @@ async function onBarcodeScannedSuccess(decodedText) {
}
} catch (err) {
console.error(err);
- closeScannerModal();
openItemModal();
document.getElementById('modal-barcode').value = decodedText;
}
diff --git a/public/index.html b/public/index.html
index af5d3b6..a474a24 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3,7 +3,7 @@
- Fridger - Smart Fridge Manager
+ Fridger - Tablet Fridge Manager
@@ -18,9 +18,6 @@
500: '#14b8a6',
600: '#0d9488',
}
- },
- screens: {
- 'tablet': '640px',
}
}
}
@@ -37,74 +34,74 @@
-
+
-
+
-
+
🧊
-
Smart Fridge
+
Tablet Manager
-
+
-
+
-
+
-
+
@@ -126,154 +123,252 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
-
+
🧊
Your fridge is empty
-
Scan a barcode or add an item to track quantities and grams.
-
-
-
-
+
Scan a barcode or add an item to track quantities.
+
-
-
-
-
-
-
-
Manage Grams / Amount
-
-
1 Full Packet = 250g
+
+
+
+
+
+
-
-
-
-
- 250
- g
-
-
Remaining in open pack
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
Currently: 250g in fridge • 1 Pack = 250g
+
+
+
+
+
+
+
+
+
+
+
+
+
3
+
How much to Add / Remove?
+
+
+
+
+
+
+
+
+
+
+
+ 1 Packet = 250g
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 150
+ g
+
+
New remaining amount in fridge
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -312,7 +407,6 @@
-
@@ -321,7 +415,6 @@
-
@@ -333,7 +426,6 @@
-
@@ -357,7 +449,6 @@
-
-