Skip to main content
The Product Management module allows you to maintain your inventory by adding new products, viewing existing items, and removing discontinued products.

Overview

Product management is designed for simplicity and speed. Each product has two essential properties:
  • Name: The product identifier (e.g., “Coffee”, “Sandwich”)
  • Price: The selling price in rupees

Simple Form

Add products with just a name and price

Instant Updates

Products appear immediately in the POS interface

Visual Grid

View all products in an organized card layout

One-Click Delete

Remove products with confirmation dialog

Adding Products

The product form is straightforward and validates input before saving:
js/products.js
import { addProduct, getProducts, deleteProduct } from './db.js';
import { money, showToast } from './shared.js';

const productForm = document.getElementById('productForm');
const pName = document.getElementById('pName');
const pPrice = document.getElementById('pPrice');

productForm.addEventListener('submit', async (e) => {
  e.preventDefault();
  await addProduct(pName.value.trim(), pPrice.value);
  showToast('Product added successfully');
  pName.value = '';
  pPrice.value = '';
  renderProducts();
});
Product names are trimmed to remove leading/trailing whitespace, and prices are automatically converted to numbers during storage.

Displaying Products

Products are rendered as cards with hover effects for better user experience:
js/products.js
async function renderProducts() {
  const products = await getProducts();
  
  if (products.length === 0) {
    productList.innerHTML = '';
    emptyState.classList.remove('hidden');
    return;
  }

  emptyState.classList.add('hidden');
  productList.innerHTML = products.map(p => `
    <div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition">
      <div class="flex items-start justify-between mb-2">
        <h3 class="font-semibold text-gray-900">${p.name}</h3>
        <button data-id="${p.id}" class="del-btn text-red-600 hover:text-red-700">
          <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
          </svg>
        </button>
      </div>
      <div class="text-2xl font-bold text-indigo-600">₹${money(p.price)}</div>
    </div>
  `).join('');

  // Attach delete handlers
  document.querySelectorAll('.del-btn').forEach(btn => {
    btn.addEventListener('click', async (e) => {
      if (confirm('Delete this product?')) {
        await deleteProduct(btn.dataset.id);
        showToast('Product deleted');
        renderProducts();
      }
    });
  });
}

Empty State Handling

When no products exist, the system displays a helpful empty state message:
js/products.js
if (products.length === 0) {
  productList.innerHTML = '';
  emptyState.classList.remove('hidden');
  return;
}
The empty state encourages users to add their first product, improving the onboarding experience.

Deleting Products

Deletion is protected by a confirmation dialog to prevent accidental removal:
js/products.js
document.querySelectorAll('.del-btn').forEach(btn => {
  btn.addEventListener('click', async (e) => {
    if (confirm('Delete this product?')) {
      await deleteProduct(btn.dataset.id);
      showToast('Product deleted');
      renderProducts();
    }
  });
});

Database Integration

Products are stored in IndexedDB with auto-incrementing IDs:
js/db.js
export async function addProduct(name, price) {
  return tx('products', 'readwrite', (s) => s.add({ name, price: Number(price) }));
}

export async function getProducts() {
  return tx('products', 'readonly', (s) => {
    return new Promise((resolve) => {
      const items = [];
      s.openCursor().onsuccess = (e) => {
        const cur = e.target.result;
        if (cur) { items.push(cur.value); cur.continue(); }
        else resolve(items);
      };
    });
  });
}

export async function deleteProduct(id) {
  return tx('products', 'readwrite', (s) => s.delete(Number(id)));
}
The database automatically assigns unique IDs to products using IndexedDB’s autoIncrement feature.

User Workflow

  1. Navigate to the Products page
  2. Enter the product name (e.g., “Espresso”)
  3. Enter the price (e.g., “150”)
  4. Click “Add Product”
  5. Product appears immediately in the grid
  6. Success toast notification appears
  7. Form fields are cleared for next entry
  1. Locate the product card in the grid
  2. Click the red trash icon button
  3. Confirm deletion in the dialog
  4. Product is removed from database
  5. Grid updates automatically
  6. Deletion confirmation toast appears

Validation

  • Name: Must not be empty (trimmed automatically)
  • Price: Must be a valid number
  • Deletion: Requires user confirmation

UI Features

  • Responsive Grid: Adapts to screen size for optimal viewing
  • Hover Effects: Cards gain shadow on hover for visual feedback
  • Color-Coded Actions: Delete buttons are red for clear indication
  • Toast Notifications: Confirm successful operations
  • Currency Formatting: Prices always display with 2 decimal places
Products added here immediately become available in the POS interface for sale processing.

Best Practices

  1. Consistent Naming: Use clear, descriptive product names
  2. Accurate Pricing: Double-check prices before adding
  3. Regular Cleanup: Remove discontinued products to keep inventory current
  4. Backup Data: Use the export features in Reports for data backup

Build docs developers (and LLMs) love