kitchen / index.html
bala41's picture
Add 2 files
5bd00e7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kitchen Stock - Track Out-of-Stock Items</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Poppins', sans-serif;
background-color: #f8fafc;
}
.gradient-bg {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
.item-card {
transition: all 0.3s ease;
}
.item-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(74, 222, 128, 0.7);
}
70% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba(74, 222, 128, 0);
}
100% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(74, 222, 128, 0);
}
}
</style>
</head>
<body class="min-h-screen gradient-bg">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<header class="flex justify-between items-center mb-8">
<div class="flex items-center">
<div class="w-12 h-12 rounded-full bg-white shadow-md flex items-center justify-center mr-4">
<i class="fas fa-utensils text-2xl text-emerald-500"></i>
</div>
<h1 class="text-3xl font-bold text-gray-800">Kitchen Stock</h1>
</div>
<button id="addItemBtn" class="bg-emerald-500 hover:bg-emerald-600 text-white px-6 py-2 rounded-full shadow-md flex items-center transition-all">
<i class="fas fa-plus mr-2"></i> Add Item
</button>
</header>
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-emerald-100 text-emerald-600 mr-4">
<i class="fas fa-box-open text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Out of Stock</p>
<h3 class="text-2xl font-bold" id="outOfStockCount">0</h3>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4">
<i class="fas fa-list text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Total Items</p>
<h3 class="text-2xl font-bold" id="totalItemsCount">0</h3>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-purple-100 text-purple-600 mr-4">
<i class="fas fa-history text-xl"></i>
</div>
<div>
<p class="text-gray-500 text-sm">Recently Added</p>
<h3 class="text-2xl font-bold" id="recentItemsCount">0</h3>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="bg-white rounded-xl shadow-md overflow-hidden">
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
<h2 class="text-xl font-semibold text-gray-800">Out of Stock Items</h2>
<div class="relative">
<input type="text" id="searchInput" placeholder="Search items..." class="pl-10 pr-4 py-2 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent">
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
</div>
<div class="p-4" id="itemsContainer">
<!-- Empty state -->
<div id="emptyState" class="text-center py-12">
<div class="w-24 h-24 mx-auto mb-4 rounded-full bg-gray-100 flex items-center justify-center">
<i class="fas fa-box-open text-3xl text-gray-400"></i>
</div>
<h3 class="text-xl font-medium text-gray-700 mb-2">No out-of-stock items</h3>
<p class="text-gray-500 mb-6">Add items that are out of stock to keep track</p>
<button id="addFirstItemBtn" class="bg-emerald-500 hover:bg-emerald-600 text-white px-6 py-2 rounded-full shadow-md inline-flex items-center transition-all">
<i class="fas fa-plus mr-2"></i> Add Your First Item
</button>
</div>
<!-- Items will be added here dynamically -->
</div>
</div>
</div>
<!-- Add Item Modal -->
<div id="addItemModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-xl shadow-xl w-full max-w-md mx-4">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold text-gray-800">Add Out-of-Stock Item</h3>
<button id="closeModalBtn" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<form id="addItemForm">
<div class="mb-4">
<label for="itemName" class="block text-sm font-medium text-gray-700 mb-1">Item Name</label>
<input type="text" id="itemName" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent" placeholder="e.g. Milk, Eggs, Bread" required>
</div>
<div class="mb-4">
<label for="itemCategory" class="block text-sm font-medium text-gray-700 mb-1">Category</label>
<select id="itemCategory" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent">
<option value="Dairy">Dairy</option>
<option value="Produce">Produce</option>
<option value="Pantry">Pantry</option>
<option value="Meat">Meat</option>
<option value="Beverages">Beverages</option>
<option value="Frozen">Frozen</option>
<option value="Other">Other</option>
</select>
</div>
<div class="mb-4">
<label for="itemNotes" class="block text-sm font-medium text-gray-700 mb-1">Notes (Optional)</label>
<textarea id="itemNotes" rows="3" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent" placeholder="Any specific details..."></textarea>
</div>
<div class="flex justify-end space-x-3">
<button type="button" id="cancelAddBtn" class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-all">
Cancel
</button>
<button type="submit" class="px-6 py-2 bg-emerald-500 hover:bg-emerald-600 text-white rounded-lg shadow-md transition-all flex items-center">
<i class="fas fa-save mr-2"></i> Save Item
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Restock Confirmation Modal -->
<div id="restockModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-xl shadow-xl w-full max-w-md mx-4">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold text-gray-800">Item Restocked</h3>
<button id="closeRestockModalBtn" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<p class="mb-6 text-gray-700">Are you sure you want to mark <span id="restockItemName" class="font-semibold"></span> as restocked? This will remove it from your out-of-stock list.</p>
<input type="hidden" id="restockItemId">
<div class="flex justify-end space-x-3">
<button type="button" id="cancelRestockBtn" class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-all">
Cancel
</button>
<button type="button" id="confirmRestockBtn" class="px-6 py-2 bg-emerald-500 hover:bg-emerald-600 text-white rounded-lg shadow-md transition-all flex items-center">
<i class="fas fa-check mr-2"></i> Confirm Restock
</button>
</div>
</div>
</div>
</div>
<script>
// Initialize with empty arrays if localStorage is empty
let items = JSON.parse(localStorage.getItem('kitchenStockItems')) || [];
let recentItems = JSON.parse(localStorage.getItem('recentKitchenItems')) || [];
// Clear recent items if no items exist
if (items.length === 0) {
recentItems = [];
localStorage.setItem('recentKitchenItems', JSON.stringify(recentItems));
}
// DOM Elements
const itemsContainer = document.getElementById('itemsContainer');
const emptyState = document.getElementById('emptyState');
const addItemBtn = document.getElementById('addItemBtn');
const addFirstItemBtn = document.getElementById('addFirstItemBtn');
const addItemModal = document.getElementById('addItemModal');
const closeModalBtn = document.getElementById('closeModalBtn');
const cancelAddBtn = document.getElementById('cancelAddBtn');
const addItemForm = document.getElementById('addItemForm');
const searchInput = document.getElementById('searchInput');
const restockModal = document.getElementById('restockModal');
const closeRestockModalBtn = document.getElementById('closeRestockModalBtn');
const cancelRestockBtn = document.getElementById('cancelRestockBtn');
const confirmRestockBtn = document.getElementById('confirmRestockBtn');
const restockItemName = document.getElementById('restockItemName');
const restockItemId = document.getElementById('restockItemId');
const outOfStockCount = document.getElementById('outOfStockCount');
const totalItemsCount = document.getElementById('totalItemsCount');
const recentItemsCount = document.getElementById('recentItemsCount');
// Event Listeners
addItemBtn.addEventListener('click', openAddItemModal);
addFirstItemBtn.addEventListener('click', openAddItemModal);
closeModalBtn.addEventListener('click', closeAddItemModal);
cancelAddBtn.addEventListener('click', closeAddItemModal);
addItemForm.addEventListener('submit', handleAddItem);
searchInput.addEventListener('input', filterItems);
closeRestockModalBtn.addEventListener('click', closeRestockModal);
cancelRestockBtn.addEventListener('click', closeRestockModal);
confirmRestockBtn.addEventListener('click', handleRestock);
// Initialize the app
document.addEventListener('DOMContentLoaded', init);
function init() {
renderItems();
updateStats();
}
// Render all items
function renderItems(filteredItems = null) {
const itemsToRender = filteredItems || items;
if (itemsToRender.length === 0) {
emptyState.classList.remove('hidden');
itemsContainer.innerHTML = '';
itemsContainer.appendChild(emptyState);
return;
}
emptyState.classList.add('hidden');
itemsContainer.innerHTML = '';
itemsToRender.forEach((item, index) => {
const itemCard = document.createElement('div');
itemCard.className = 'item-card bg-white rounded-lg shadow-sm p-4 mb-3 border-l-4 border-red-500 hover:shadow-md';
itemCard.innerHTML = `
<div class="flex justify-between items-start">
<div>
<h3 class="font-semibold text-lg text-gray-800">${item.name}</h3>
<div class="flex items-center mt-1">
<span class="text-xs px-2 py-1 rounded-full ${getCategoryColor(item.category)} mr-2">${item.category}</span>
<span class="text-xs text-gray-500"><i class="far fa-clock mr-1"></i> ${formatDate(item.dateAdded)}</span>
</div>
${item.notes ? `<p class="text-sm text-gray-600 mt-2">${item.notes}</p>` : ''}
</div>
<button class="restock-btn px-3 py-1 bg-emerald-500 hover:bg-emerald-600 text-white text-sm rounded-full transition-all" data-id="${index}">
<i class="fas fa-check mr-1"></i> Restocked
</button>
</div>
`;
itemsContainer.appendChild(itemCard);
});
// Add event listeners to all restock buttons
document.querySelectorAll('.restock-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const itemId = e.currentTarget.getAttribute('data-id');
openRestockModal(itemId);
});
});
}
// Get category color class
function getCategoryColor(category) {
const colors = {
'Dairy': 'bg-blue-100 text-blue-800',
'Produce': 'bg-green-100 text-green-800',
'Pantry': 'bg-yellow-100 text-yellow-800',
'Meat': 'bg-red-100 text-red-800',
'Beverages': 'bg-indigo-100 text-indigo-800',
'Frozen': 'bg-purple-100 text-purple-800',
'Other': 'bg-gray-100 text-gray-800'
};
return colors[category] || 'bg-gray-100 text-gray-800';
}
// Format date
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
}
// Open Add Item Modal
function openAddItemModal() {
addItemModal.classList.remove('hidden');
document.getElementById('itemName').focus();
}
// Close Add Item Modal
function closeAddItemModal() {
addItemModal.classList.add('hidden');
addItemForm.reset();
}
// Open Restock Modal
function openRestockModal(itemId) {
const item = items[itemId];
restockItemName.textContent = item.name;
restockItemId.value = itemId;
restockModal.classList.remove('hidden');
}
// Close Restock Modal
function closeRestockModal() {
restockModal.classList.add('hidden');
}
// Handle Add Item Form Submission
function handleAddItem(e) {
e.preventDefault();
const name = document.getElementById('itemName').value.trim();
const category = document.getElementById('itemCategory').value;
const notes = document.getElementById('itemNotes').value.trim();
const newItem = {
name,
category,
notes,
dateAdded: new Date().toISOString()
};
// Add to items list
items.unshift(newItem);
// Add to recent items (only if not already there)
if (!recentItems.some(item => item.name === newItem.name && item.dateAdded === newItem.dateAdded)) {
recentItems.unshift(newItem);
// Keep only the 5 most recent items
if (recentItems.length > 5) {
recentItems = recentItems.slice(0, 5);
}
}
saveItems();
renderItems();
updateStats();
closeAddItemModal();
// Show success animation
const successIcon = document.createElement('div');
successIcon.className = 'fixed bottom-6 right-6 w-16 h-16 rounded-full bg-emerald-500 flex items-center justify-center text-white text-2xl shadow-lg pulse';
successIcon.innerHTML = '<i class="fas fa-check"></i>';
document.body.appendChild(successIcon);
setTimeout(() => {
successIcon.classList.remove('pulse');
setTimeout(() => {
successIcon.remove();
}, 300);
}, 2000);
}
// Handle Restock
function handleRestock() {
const itemId = restockItemId.value;
const restockedItem = items[itemId];
// Remove from items list
items.splice(itemId, 1);
// Add to recent items (only if not already there)
if (!recentItems.some(item => item.name === restockedItem.name && item.dateAdded === restockedItem.dateAdded)) {
recentItems.unshift(restockedItem);
// Keep only the 5 most recent items
if (recentItems.length > 5) {
recentItems = recentItems.slice(0, 5);
}
}
saveItems();
renderItems();
updateStats();
closeRestock
</html>