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:
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:
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:
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:
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:
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
Navigate to the Products page
Enter the product name (e.g., “Espresso”)
Enter the price (e.g., “150”)
Click “Add Product”
Product appears immediately in the grid
Success toast notification appears
Form fields are cleared for next entry
Locate the product card in the grid
Click the red trash icon button
Confirm deletion in the dialog
Product is removed from database
Grid updates automatically
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
Consistent Naming : Use clear, descriptive product names
Accurate Pricing : Double-check prices before adding
Regular Cleanup : Remove discontinued products to keep inventory current
Backup Data : Use the export features in Reports for data backup
Related Pages