G
Grocery / Food / Medicine
White • Skyblue theme — Orders via WhatsApp
<nav>
<button class="nav-btn" id="nav-home">Home</button>
<button class="nav-btn" id="nav-camera">Camera</button>
<button class="nav-btn" id="nav-maps">Google Maps</button>
<button class="nav-btn" id="nav-orders">Orders</button>
</nav>
</header>
<main>
<section class="content" id="page-content">
<!-- Home / Category view will be rendered here -->
<div id="home-view">
<div class="category-bar" id="category-bar"></div>
<div id="category-title" style="font-weight:700;margin:8px 0;color:#025;">Select category</div>
<div class="grid" id="product-grid"></div>
</div>
<div id="camera-view" style="display:none;">
<h3>Camera (capture image)</h3>
<video id="camera-stream" autoplay playsinline style="width:100%; border-radius:8px; background:#000;"></video>
<div style="margin-top:8px; display:flex; gap:8px;">
<button class="btn btn-primary" id="capture-btn">Capture</button>
<button class="btn btn-ghost" id="stop-camera-btn">Stop Camera</button>
</div>
<div style="margin-top:12px;">
<canvas id="capture-canvas" style="display:none;"></canvas>
<img id="captured-img" style="max-width:100%; border-radius:8px; display:none; margin-top:8px;" />
</div>
</div>
<div id="maps-view" style="display:none;">
<h3>Open Google Maps</h3>
<div class="small">Enter address (or leave empty to open maps):</div>
<div style="margin-top:8px; display:flex; gap:8px;">
<input id="maps-address" placeholder="Delivery address or location" />
<button class="btn btn-primary" id="maps-open-btn">Open Maps</button>
</div>
</div>
<div id="orders-view" style="display:none;">
<h3>Saved Orders</h3>
<div class="orders-list" id="orders-list"></div>
<div class="small">Orders are stored locally in your browser. Use "Resend via WhatsApp" to re-open WhatsApp with the order text.</div>
</div>
</section>
<aside class="right">
<div class="order-panel">
<h3>Order Panel</h3>
<div class="small">Selected category: <span id="selected-cat">Groceries → Milk</span></div>
<div style="margin-top:10px;">
<div class="form-row">
<input id="cust-name" placeholder="Name" />
<input id="cust-phone" placeholder="Phone" />
</div>
<div style="margin-top:8px;">
<textarea id="cust-address" placeholder="Delivery address"></textarea>
</div>
</div>
<div style="margin-top:10px; font-weight:700;">Order items</div>
<div class="order-items" id="order-items"></div>
<div style="margin-top:8px;">
<button class="btn btn-primary" id="whatsapp-order">Order via WhatsApp</button>
<button class="btn btn-ghost" id="clear-order">Clear</button>
</div>
<div style="margin-top:12px;">
<div class="small">Captured image (optional)</div>
<img id="order-captured" style="width:100%; height:120px; object-fit:cover; border-radius:8px; display:none; margin-top:8px;" />
</div>
</div>
</aside>
</main>
<footer>
<div class="small">How it works: pick a category → click items to add → fill name/phone/address → click "Order via WhatsApp" → WhatsApp opens with the message. Camera can capture a picture attached to the order (image shown but note: the image isn't uploaded to server, only local data URL).</div>
<div style="margin-top:6px;" class="small">WhatsApp number used: <strong>+91 8977143043</strong></div>
</footer>
${it.name}
Add
`;
el.querySelector('button').onclick = () => {
basket.push({...it, category:selectedCategory});
renderBasket();
};
productGrid.appendChild(el);
});
if(items.length === 0){
productGrid.innerHTML = 'No items found.
';
}
}
/**********************
* Basket / Order UI
**********************/
function renderBasket(){
orderItemsDiv.innerHTML = '';
if(basket.length === 0){
orderItemsDiv.innerHTML = 'No items selected yet.
';
return;
}
basket.forEach((it, idx)=>{
const row = document.createElement('div');
row.className = 'order-item';
row.innerHTML = `
${it.name}
${it.category}
Remove
`;
row.querySelector('button').onclick = (e) => {
const i = Number(e.target.dataset.idx);
basket.splice(i,1);
renderBasket();
};
orderItemsDiv.appendChild(row);
});
}
clearBtn.onclick = () => {
basket = [];
custName.value = '';
custPhone.value = '';
custAddress.value = '';
capturedDataUrl = null;
localStorage.removeItem('capturedImage');
orderCapturedEl.style.display = 'none';
renderBasket();
};
/**********************
* WhatsApp order
**********************/
whatsappBtn.onclick = () => {
const name = custName.value.trim();
const phone = custPhone.value.trim();
const address = custAddress.value.trim();
if(!name || !phone || !address){
alert('Please fill Name, Phone and Delivery Address before ordering.');
return;
}
if(basket.length === 0){
alert('Please add at least one item to the order.');
return;
}
// build order text
let txt = `New order from ${name} (%2B${phone})%0A`; // encode newlines as %0A
txt += `Delivery address: ${address}%0A%0A`;
txt += `Items:%0A`;
basket.forEach((it, idx)=>{
txt += `${idx+1}. ${it.name} (${it.category})%0A`;
});
if(capturedDataUrl){
txt += `%0ANote: customer attached a photo (captured in app).`;
}
// save to local orders
const orderObj = {
id: 'ord-' + Date.now(),
name, phone, address,
items: basket.slice(),
captured: capturedDataUrl,
timestamp: new Date().toISOString()
};
saveOrder(orderObj);
// open whatsapp
const url = `https://wa.me/${WHATSAPP_NUMBER}?text=${encodeURIComponent(decodeURIComponent(txt))}`;
// Use window.open to open in new tab / invoke native app on mobile
window.open(url, '_blank');
// optional: clear basket after ordering
basket = [];
renderBasket();
};
/**********************
* Orders storage: save to localStorage
**********************/
function saveOrder(order){
const arr = JSON.parse(localStorage.getItem('savedOrders') || '[]');
arr.unshift(order); // newest first
localStorage.setItem('savedOrders', JSON.stringify(arr));
loadOrdersToUI();
}
function loadOrdersToUI(){
ordersListDiv.innerHTML = '';
const arr = JSON.parse(localStorage.getItem('savedOrders') || '[]');
if(arr.length === 0){
ordersListDiv.innerHTML = 'No orders saved yet.
';
return;
}
arr.forEach(ord=>{
const card = document.createElement('div');
card.className = 'order-card';
card.innerHTML = `
${ord.name} — ${ord.phone}
${new Date(ord.timestamp).toLocaleString()}
Resend via WhatsApp
Address: ${escapeHtml(ord.address)}
${ord.items.map(i => `
${ord.captured ? `` : ''}
• ${escapeHtml(i.name)} (${escapeHtml(i.category)})
`).join('')}
Delete
`;
ordersListDiv.appendChild(card);
});
// attach handlers for resend buttons
document.querySelectorAll('.btn-resend').forEach(b=>{
b.onclick = (e) => {
const id = e.target.dataset.id;
const arr = JSON.parse(localStorage.getItem('savedOrders') || '[]');
const ord = arr.find(x=>x.id===id);
if(!ord) return alert('Order not found');
let txt = `Order from ${ord.name} (%2B${ord.phone})%0ADelivery address: ${ord.address}%0A%0AItems:%0A`;
ord.items.forEach((it, idx)=> txt += `${idx+1}. ${it.name} (${it.category})%0A`);
if(ord.captured) txt += `%0APhoto attached by customer.`;
const url = `https://wa.me/${WHATSAPP_NUMBER}?text=${encodeURIComponent(txt)}`;
window.open(url, '_blank');
};
});
}
// delete order (global function used in inline onclick)
window.deleteOrder = function(id){
let arr = JSON.parse(localStorage.getItem('savedOrders') || '[]');
arr = arr.filter(a=>a.id !== id);
localStorage.setItem('savedOrders', JSON.stringify(arr));
loadOrdersToUI();
};
function escapeHtml(s){
return String(s).replaceAll('&','&').replaceAll('<','<').replaceAll('>','>');
}
/**********************
* Camera functionality (getUserMedia)
**********************/
let streamRef = null;
navCamera.onclick = startCameraRoute;
function startCameraRoute(){
// show camera view
showView('camera');
startCamera();
}
async function startCamera(){
stopCamera();
try{
const constraints = { video: { facingMode: "environment" }, audio:false };
const stream = await navigator.mediaDevices.getUserMedia(constraints);
streamRef = stream;
cameraStream.srcObject = stream;
cameraStream.play();
}catch(err){
alert('Camera access failed or blocked. ' + (err && err.message ? err.message : ''));
}
}
captureBtn.onclick = () => {
if(!streamRef) return alert('Camera not started.');
const video = cameraStream;
captureCanvas.width = video.videoWidth;
captureCanvas.height = video.videoHeight;
const ctx = captureCanvas.getContext('2d');
ctx.drawImage(video, 0, 0, captureCanvas.width, captureCanvas.height);
const dataUrl = captureCanvas.toDataURL('image/jpeg', 0.85);
capturedImgEl.src = dataUrl;
capturedImgEl.style.display = 'block';
// save to order captured
capturedDataUrl = dataUrl;
localStorage.setItem('capturedImage', dataUrl);
showCapturedDataUrl();
alert('Image captured and attached to order panel.');
};
stopCameraBtn.onclick = () => {
stopCamera();
cameraStream.srcObject = null;
};
function stopCamera(){
if(streamRef){
streamRef.getTracks().forEach(t => t.stop());
streamRef = null;
}
}
function showCapturedDataUrl(){
if(capturedDataUrl){
orderCapturedEl.src = capturedDataUrl;
orderCapturedEl.style.display = 'block';
}else{
orderCapturedEl.style.display = 'none';
}
}
/**********************
* Maps
**********************/
navMaps.onclick = () => showView('maps');
mapsOpenBtn.onclick = () => {
const address = mapsAddress.value.trim();
let url;
if(address){
url = `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(address)}`;
}else{
url = 'https://www.google.com/maps';
}
window.open(url, '_blank');
};
/**********************
* Nav / SPA switching
**********************/
navHome.onclick = ()=> showView('home');
navOrders.onclick = ()=> showView('orders');
function showView(name){
homeView.style.display = (name==='home') ? '' : 'none';
cameraView.style.display = (name==='camera') ? '' : 'none';
mapsView.style.display = (name==='maps') ? '' : 'none';
ordersView.style.display = (name==='orders') ? '' : 'none';
// stop camera when leaving
if(name !== 'camera') stopCamera();
// update active states
[navHome, navCamera, navMaps, navOrders].forEach(btn => btn.style.opacity = '1');
if(name==='home') navHome.style.opacity = '0.9';
if(name==='camera') navCamera.style.opacity = '0.9';
if(name==='maps') navMaps.style.opacity = '0.9';
if(name==='orders') navOrders.style.opacity = '0.9';
}
function attachNav(){
// default to home
showView('home');
}
/**********************
* Load orders on init
**********************/
// already done in init() via loadOrdersToUI
/**********************
* Helpful note:
* This is a front-end only demo. In production, attachments (captured images) should be uploaded to a server and a store DB used for orders.
**********************/
</script>