diff --git a/public/app.js b/public/app.js
index a1be8f9..9f05cb9 100644
--- a/public/app.js
+++ b/public/app.js
@@ -1,11 +1,11 @@
/**
* Fridger Frontend Application
- * Tablet-optimized Fridge & Freezer Inventory Manager (No expiration dates)
- * Main -> Scan -> Manage (Scan/Search -> Add/Remove -> Packet/Amount)
+ * Route / : Current Stock Overview
+ * Route /manage : Tablet Scanner & Manager Station
*/
const state = {
- currentTab: 'main', // 'main' or 'manage'
+ currentRoute: '/', // '/' or '/manage'
items: [],
meta: {
categories: [],
@@ -28,11 +28,19 @@ const state = {
isScanning: false
};
-// Initialize Application
+// Initialize Application & Router
document.addEventListener('DOMContentLoaded', async () => {
await loadMetadata();
await loadStats();
await loadItems();
+
+ // Handle initial browser route
+ handleInitialRoute();
+
+ window.addEventListener('popstate', () => {
+ handleInitialRoute();
+ });
+
initLucide();
});
@@ -42,6 +50,56 @@ function initLucide() {
}
}
+// ----------------------------------------------------
+// Client-Side Router
+// ----------------------------------------------------
+
+function handleInitialRoute() {
+ const path = window.location.pathname;
+ if (path === '/manage' || path.startsWith('/manage')) {
+ setRoute('/manage');
+ } else {
+ setRoute('/');
+ }
+}
+
+function navigateTo(path) {
+ window.history.pushState({}, '', path);
+ setRoute(path);
+}
+
+function setRoute(path) {
+ state.currentRoute = path === '/manage' ? '/manage' : '/';
+
+ const stockBtn = document.getElementById('nav-stock-btn');
+ const manageBtn = document.getElementById('nav-manage-btn');
+ const stockPage = document.getElementById('stock-view-page');
+ const managePage = document.getElementById('manage-view-page');
+ const subTitle = document.getElementById('page-subtitle');
+
+ const activeClass = 'flex items-center justify-center gap-2 px-3.5 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 inactiveClass = 'flex items-center justify-center gap-2 px-3.5 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]';
+
+ if (state.currentRoute === '/') {
+ stockBtn.className = activeClass;
+ manageBtn.className = inactiveClass;
+ stockPage.classList.remove('hidden');
+ managePage.classList.add('hidden');
+ subTitle.textContent = 'Current Stock';
+ renderStockView();
+ } else {
+ manageBtn.className = activeClass;
+ stockBtn.className = inactiveClass;
+ managePage.classList.remove('hidden');
+ stockPage.classList.add('hidden');
+ subTitle.textContent = 'Tablet Station';
+ resetTabletManageStation();
+ renderTabletProductPicker();
+ }
+
+ initLucide();
+}
+
// ----------------------------------------------------
// API Calls
// ----------------------------------------------------
@@ -84,49 +142,20 @@ async function loadItems() {
const res = await fetch(`/api/items?${params.toString()}`);
if (res.ok) {
state.items = await res.json();
- renderApp();
- renderManageQuickPicker();
+ if (state.currentRoute === '/') {
+ renderStockView();
+ } else {
+ renderTabletProductPicker();
+ }
}
} catch (err) {
- console.error('Failed to load items:', err);
+ console.error('Failed to load inventory:', err);
showToast('Failed to load inventory', 'error');
}
}
// ----------------------------------------------------
-// Navigation Tab Switching (Main -> Scan -> Manage)
-// ----------------------------------------------------
-
-function switchTab(tab) {
- state.currentTab = tab;
-
- const mainBtn = document.getElementById('nav-main-btn');
- const manageBtn = document.getElementById('nav-manage-btn');
- const mainView = document.getElementById('main-tab-view');
- const manageView = document.getElementById('manage-tab-view');
-
- 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;
- mainView.classList.remove('hidden');
- manageView.classList.add('hidden');
- renderApp();
- } else if (tab === 'manage') {
- manageBtn.className = activeClasses;
- mainBtn.className = inactiveClasses;
- manageView.classList.remove('hidden');
- mainView.classList.add('hidden');
- renderManageQuickPicker();
- }
-
- initLucide();
-}
-
-// ----------------------------------------------------
-// Filters & Search (Main Tab)
+// ROUTE 1: / (CURRENT STOCK DISPLAY)
// ----------------------------------------------------
let searchDebounceTimer = null;
@@ -200,22 +229,10 @@ function renderCategoryFilters() {
container.innerHTML = html;
}
-function populateModalSelects() {
- const catSelect = document.getElementById('modal-category');
- if (catSelect && state.meta.categories) {
- catSelect.innerHTML = state.meta.categories.map(c => `
-
- `).join('');
- }
-}
-
-// ----------------------------------------------------
-// UI Rendering (Main Tab: Fridge / Freezer Cards)
-// ----------------------------------------------------
-
-function renderApp() {
- const emptyState = document.getElementById('empty-state');
- const container = document.getElementById('fridge-view-content');
+function renderStockView() {
+ const container = document.getElementById('stock-grid');
+ const emptyState = document.getElementById('empty-stock');
+ if (!container) return;
if (state.items.length === 0) {
emptyState.classList.remove('hidden');
@@ -236,16 +253,16 @@ function renderApp() {
else if (percent <= 50) progressBarColor = 'bg-amber-500';
return `
-
+
-
+
${escapeHtml(item.brand || item.category)}
- ${isOpened ? 'Opened / In Use' : 'Full Pack'}
+ ${isOpened ? 'Opened' : 'Full Pack'}
@@ -265,7 +282,7 @@ function renderApp() {
-
+
@@ -282,12 +299,6 @@ function renderApp() {
-
-
-
`;
}).join('');
@@ -296,11 +307,31 @@ function renderApp() {
}
// ----------------------------------------------------
-// MANAGE TAB: 3-Step Flow (Scan/Search -> Add/Remove -> Packet/Amount)
+// ROUTE 2: /manage (TABLET STATION WORKFLOW)
// ----------------------------------------------------
-function renderManageQuickPicker(filterQuery = '') {
- const container = document.getElementById('manage-quick-picker');
+function resetTabletManageStation() {
+ state.manage.selectedItem = null;
+ state.manage.action = null;
+ document.getElementById('tablet-step-select').classList.remove('hidden');
+ document.getElementById('tablet-step-action').classList.add('hidden');
+ document.getElementById('tablet-search-input').value = '';
+}
+
+function handleTabletSearch(val) {
+ renderTabletProductPicker(val);
+
+ const clean = val.trim();
+ if (/^\d{8,14}$/.test(clean)) {
+ const found = state.items.find(i => i.barcode === clean);
+ if (found) {
+ forwardToProductAction(found);
+ }
+ }
+}
+
+function renderTabletProductPicker(filterQuery = '') {
+ const container = document.getElementById('tablet-product-picker');
if (!container) return;
const query = filterQuery.toLowerCase().trim();
@@ -314,7 +345,7 @@ function renderManageQuickPicker(filterQuery = '') {
if (matched.length === 0) {
container.innerHTML = `
- No matching items found. Scan barcode or add as new product!
+ No matching product found. Scan barcode or tap "Add Manually"!
`;
return;
@@ -322,8 +353,8 @@ function renderManageQuickPicker(filterQuery = '') {
container.innerHTML = matched.map(item => `