Build and Push Fridger Container / Build & Push Fridger Image (push) Successful in 1m53s
667 lines
36 KiB
HTML
667 lines
36 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en" class="h-full bg-slate-950 text-slate-100">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<title>Fridger - Current Stock & Manager</title>
|
||
|
||
<!-- Tailwind CSS -->
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script>
|
||
tailwind.config = {
|
||
darkMode: 'class',
|
||
theme: {
|
||
extend: {
|
||
colors: {
|
||
brand: {
|
||
400: '#2dd4bf',
|
||
500: '#14b8a6',
|
||
600: '#0d9488',
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<!-- Lucide Icons -->
|
||
<script src="https://unpkg.com/lucide@latest"></script>
|
||
|
||
<!-- HTML5 QR Code Scanner -->
|
||
<script src="https://unpkg.com/html5-qrcode" type="text/javascript"></script>
|
||
|
||
<link rel="stylesheet" href="/styles.css">
|
||
</head>
|
||
<body class="h-full flex flex-col bg-slate-950 text-slate-100 antialiased selection:bg-teal-500 selection:text-white select-none">
|
||
|
||
<!-- TOP HEADER -->
|
||
<header class="sticky top-0 z-30 bg-slate-900/95 backdrop-blur-md border-b border-slate-800 shadow-md">
|
||
<div class="max-w-4xl mx-auto px-3 sm:px-6">
|
||
<div class="flex items-center justify-between h-16 sm:h-20 gap-3">
|
||
|
||
<!-- Logo & Title -->
|
||
<a href="/" onclick="navigateTo('/'); return false;" class="flex items-center gap-2.5 shrink-0 group">
|
||
<div class="w-10 h-10 sm:w-11 sm:h-11 rounded-2xl bg-gradient-to-tr from-teal-500 to-cyan-400 flex items-center justify-center shadow-lg shadow-teal-500/20 text-white font-black text-xl group-hover:scale-105 transition-transform">
|
||
🧊
|
||
</div>
|
||
<div>
|
||
<h1 class="font-extrabold text-lg sm:text-xl leading-none bg-gradient-to-r from-teal-300 to-cyan-300 bg-clip-text text-transparent">Fridger</h1>
|
||
<p id="page-subtitle" class="text-[11px] text-slate-400 font-medium">Current Stock</p>
|
||
</div>
|
||
</a>
|
||
|
||
<!-- TOP NAVIGATION SWITCHER -->
|
||
<div class="flex items-center bg-slate-950 p-1.5 rounded-2xl border border-slate-800 shadow-inner gap-1">
|
||
<!-- 1. Current Stock (/) -->
|
||
<button
|
||
id="nav-stock-btn"
|
||
onclick="navigateTo('/')"
|
||
class="flex items-center justify-center gap-2 px-4 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]"
|
||
>
|
||
<i data-lucide="layout-grid" class="w-4 h-4 sm:w-5 sm:h-5"></i>
|
||
<span>Current Stock</span>
|
||
</button>
|
||
|
||
<!-- 2. Manager (/manage) -->
|
||
<button
|
||
id="nav-manage-btn"
|
||
onclick="navigateTo('/manage')"
|
||
class="flex items-center justify-center gap-2 px-4 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]"
|
||
>
|
||
<i data-lucide="sliders-horizontal" class="w-4 h-4 sm:w-5 sm:h-5 text-teal-400"></i>
|
||
<span>Manager</span>
|
||
</button>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ======================================================== -->
|
||
<!-- 1. ROOT VIEW: / (CURRENT STOCK ONLY) -->
|
||
<!-- ======================================================== -->
|
||
<div id="stock-view-page" class="flex-1 flex flex-col">
|
||
|
||
<!-- Quick Stock Summary Bar -->
|
||
<section class="bg-slate-900/50 border-b border-slate-800/60 py-2 sm:py-2.5">
|
||
<div class="max-w-4xl mx-auto px-3 sm:px-6">
|
||
<div class="flex items-center justify-between text-xs sm:text-sm text-slate-400 overflow-x-auto gap-4 scrollbar-none">
|
||
<div class="flex items-center gap-2 shrink-0">
|
||
<span class="w-2.5 h-2.5 rounded-full bg-teal-400"></span>
|
||
<span>Total Stock: <strong id="stat-total" class="text-white font-black text-sm sm:text-base">0</strong> items</span>
|
||
</div>
|
||
<div class="flex items-center gap-2 shrink-0">
|
||
<span class="w-2.5 h-2.5 rounded-full bg-emerald-400"></span>
|
||
<span>Full Packs: <strong id="stat-full" class="text-emerald-300 font-bold">0</strong></span>
|
||
</div>
|
||
<div class="flex items-center gap-2 shrink-0">
|
||
<span class="w-2.5 h-2.5 rounded-full bg-indigo-400"></span>
|
||
<span>Opened / Partial: <strong id="stat-opened" class="text-indigo-300 font-bold">0</strong></span>
|
||
</div>
|
||
<div class="flex items-center gap-2 shrink-0">
|
||
<span class="w-2.5 h-2.5 rounded-full bg-amber-400"></span>
|
||
<span>Low (≤25%): <strong id="stat-low" class="text-amber-300 font-bold">0</strong></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Stock Search & Filter Trigger -->
|
||
<main class="flex-1 max-w-4xl mx-auto px-3 sm:px-6 py-4 w-full space-y-3">
|
||
<div class="space-y-2">
|
||
<div class="flex gap-2.5 items-center">
|
||
<div class="relative flex-1">
|
||
<i data-lucide="search" class="w-4 h-4 text-slate-400 absolute left-3.5 top-1/2 -translate-y-1/2"></i>
|
||
<input
|
||
type="text"
|
||
id="search-input"
|
||
placeholder="Search current stock by name, brand, or barcode..."
|
||
oninput="handleSearch(this.value)"
|
||
class="w-full bg-slate-900 border border-slate-800 focus:border-teal-500 rounded-xl pl-9 pr-9 py-2.5 text-sm sm:text-base text-slate-100 placeholder-slate-500 outline-none transition"
|
||
>
|
||
<button id="clear-search" onclick="clearSearch()" class="hidden absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-white p-1">
|
||
<i data-lucide="x" class="w-4 h-4"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Filter Trigger Button (Opens Filter Overlay) -->
|
||
<button
|
||
id="filter-trigger-btn"
|
||
onclick="openFilterOverlay()"
|
||
class="px-4 py-2.5 rounded-xl bg-slate-900 hover:bg-slate-800 text-slate-300 border border-slate-800 text-xs sm:text-sm font-bold flex items-center gap-2 shrink-0 min-h-[42px] touch-manipulation transition shadow-sm active:scale-95"
|
||
>
|
||
<i data-lucide="filter" class="w-4 h-4 text-teal-400"></i>
|
||
<span>Filter</span>
|
||
<span id="stock-filter-badge" class="hidden px-1.5 py-0.5 text-[10px] font-black rounded-full bg-teal-500 text-white leading-none">0</span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Active Filter Indicator Banner -->
|
||
<div id="active-filter-indicator" class="hidden flex items-center justify-between gap-2 p-2 px-3 bg-teal-950/50 border border-teal-500/40 rounded-xl text-xs animate-scale-up">
|
||
<div class="flex items-center gap-2 min-w-0">
|
||
<i data-lucide="filter" class="w-3.5 h-3.5 text-teal-400 shrink-0"></i>
|
||
<span id="active-filter-text" class="text-teal-200 truncate font-medium">Filtered</span>
|
||
</div>
|
||
<div class="flex items-center gap-2 shrink-0">
|
||
<button onclick="openFilterOverlay()" class="text-teal-300 hover:text-white text-xs font-bold underline">
|
||
Change
|
||
</button>
|
||
<span class="text-slate-600">•</span>
|
||
<button onclick="resetAllFilters()" class="text-slate-400 hover:text-rose-300 text-xs font-medium">
|
||
Reset
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Current Stock Grid (Display only) -->
|
||
<div id="stock-grid" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3.5 sm:gap-4 pt-1">
|
||
<!-- Rendered items -->
|
||
</div>
|
||
|
||
<!-- Empty Stock State -->
|
||
<div id="empty-stock" class="hidden text-center py-16 px-4">
|
||
<div class="w-16 h-16 sm:w-20 sm:h-20 rounded-2xl bg-slate-900 text-slate-500 flex items-center justify-center mx-auto mb-3 border border-slate-800 text-3xl">
|
||
🧊
|
||
</div>
|
||
<h3 class="text-lg font-bold text-white mb-1">No items match your filter</h3>
|
||
<p class="text-xs sm:text-sm text-slate-400 max-w-xs mx-auto mb-6">Try resetting filters or adding items via Manager.</p>
|
||
<div class="flex items-center justify-center gap-3">
|
||
<button onclick="resetAllFilters()" class="px-4 py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-white text-xs sm:text-sm font-bold active:scale-95 touch-manipulation">
|
||
Reset Filters
|
||
</button>
|
||
<button onclick="navigateTo('/manage')" class="px-5 py-2.5 rounded-xl bg-teal-500 hover:bg-teal-400 text-white text-xs sm:text-sm font-bold flex items-center gap-2 shadow-lg shadow-teal-500/20 active:scale-95 touch-manipulation">
|
||
<i data-lucide="sliders-horizontal" class="w-4 h-4"></i>
|
||
<span>Go to Manager</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
</div>
|
||
|
||
<!-- ======================================================== -->
|
||
<!-- FILTER OVERLAY MODAL -->
|
||
<!-- ======================================================== -->
|
||
<div id="filter-modal" class="fixed inset-0 z-50 bg-black/85 backdrop-blur-md hidden flex items-center justify-center p-3 sm:p-5 overflow-y-auto">
|
||
<div class="bg-slate-900 border border-slate-800 rounded-3xl max-w-2xl w-full p-5 sm:p-7 shadow-2xl space-y-6 my-auto animate-scale-up">
|
||
|
||
<!-- Top Title Bar -->
|
||
<div class="flex items-center justify-between pb-1 border-b border-slate-800/80">
|
||
<div>
|
||
<h3 class="text-lg sm:text-xl font-black text-white">Filter Stock</h3>
|
||
<p class="text-xs text-slate-400 mt-0.5">Filter inventory by condition or category</p>
|
||
</div>
|
||
<div class="flex items-center gap-2">
|
||
<button
|
||
onclick="resetAllFilters()"
|
||
class="px-3.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white text-xs font-bold transition active:scale-95 touch-manipulation border border-slate-700/60"
|
||
>
|
||
Reset
|
||
</button>
|
||
<button onclick="closeFilterOverlay()" class="text-slate-400 hover:text-white p-2 rounded-xl bg-slate-800/80 touch-manipulation hover:bg-slate-700 transition">
|
||
<i data-lucide="x" class="w-5 h-5"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Section 1: Pack States (Spacious 4-Grid, completely visible) -->
|
||
<div class="space-y-2.5">
|
||
<label class="text-xs font-bold text-teal-400 uppercase tracking-wider block">1. Pack State & Condition</label>
|
||
|
||
<div class="grid grid-cols-2 sm:grid-cols-4 gap-2.5">
|
||
<!-- All Pack States -->
|
||
<button
|
||
onclick="setStatusFilter('all')"
|
||
data-filter-status="all"
|
||
class="filter-status-card p-3.5 rounded-2xl border text-left flex flex-col justify-between space-y-2.5 transition-all active:scale-95 touch-manipulation bg-teal-500/20 border-teal-500 text-white shadow-lg shadow-teal-500/15 ring-2 ring-teal-500/30"
|
||
>
|
||
<div class="flex items-center justify-between">
|
||
<span class="w-8 h-8 rounded-xl bg-teal-500/20 text-teal-300 flex items-center justify-center">
|
||
<i data-lucide="layers" class="w-4 h-4"></i>
|
||
</span>
|
||
<span id="filter-count-all" class="text-xs font-bold px-2 py-0.5 rounded-md bg-slate-800 text-slate-200">0</span>
|
||
</div>
|
||
<div>
|
||
<h4 class="text-xs sm:text-sm font-extrabold text-white leading-tight">All Stock</h4>
|
||
<p class="text-[11px] text-slate-400 mt-0.5">Everything</p>
|
||
</div>
|
||
</button>
|
||
|
||
<!-- Full Packs -->
|
||
<button
|
||
onclick="setStatusFilter('full')"
|
||
data-filter-status="full"
|
||
class="filter-status-card p-3.5 rounded-2xl border text-left flex flex-col justify-between space-y-2.5 transition-all active:scale-95 touch-manipulation bg-slate-950 border-slate-800 text-slate-300 hover:border-slate-700"
|
||
>
|
||
<div class="flex items-center justify-between">
|
||
<span class="w-8 h-8 rounded-xl bg-emerald-500/20 text-emerald-400 flex items-center justify-center">
|
||
<i data-lucide="package-check" class="w-4 h-4"></i>
|
||
</span>
|
||
<span id="filter-count-full" class="text-xs font-bold px-2 py-0.5 rounded-md bg-slate-800 text-emerald-300">0</span>
|
||
</div>
|
||
<div>
|
||
<h4 class="text-xs sm:text-sm font-extrabold text-white leading-tight">Full Packs</h4>
|
||
<p class="text-[11px] text-slate-400 mt-0.5">Unopened</p>
|
||
</div>
|
||
</button>
|
||
|
||
<!-- Opened / In Use -->
|
||
<button
|
||
onclick="setStatusFilter('opened')"
|
||
data-filter-status="opened"
|
||
class="filter-status-card p-3.5 rounded-2xl border text-left flex flex-col justify-between space-y-2.5 transition-all active:scale-95 touch-manipulation bg-slate-950 border-slate-800 text-slate-300 hover:border-slate-700"
|
||
>
|
||
<div class="flex items-center justify-between">
|
||
<span class="w-8 h-8 rounded-xl bg-indigo-500/20 text-indigo-400 flex items-center justify-center">
|
||
<i data-lucide="package-open" class="w-4 h-4"></i>
|
||
</span>
|
||
<span id="filter-count-opened" class="text-xs font-bold px-2 py-0.5 rounded-md bg-slate-800 text-indigo-300">0</span>
|
||
</div>
|
||
<div>
|
||
<h4 class="text-xs sm:text-sm font-extrabold text-white leading-tight">Opened</h4>
|
||
<p class="text-[11px] text-slate-400 mt-0.5">In-Use / Partial</p>
|
||
</div>
|
||
</button>
|
||
|
||
<!-- Low Stock -->
|
||
<button
|
||
onclick="setStatusFilter('low')"
|
||
data-filter-status="low"
|
||
class="filter-status-card p-3.5 rounded-2xl border text-left flex flex-col justify-between space-y-2.5 transition-all active:scale-95 touch-manipulation bg-slate-950 border-slate-800 text-slate-300 hover:border-slate-700"
|
||
>
|
||
<div class="flex items-center justify-between">
|
||
<span class="w-8 h-8 rounded-xl bg-amber-500/20 text-amber-400 flex items-center justify-center">
|
||
<i data-lucide="alert-circle" class="w-4 h-4"></i>
|
||
</span>
|
||
<span id="filter-count-low" class="text-xs font-bold px-2 py-0.5 rounded-md bg-slate-800 text-amber-300">0</span>
|
||
</div>
|
||
<div>
|
||
<h4 class="text-xs sm:text-sm font-extrabold text-white leading-tight">Low Stock</h4>
|
||
<p class="text-[11px] text-slate-400 mt-0.5">≤25% left</p>
|
||
</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Section 2: Categories (2-Column touch grid with full unabbreviated names) -->
|
||
<div class="space-y-2.5">
|
||
<label class="text-xs font-bold text-teal-400 uppercase tracking-wider block">2. Category</label>
|
||
<div id="filter-categories-container" class="grid grid-cols-1 sm:grid-cols-2 gap-2.5 max-h-[300px] overflow-y-auto p-1 pr-1.5 scrollbar-thin">
|
||
<!-- Populated dynamically with full unabbreviated names -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Action: Big Prominent Done button -->
|
||
<div class="pt-2 border-t border-slate-800">
|
||
<button
|
||
onclick="closeFilterOverlay()"
|
||
class="w-full py-4 rounded-2xl bg-teal-500 hover:bg-teal-400 text-white font-black text-base sm:text-lg shadow-xl shadow-teal-500/25 active:scale-95 transition-all touch-manipulation flex items-center justify-center gap-2"
|
||
>
|
||
<i data-lucide="check" class="w-5 h-5 sm:w-6 sm:h-6"></i>
|
||
<span>Show Matching Stock (<span id="filter-total-count">0</span> items)</span>
|
||
</button>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ======================================================== -->
|
||
<!-- 2. MANAGER: /manage -->
|
||
<!-- ======================================================== -->
|
||
<div id="manage-view-page" class="hidden flex-1 flex flex-col">
|
||
|
||
<main class="flex-1 max-w-3xl mx-auto px-3 sm:px-6 py-4 sm:py-6 w-full space-y-4">
|
||
|
||
<!-- STEP 1: SCAN / SEARCH / ADD MANUALLY -->
|
||
<div id="tablet-step-select" class="bg-slate-900 border border-slate-800 rounded-3xl p-5 sm:p-7 shadow-xl space-y-5">
|
||
|
||
<div class="flex items-center justify-between gap-2">
|
||
<div>
|
||
<h2 class="text-lg sm:text-xl font-extrabold text-white">Manager</h2>
|
||
<p class="text-xs sm:text-sm text-slate-400">Scan barcode, search product, or add manually</p>
|
||
</div>
|
||
|
||
<!-- Add Manually Button -->
|
||
<button
|
||
onclick="openItemModal()"
|
||
class="px-4 py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-teal-300 border border-teal-500/30 text-xs sm:text-sm font-bold flex items-center gap-1.5 active:scale-95 touch-manipulation shadow-sm shrink-0"
|
||
>
|
||
<i data-lucide="plus" class="w-4 h-4"></i>
|
||
<span>Add Manually</span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Integrated Camera Scanner & Live Search Box -->
|
||
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3 pt-1">
|
||
<!-- Big Camera Scanner Touch Launch -->
|
||
<button
|
||
onclick="openScannerModal()"
|
||
class="sm:col-span-1 p-4 rounded-2xl bg-gradient-to-r from-teal-500 to-emerald-600 hover:from-teal-400 hover:to-emerald-500 text-white font-extrabold flex items-center justify-center gap-3 shadow-lg shadow-teal-500/25 active:scale-95 touch-manipulation min-h-[60px]"
|
||
>
|
||
<i data-lucide="scan-barcode" class="w-6 h-6"></i>
|
||
<span class="text-sm sm:text-base">Scan Barcode</span>
|
||
</button>
|
||
|
||
<!-- Search or Barcode Input -->
|
||
<div class="sm:col-span-2 relative flex items-center">
|
||
<i data-lucide="search" class="w-5 h-5 text-slate-400 absolute left-4"></i>
|
||
<input
|
||
type="text"
|
||
id="tablet-search-input"
|
||
placeholder="Search product name, brand, or barcode..."
|
||
oninput="handleTabletSearch(this.value)"
|
||
class="w-full bg-slate-950 border border-slate-800 focus:border-teal-500 rounded-2xl pl-12 pr-4 py-3.5 text-sm sm:text-base text-white placeholder-slate-500 outline-none transition min-h-[60px]"
|
||
>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Matching Search Results Container (Only shows when searching) -->
|
||
<div id="tablet-search-results-container" class="hidden space-y-2 pt-3 border-t border-slate-800">
|
||
<label class="text-xs font-bold text-teal-400 uppercase tracking-wider block">Search Results:</label>
|
||
<div id="tablet-product-picker" class="grid grid-cols-1 sm:grid-cols-2 gap-2.5 max-h-64 overflow-y-auto p-1 scrollbar-none">
|
||
<!-- Populated dynamically when user searches -->
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- STEP 2 & 3: PRODUCT ACTION SCREEN (ADD / REMOVE -> PACKET / AMOUNT) -->
|
||
<div id="tablet-step-action" class="hidden bg-slate-900 border border-teal-500/40 rounded-3xl p-5 sm:p-6 shadow-2xl space-y-5 animate-scale-up">
|
||
|
||
<!-- Product Header Banner -->
|
||
<div class="bg-slate-950 p-4 rounded-2xl border border-slate-800 flex items-center justify-between gap-3">
|
||
<div class="flex items-center gap-3.5 min-w-0">
|
||
<div id="action-product-img" class="w-14 h-14 rounded-xl bg-slate-800 flex items-center justify-center text-teal-400 shrink-0 overflow-hidden">
|
||
<i data-lucide="package" class="w-7 h-7"></i>
|
||
</div>
|
||
<div class="min-w-0">
|
||
<h3 id="action-product-name" class="font-extrabold text-white text-base sm:text-lg truncate">Selected Product</h3>
|
||
<p id="action-product-sub" class="text-xs sm:text-sm text-slate-400 mt-0.5">Current Stock: 250g • 1 Pack = 250g</p>
|
||
</div>
|
||
</div>
|
||
|
||
<button onclick="cancelActionAndReturnToManage()" class="text-slate-400 hover:text-white p-2 rounded-xl bg-slate-800/80 touch-manipulation">
|
||
<i data-lucide="x" class="w-5 h-5"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Choose Action: Add or Remove -->
|
||
<div class="space-y-2.5">
|
||
<label class="text-xs font-bold text-slate-400 uppercase tracking-wider block">Choose Action:</label>
|
||
<div class="grid grid-cols-2 gap-3 sm:gap-4">
|
||
<button
|
||
id="action-btn-add"
|
||
onclick="setActionType('add')"
|
||
class="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-[84px] bg-slate-950 border-slate-800 text-slate-300 hover:border-emerald-500/60 hover:text-white"
|
||
>
|
||
<i data-lucide="plus-circle" class="w-7 h-7 text-emerald-400"></i>
|
||
<span class="text-sm sm:text-base">➕ Add to Stock</span>
|
||
</button>
|
||
|
||
<button
|
||
id="action-btn-remove"
|
||
onclick="setActionType('remove')"
|
||
class="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-[84px] bg-slate-950 border-slate-800 text-slate-300 hover:border-rose-500/60 hover:text-white"
|
||
>
|
||
<i data-lucide="minus-circle" class="w-7 h-7 text-rose-400"></i>
|
||
<span class="text-sm sm:text-base">➖ Remove from Stock</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Choose Mode: Whole Packet vs Specific Grams -->
|
||
<div id="action-qty-section" class="hidden space-y-4 pt-2 border-t border-slate-800">
|
||
|
||
<div class="grid grid-cols-2 gap-2 p-1.5 bg-slate-950 rounded-2xl border border-slate-800">
|
||
<button
|
||
id="btn-mode-packet"
|
||
onclick="setActionQtyMode('packet')"
|
||
class="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"
|
||
>
|
||
<i data-lucide="package" class="w-4 h-4"></i>
|
||
<span>Whole Packet(s)</span>
|
||
</button>
|
||
<button
|
||
id="btn-mode-amount"
|
||
onclick="setActionQtyMode('amount')"
|
||
class="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"
|
||
>
|
||
<i data-lucide="scale" class="w-4 h-4"></i>
|
||
<span>Specific Grams / Amount</span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Packet Controls -->
|
||
<div id="controls-packet" class="bg-slate-950 p-4 sm:p-5 rounded-2xl border border-slate-800 space-y-4 text-center">
|
||
<div class="text-xs sm:text-sm text-slate-400 font-medium">
|
||
1 Full Packet = <strong id="action-pack-size-label" class="text-teal-300 font-bold">250g</strong>
|
||
</div>
|
||
|
||
<div class="flex items-center justify-center gap-4">
|
||
<button onclick="changeActionPackCount(-1)" class="w-12 h-12 rounded-2xl bg-slate-800 hover:bg-slate-700 text-white font-extrabold text-2xl flex items-center justify-center active:scale-95 touch-manipulation border border-slate-700">-</button>
|
||
<div class="text-3xl sm:text-4xl font-black text-white min-w-[90px]">
|
||
<span id="action-pack-count-val">1</span> <span class="text-sm font-normal text-slate-400">pack</span>
|
||
</div>
|
||
<button onclick="changeActionPackCount(1)" class="w-12 h-12 rounded-2xl bg-slate-800 hover:bg-slate-700 text-white font-extrabold text-2xl flex items-center justify-center active:scale-95 touch-manipulation border border-slate-700">+</button>
|
||
</div>
|
||
|
||
<div class="flex items-center justify-center gap-2">
|
||
<button onclick="setActionPackCount(1)" class="px-3.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs text-slate-200 border border-slate-700 font-bold">1 Pack</button>
|
||
<button onclick="setActionPackCount(2)" class="px-3.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs text-slate-200 border border-slate-700 font-bold">2 Packs</button>
|
||
<button onclick="setActionPackCount(3)" class="px-3.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs text-slate-200 border border-slate-700 font-bold">3 Packs</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Specific Grams Controls (Slider & Quick Chips) -->
|
||
<div id="controls-amount" class="hidden bg-slate-950 p-4 sm:p-5 rounded-2xl border border-slate-800 space-y-4 text-center">
|
||
<div>
|
||
<div class="text-3xl sm:text-4xl font-black text-white flex items-center justify-center gap-1.5">
|
||
<span id="action-amount-val">150</span>
|
||
<span id="action-amount-unit" class="text-base font-normal text-teal-400">g</span>
|
||
</div>
|
||
<div class="text-xs text-slate-400 mt-1 font-medium">New remaining amount in stock</div>
|
||
</div>
|
||
|
||
<input
|
||
type="range"
|
||
id="action-amount-slider"
|
||
min="0"
|
||
max="500"
|
||
step="5"
|
||
value="150"
|
||
oninput="onActionAmountSlider(this.value)"
|
||
class="w-full accent-teal-500 cursor-pointer h-3.5 bg-slate-800 rounded-xl"
|
||
>
|
||
|
||
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2">
|
||
<button onclick="applyActionDelta(-10)" class="py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs sm:text-sm text-slate-200 border border-slate-700 font-bold touch-manipulation">-10</button>
|
||
<button onclick="applyActionDelta(-25)" class="py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs sm:text-sm text-slate-200 border border-slate-700 font-bold touch-manipulation">-25</button>
|
||
<button onclick="applyActionDelta(-50)" class="py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs sm:text-sm text-slate-200 border border-slate-700 font-bold touch-manipulation">-50</button>
|
||
<button onclick="applyActionDelta(-100)" class="py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs sm:text-sm text-slate-200 border border-slate-700 font-bold touch-manipulation">-100</button>
|
||
<button onclick="setActionAmountRatio(0.5)" class="py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs sm:text-sm text-slate-200 border border-slate-700 font-bold touch-manipulation">Half</button>
|
||
<button onclick="setActionAmountRatio(0)" class="py-2.5 rounded-xl bg-rose-950/40 hover:bg-rose-900 text-xs sm:text-sm text-rose-300 border border-rose-800 font-bold touch-manipulation">0 (Empty)</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Confirm & Finish (Returns back to /manage) -->
|
||
<div class="flex items-center gap-3 pt-2">
|
||
<button
|
||
onclick="cancelActionAndReturnToManage()"
|
||
class="px-5 py-4 rounded-2xl bg-slate-800 hover:bg-slate-700 text-slate-300 font-bold text-sm touch-manipulation"
|
||
>
|
||
Cancel
|
||
</button>
|
||
<button
|
||
id="action-confirm-btn"
|
||
onclick="confirmAndFinishAction()"
|
||
class="flex-1 py-4 rounded-2xl bg-teal-500 hover:bg-teal-400 text-white font-black text-base sm:text-lg shadow-xl shadow-teal-500/25 active:scale-95 transition-all touch-manipulation flex items-center justify-center gap-2"
|
||
>
|
||
<i data-lucide="check" class="w-6 h-6"></i>
|
||
<span id="action-confirm-label">Confirm & Done</span>
|
||
</button>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</main>
|
||
|
||
</div>
|
||
|
||
<!-- ADD / EDIT ITEM MODAL -->
|
||
<div id="item-modal" class="fixed inset-0 z-50 bg-black/80 backdrop-blur-sm hidden flex items-center justify-center p-3 sm:p-4 overflow-y-auto">
|
||
<div class="bg-slate-900 border border-slate-800 rounded-3xl max-w-lg w-full p-5 sm:p-6 shadow-2xl space-y-4 my-8 animate-scale-up">
|
||
|
||
<div class="flex items-start justify-between">
|
||
<div>
|
||
<h3 id="item-modal-title" class="text-base sm:text-lg font-bold text-white">Add Product Manually</h3>
|
||
<p class="text-xs text-slate-400">Enter product name and package size</p>
|
||
</div>
|
||
<button onclick="closeItemModal()" class="text-slate-400 hover:text-white p-2 rounded-xl bg-slate-800/60 touch-manipulation">
|
||
<i data-lucide="x" class="w-5 h-5"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Barcode Lookup row -->
|
||
<div class="bg-slate-950 p-2.5 sm:p-3 rounded-2xl border border-slate-800 flex items-center gap-2">
|
||
<div class="relative flex-1">
|
||
<i data-lucide="barcode" class="w-4 h-4 text-slate-400 absolute left-3 top-1/2 -translate-y-1/2"></i>
|
||
<input
|
||
type="text"
|
||
id="modal-barcode"
|
||
placeholder="Barcode (e.g. 4008452023451)"
|
||
class="w-full bg-slate-900 border border-slate-800 focus:border-teal-500 rounded-xl pl-9 pr-3 py-2 text-xs sm:text-sm text-white outline-none"
|
||
>
|
||
</div>
|
||
<button type="button" onclick="lookupCurrentBarcode()" id="modal-barcode-lookup-btn" class="px-3.5 py-2 rounded-xl bg-teal-600 hover:bg-teal-500 text-white text-xs sm:text-sm font-bold shrink-0 min-h-[38px] touch-manipulation">
|
||
Lookup
|
||
</button>
|
||
<button type="button" onclick="openScannerModal(true)" class="p-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 shrink-0 min-h-[38px] min-w-[38px] flex items-center justify-center touch-manipulation" title="Scan with Camera">
|
||
<i data-lucide="camera" class="w-4 h-4"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<form id="item-form" onsubmit="handleItemFormSubmit(event)" class="space-y-3.5 text-xs sm:text-sm">
|
||
<input type="hidden" id="modal-item-id">
|
||
<input type="hidden" id="modal-image-url">
|
||
|
||
<div id="modal-image-container" class="hidden items-center gap-3 p-2.5 bg-slate-950 rounded-2xl border border-slate-800">
|
||
<img id="modal-image-preview" src="" alt="Product" class="w-12 h-12 object-contain rounded-xl bg-white/5">
|
||
<div class="flex-1 min-w-0">
|
||
<span class="text-xs text-teal-400 font-bold">Product Match</span>
|
||
<p id="modal-image-label" class="text-xs text-slate-400 truncate"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-1 sm:grid-cols-3 gap-2.5">
|
||
<div class="sm:col-span-2">
|
||
<label class="block text-slate-300 font-semibold mb-1">Product Name *</label>
|
||
<input type="text" id="modal-name" required placeholder="e.g. Frozen Berries" class="w-full bg-slate-950 border border-slate-800 focus:border-teal-500 rounded-xl px-3.5 py-2.5 text-white outline-none">
|
||
</div>
|
||
<div>
|
||
<label class="block text-slate-300 font-semibold mb-1">Brand</label>
|
||
<input type="text" id="modal-brand" placeholder="e.g. Bio" class="w-full bg-slate-950 border border-slate-800 focus:border-teal-500 rounded-xl px-3.5 py-2.5 text-white outline-none">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-3 gap-2.5">
|
||
<div>
|
||
<label class="block text-slate-300 font-semibold mb-1">Current Amount *</label>
|
||
<input type="number" id="modal-quantity" step="any" min="0" required placeholder="500" class="w-full bg-slate-950 border border-slate-800 focus:border-teal-500 rounded-xl px-3.5 py-2.5 text-white outline-none">
|
||
</div>
|
||
<div>
|
||
<label class="block text-slate-300 font-semibold mb-1">1 Full Pack Size *</label>
|
||
<input type="number" id="modal-initial-quantity" step="any" min="0" required placeholder="500" class="w-full bg-slate-950 border border-slate-800 focus:border-teal-500 rounded-xl px-3.5 py-2.5 text-white outline-none">
|
||
</div>
|
||
<div>
|
||
<label class="block text-slate-300 font-semibold mb-1">Unit</label>
|
||
<select id="modal-unit" class="w-full bg-slate-950 border border-slate-800 focus:border-teal-500 rounded-xl px-3.5 py-2.5 text-white outline-none">
|
||
<option value="g">Grams (g)</option>
|
||
<option value="ml">Milliliters (ml)</option>
|
||
<option value="pcs">Pieces (Stk)</option>
|
||
<option value="kg">Kilograms (kg)</option>
|
||
<option value="l">Liters (L)</option>
|
||
<option value="pack">Pack</option>
|
||
<option value="slices">Slices</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-slate-300 font-semibold mb-1">Category</label>
|
||
<select id="modal-category" class="w-full bg-slate-950 border border-slate-800 focus:border-teal-500 rounded-xl px-3.5 py-2.5 text-white outline-none">
|
||
<!-- Populated dynamically -->
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-slate-300 font-semibold mb-1">Notes (Optional)</label>
|
||
<input type="text" id="modal-notes" placeholder="e.g. In freezer top basket" class="w-full bg-slate-950 border border-slate-800 focus:border-teal-500 rounded-xl px-3.5 py-2.5 text-white outline-none">
|
||
</div>
|
||
|
||
<div class="flex items-center justify-end gap-3 pt-3 border-t border-slate-800">
|
||
<button type="button" onclick="closeItemModal()" class="px-5 py-3 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 text-sm font-medium touch-manipulation">Cancel</button>
|
||
<button type="submit" class="px-6 py-3 rounded-xl bg-teal-500 hover:bg-teal-400 text-white text-sm font-bold shadow-lg shadow-teal-500/20 active:scale-95 touch-manipulation">Save Product</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TABLET LIVE CAMERA BARCODE SCANNER MODAL -->
|
||
<div id="scanner-modal" class="fixed inset-0 z-50 bg-black/90 backdrop-blur-md hidden flex items-center justify-center p-3 sm:p-4">
|
||
<div class="bg-slate-900 border border-slate-800 rounded-3xl max-w-lg w-full overflow-hidden shadow-2xl flex flex-col animate-scale-up">
|
||
|
||
<div class="p-4 bg-slate-850 border-b border-slate-800 flex items-center justify-between">
|
||
<div class="flex items-center gap-2.5">
|
||
<div class="w-8 h-8 rounded-xl bg-teal-500/10 text-teal-400 flex items-center justify-center">
|
||
<i data-lucide="scan-barcode" class="w-5 h-5"></i>
|
||
</div>
|
||
<span class="font-extrabold text-white text-base">Barcode Scanner</span>
|
||
</div>
|
||
<button onclick="closeScannerModal()" class="text-slate-400 hover:text-white p-2 rounded-xl bg-slate-800/60 touch-manipulation">
|
||
<i data-lucide="x" class="w-5 h-5"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Camera Viewfinder -->
|
||
<div class="relative bg-black flex items-center justify-center min-h-[300px] sm:min-h-[360px] w-full overflow-hidden">
|
||
<div id="barcode-reader" class="w-full"></div>
|
||
<div id="scanner-overlay" class="absolute inset-0 pointer-events-none border-2 border-teal-400/50 rounded-3xl m-8 sm:m-12 flex items-center justify-center">
|
||
<div class="w-full h-1 bg-red-500/90 animate-pulse shadow-lg shadow-red-500"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Status & Manual fallback -->
|
||
<div class="p-4 bg-slate-900 space-y-3">
|
||
<div id="scanner-status" class="text-xs sm:text-sm text-center text-slate-400 font-medium">
|
||
Hold product barcode in front of camera
|
||
</div>
|
||
|
||
<div class="flex items-center gap-2">
|
||
<input
|
||
type="text"
|
||
id="scanner-manual-input"
|
||
placeholder="Or type barcode number manually..."
|
||
class="flex-1 bg-slate-950 border border-slate-800 focus:border-teal-500 rounded-xl px-3.5 py-2.5 text-xs sm:text-sm text-white outline-none"
|
||
onkeydown="if(event.key === 'Enter') handleManualBarcodeSubmit()"
|
||
>
|
||
<button onclick="handleManualBarcodeSubmit()" class="px-4 py-2.5 rounded-xl bg-teal-600 hover:bg-teal-500 text-white text-xs sm:text-sm font-bold min-h-[42px] touch-manipulation">
|
||
Lookup
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TOAST NOTIFICATION CONTAINER -->
|
||
<div id="toast-container" class="fixed bottom-5 right-5 z-50 flex flex-col gap-2 max-w-sm pointer-events-none"></div>
|
||
|
||
<!-- MAIN SCRIPT -->
|
||
<script src="/app.js"></script>
|
||
</body>
|
||
</html>
|