diff --git a/css/style.css b/css/style.css
index 9a3ad1e..bb0b35a 100644
--- a/css/style.css
+++ b/css/style.css
@@ -3742,4 +3742,19 @@ body.dark .hero-content p {
animation: none !important;
transition: none !important;
}
+}
+.menu-toggle-wrapper{
+ grid-column:1/-1;
+ display:flex;
+ justify-content:flex-end;
+ margin-top:20px;
+}
+
+.menu-toggle-btn{
+ padding:12px 25px;
+ border:none;
+ border-radius:40px;
+ background:#ff6b35;
+ color:white;
+ cursor:pointer;
}
\ No newline at end of file
diff --git a/js/main.js b/js/main.js
index 3f7292d..202565a 100644
--- a/js/main.js
+++ b/js/main.js
@@ -1,26 +1,28 @@
// ===== Global State =====
let menuItems = [];
+let expanded = false;
+const initialCount = 8;
let currentCategory = "All";
let orders = JSON.parse(localStorage.getItem('chaatOrders')) || [];
-
+
// Initialize cart from cart manager (will be set after DOM loads)
let cart = [];
let loyaltyPointsApplied = false;
-
+
// Will be initialized in setupCartManager() after document loads
function setupCartManager() {
cart = cartManager.getItems();
-
+
// Subscribe to cart changes to keep cart variable in sync
cartManager.subscribe((items) => {
cart = [...items];
});
-
+
// Validate cart integrity
cartManager.validate();
}
-
+
async function loadMenuData() {
try {
const response = await fetch("data/menu.json");
@@ -52,59 +54,59 @@ async function loadMenuData() {
}
}
}
-
+
// ===== Globals =====
-const specialsContainer = document.getElementById("specials-cards");
+const specialsContainer = document.getElementById("specials-cards");
// FIX: menu.html uses id="menu-container"; index.html uses id="menu-cards"
-const menuContainer = document.getElementById("menu-cards") || document.getElementById("menu-container");
-const cartCount = document.getElementById("cart-count");
-const cartSidebar = document.getElementById("cart-sidebar");
-const cartItemsContainer = document.getElementById("cart-items");
-const cartTotal = document.getElementById("cart-total") || document.getElementById("total-price");
-const checkoutBtn = document.getElementById("checkout-btn");
-
-const couponCodeInput = document.getElementById("coupon-code-input");
-const applyCouponBtn = document.getElementById("apply-coupon-btn");
-const removeCouponBtn = document.getElementById("remove-coupon-btn");
-const couponMessage = document.getElementById("coupon-message");
-const couponSubtotalEl = document.getElementById("coupon-subtotal");
-const couponDiscountEl = document.getElementById("coupon-discount");
-const couponDiscountRow = document.getElementById("coupon-discount-row");
-const couponGrandTotalEl = document.getElementById("coupon-grand-total");
-const appliedCouponLabel = document.getElementById("applied-coupon-label");
-
+const menuContainer = document.getElementById("menu-cards") || document.getElementById("menu-container");
+const cartCount = document.getElementById("cart-count");
+const cartSidebar = document.getElementById("cart-sidebar");
+const cartItemsContainer = document.getElementById("cart-items");
+const cartTotal = document.getElementById("cart-total") || document.getElementById("total-price");
+const checkoutBtn = document.getElementById("checkout-btn");
+
+const couponCodeInput = document.getElementById("coupon-code-input");
+const applyCouponBtn = document.getElementById("apply-coupon-btn");
+const removeCouponBtn = document.getElementById("remove-coupon-btn");
+const couponMessage = document.getElementById("coupon-message");
+const couponSubtotalEl = document.getElementById("coupon-subtotal");
+const couponDiscountEl = document.getElementById("coupon-discount");
+const couponDiscountRow = document.getElementById("coupon-discount-row");
+const couponGrandTotalEl = document.getElementById("coupon-grand-total");
+const appliedCouponLabel = document.getElementById("applied-coupon-label");
+
const COUPON_STORAGE_KEY = 'chaatCoupon';
const coupons = {
WELCOME10: { type: "percent", value: 10 },
- SAVE50: { type: "flat", value: 50 }
+ SAVE50: { type: "flat", value: 50 }
};
let activeCoupon = null;
-
+
// Cart is managed by CartManager - initialized in main startup
-
+
function formatPrice(price) {
return `₹${price}`;
}
-
+
function getCartSubtotal() {
return cart.reduce((sum, ci) => sum + ci.item.price * ci.quantity, 0);
}
-
+
function loadCouponFromStorage() {
const stored = localStorage.getItem(COUPON_STORAGE_KEY);
if (!stored) return null;
-
+
try {
const data = JSON.parse(stored);
if (!data || !data.code) return null;
-
- const code = String(data.code).trim().toUpperCase();
+
+ const code = String(data.code).trim().toUpperCase();
const coupon = coupons[code];
if (!coupon) {
localStorage.removeItem(COUPON_STORAGE_KEY);
return null;
}
-
+
activeCoupon = { code, ...coupon };
return activeCoupon;
} catch (error) {
@@ -112,7 +114,7 @@ function loadCouponFromStorage() {
return null;
}
}
-
+
function saveCouponToStorage() {
if (activeCoupon) {
localStorage.setItem(COUPON_STORAGE_KEY, JSON.stringify({ code: activeCoupon.code, appliedAt: Date.now() }));
@@ -120,71 +122,71 @@ function saveCouponToStorage() {
localStorage.removeItem(COUPON_STORAGE_KEY);
}
}
-
+
function validateCouponCode(input) {
const code = String(input || '').trim().toUpperCase();
-
+
if (!code) {
return { valid: false, message: 'Enter a coupon code.' };
}
-
+
const coupon = coupons[code];
if (!coupon) {
return { valid: false, message: 'Invalid or expired coupon.' };
}
-
+
return { valid: true, code, coupon };
}
-
+
function calculateCouponDiscount(subtotal) {
if (!activeCoupon) return 0;
-
+
if (activeCoupon.type === 'percent') {
return Math.min(Math.round((subtotal * activeCoupon.value) / 100), subtotal);
}
-
+
if (activeCoupon.type === 'flat') {
return Math.min(activeCoupon.value, subtotal);
}
-
+
return 0;
}
-
+
function showCouponMessage(message, type = 'success') {
if (couponMessage) {
couponMessage.textContent = message;
couponMessage.classList.toggle('success', type === 'success');
- couponMessage.classList.toggle('error', type === 'error');
+ couponMessage.classList.toggle('error', type === 'error');
}
-
+
showToast(type === 'success' ? `✅ ${message}` : `⚠️ ${message}`);
}
-
+
function updateCartSummary() {
const subtotal = getCartSubtotal();
const discount = calculateCouponDiscount(subtotal);
- const total = Math.max(subtotal - discount, 0);
-
- if (couponSubtotalEl) couponSubtotalEl.textContent = formatPrice(subtotal);
- if (couponDiscountEl) couponDiscountEl.textContent = `- ${formatPrice(discount)}`;
+ const total = Math.max(subtotal - discount, 0);
+
+ if (couponSubtotalEl) couponSubtotalEl.textContent = formatPrice(subtotal);
+ if (couponDiscountEl) couponDiscountEl.textContent = `- ${formatPrice(discount)}`;
if (couponDiscountRow) couponDiscountRow.style.display = discount > 0 ? 'flex' : 'none';
-
+
if (couponGrandTotalEl) {
couponGrandTotalEl.textContent = formatPrice(total);
} else if (cartTotal) {
cartTotal.textContent = `Total: ${formatPrice(total)}`;
}
-
+
if (appliedCouponLabel) {
appliedCouponLabel.textContent = activeCoupon ? `Coupon applied: ${activeCoupon.code}` : '';
}
-
+
if (checkoutBtn) checkoutBtn.disabled = cart.length === 0;
}
-
+
function applyCouponCode() {
const result = validateCouponCode(couponCodeInput ? couponCodeInput.value : '');
-
+
if (!result.valid) {
activeCoupon = null;
saveCouponToStorage();
@@ -192,7 +194,7 @@ function applyCouponCode() {
updateCartSummary();
return false;
}
-
+
activeCoupon = { code: result.code, ...result.coupon };
saveCouponToStorage();
showCouponMessage(`${result.code} applied!`, 'success');
@@ -200,22 +202,22 @@ function applyCouponCode() {
updateCartSummary();
return true;
}
-
+
function removeCoupon() {
activeCoupon = null;
saveCouponToStorage();
-
+
if (couponCodeInput) couponCodeInput.value = '';
if (removeCouponBtn) removeCouponBtn.style.display = 'none';
showCouponMessage('Coupon removed.', 'success');
updateCartSummary();
}
-
+
function setupCouponListeners() {
if (applyCouponBtn) {
applyCouponBtn.addEventListener('click', applyCouponCode);
}
-
+
if (couponCodeInput) {
couponCodeInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
@@ -224,32 +226,32 @@ function setupCouponListeners() {
}
});
}
-
+
if (removeCouponBtn) {
removeCouponBtn.addEventListener('click', removeCoupon);
}
-
+
if (loadCouponFromStorage() && couponCodeInput) {
couponCodeInput.value = activeCoupon.code;
}
-
+
if (activeCoupon && removeCouponBtn) {
removeCouponBtn.style.display = 'inline-flex';
}
-
+
updateCartSummary();
}
-
+
// ===== Fuzzy Match & Highlighter Utilities =====
function fuzzyMatch(target, query) {
if (!target || !query) return false;
const t = target.toLowerCase();
const q = query.toLowerCase();
-
+
// 1. Direct Substring Match
if (t.includes(q)) return true;
-
+
// 2. Fuzzy sequencing character lookup
let qIdx = 0;
@@ -265,37 +267,37 @@ function fuzzyMatch(target, query) {
return false;
}
-
+
function highlightText(text, query) {
- if (!text) return "";
+ if (!text) return "";
if (!query) return text;
const escapedQuery = query.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
const regex = new RegExp(`(${escapedQuery})`, "gi");
return text.replace(regex, "$1");
}
-
+
// ===== Render Functions =====
-
+
function createCard(item, highlightQuery = "") {
const card = document.createElement("article");
card.className = "card";
- card.tabIndex = 0;
+ card.tabIndex = 0;
card.setAttribute("aria-label", `${item.name} - ${item.description}. Price: ${formatPrice(item.price)}.`);
-
- const ratingStars = "⭐".repeat(Math.round(item.rating || 5));
- const dietaryTags = item.dietary ? item.dietary.map(d => `${d}`).join(" ") : "";
- const spiceIcon = item.spice === "High" ? "🌶️🌶️🌶️" : item.spice === "Medium" ? "🌶️🌶️" : "🌶️";
-
+
+ const ratingStars = "⭐".repeat(Math.round(item.rating || 5));
+ const dietaryTags = item.dietary ? item.dietary.map(d => `${d}`).join(" ") : "";
+ const spiceIcon = item.spice === "High" ? "🌶️🌶️🌶️" : item.spice === "Medium" ? "🌶️🌶️" : "🌶️";
+
const highlightedName = highlightText(item.name, highlightQuery);
const highlightedDesc = highlightText(item.description, highlightQuery);
-
- const isAvailable = item.available !== undefined ? item.available : true;
- const outOfStockBadge = !isAvailable ? 'Out of Stock ❌' : '';
- const buttonDisabled = !isAvailable ? 'disabled' : '';
- const buttonColor = isAvailable ? '#28a745' : '#cccccc';
-
+
+ const isAvailable = item.available !== undefined ? item.available : true;
+ const outOfStockBadge = !isAvailable ? 'Out of Stock ❌' : '';
+ const buttonDisabled = !isAvailable ? 'disabled' : '';
+ const buttonColor = isAvailable ? '#28a745' : '#cccccc';
+
card.innerHTML = `
@@ -335,67 +337,67 @@ function createCard(item, highlightQuery = "") {
`;
-
+
const addBtn = card.querySelector(".add-btn");
if (isAvailable) {
addBtn.addEventListener("click", () => addToCart(item.id));
- }
+ }
else {
addBtn.addEventListener("click", () => alert(`${item.name} is currently out of stock!`));
}
-
+
card.addEventListener("click", () => {
RecentlyViewed.addItem(item);
renderRecentlyViewed();
});
-
+
return card;
}
-
+
function renderSpecials() {
if (!specialsContainer) return;
const specials = menuItems.slice(0, 3);
-
+
showSkeletonCards(specialsContainer, specials.length);
-
+
setTimeout(() => {
specialsContainer.innerHTML = "";
specials.forEach(item => specialsContainer.appendChild(createCard(item)));
}, 1500);
}
-
+
function renderMenu(filter = "All") {
currentCategory = filter;
applyAllFilters();
}
-
+
function renderRecentlyViewed() {
const recentlyViewedContainer = document.getElementById("recently-viewed-cards");
- const recentlyViewedSection = document.getElementById("recently-viewed");
+ const recentlyViewedSection = document.getElementById("recently-viewed");
if (!recentlyViewedContainer || !recentlyViewedSection) return;
-
+
const recentItems = RecentlyViewed.getItems();
recentlyViewedContainer.innerHTML = "";
-
+
if (recentItems.length === 0) {
recentlyViewedSection.style.display = "none";
return;
}
-
+
recentlyViewedSection.style.display = "block";
recentItems.forEach(item => recentlyViewedContainer.appendChild(createCard(item)));
}
-
+
// Unified Interactive Filter Engine =====
function renderFavorites() {
const favoritesContainer = document.getElementById("favorites-container");
if (!favoritesContainer) return;
-
+
const recentItems = RecentlyViewed.getItems();
favoritesContainer.innerHTML = "";
-
+
if (recentItems.length === 0) {
favoritesContainer.innerHTML = `
@@ -407,70 +409,70 @@ function renderFavorites() {
return;
}
-
+
recentItems.forEach(item => favoritesContainer.appendChild(createCard(item)));
}
-
+
// ===== Unified Interactive Filter Engine =====
function applyAllFilters() {
if (!menuContainer) return;
-
+
showSkeletonCards(menuContainer, 4);
-
+
setTimeout(() => {
menuContainer.innerHTML = "";
-
+
const searchInput = document.getElementById("search-input");
- const query = searchInput ? searchInput.value.trim() : "";
-
+ const query = searchInput ? searchInput.value.trim() : "";
+
const priceSlider = document.getElementById("price-range-slider");
// FIX: use slider's own max as fallback so all items show when slider is at max
- const maxPrice = priceSlider ? parseFloat(priceSlider.value) : 100;
-
- const spiceSelect = document.getElementById("spice-level-select");
+ const maxPrice = priceSlider ? parseFloat(priceSlider.value) : 100;
+
+ const spiceSelect = document.getElementById("spice-level-select");
const selectedSpice = spiceSelect ? spiceSelect.value : "All";
-
+
const ratingSelect = document.getElementById("rating-select");
- const minRating = ratingSelect ? ratingSelect.value : "All";
-
+ const minRating = ratingSelect ? ratingSelect.value : "All";
+
const veganCheck = document.getElementById("dietary-vegan");
- const gfCheck = document.getElementById("dietary-gf");
-
+ const gfCheck = document.getElementById("dietary-gf");
+
let filtered = menuItems;
-
+
if (currentCategory !== "All") {
filtered = filtered.filter((item) => item.category === currentCategory);
}
-
+
if (query) {
filtered = filtered.filter(item =>
fuzzyMatch(item.name, query) ||
(item.description && fuzzyMatch(item.description, query)) ||
- (item.category && fuzzyMatch(item.category, query))
+ (item.category && fuzzyMatch(item.category, query))
);
}
-
+
filtered = filtered.filter(item => item.price <= maxPrice);
-
+
if (selectedSpice !== "All") {
filtered = filtered.filter((item) => item.spice === selectedSpice);
}
-
+
if (minRating !== "All") {
- const ratingVal = parseFloat(minRating);
+ const ratingVal = parseFloat(minRating);
filtered = filtered.filter(item => (item.rating || 5) >= ratingVal);
}
-
+
if (veganCheck && veganCheck.checked) {
filtered = filtered.filter(item => item.dietary && item.dietary.includes("vegan"));
}
// Gluten Free
if (gfCheck && gfCheck.checked) {
- filtered = filtered.filter(item => item.dietary && item.dietary.includes("gluten-free"));
+ filtered = filtered.filter(item => item.dietary && item.dietary.includes("gluten-free"));
}
-
+
if (filtered.length === 0) {
menuContainer.innerHTML = `
@@ -479,36 +481,64 @@ function applyAllFilters() {
`;
return;
}
-
- filtered.forEach(item => menuContainer.appendChild(createCard(item, query)));
+
+
+const initialCount = 8;
+
+const visibleItems = expanded
+ ? filtered
+ : filtered.slice(0, initialCount);
+
+visibleItems.forEach(item => {
+ menuContainer.appendChild(createCard(item, query));
+});
+
+if (filtered.length > initialCount) {
+
+ const btnWrapper = document.createElement("div");
+ btnWrapper.className = "menu-toggle-wrapper";
+
+ btnWrapper.innerHTML = `
+
+ `;
+
+ btnWrapper.querySelector("button").onclick = () => {
+ expanded = !expanded;
+ applyAllFilters();
+ };
+
+ menuContainer.appendChild(btnWrapper);
+}
}, 800);
}
-
+
function renderCart() {
if (!cartItemsContainer) return;
-
+
if (cart.length > 0) {
showSkeletonCartItems(cart.length);
}
-
+
setTimeout(() => {
cartItemsContainer.innerHTML = "";
-
+
if (cart.length === 0) {
cartItemsContainer.innerHTML =
`
Your cart is empty.
`;
if (checkoutBtn) checkoutBtn.disabled = true;
- if (cartTotal) cartTotal.textContent = "Total: ₹0";
+ if (cartTotal) cartTotal.textContent = "Total: ₹0";
updateCartSummary();
return;
}
-
+
cart.forEach(({ item, quantity }) => {
const cartItem = document.createElement("div");
cartItem.className = "cart-item";
-
+
cartItem.innerHTML = `

@@ -528,7 +558,7 @@ function renderCart() {
`;
-
+
const decreaseBtn = cartItem.querySelector(".qty-decrease");
if (decreaseBtn) {
decreaseBtn.addEventListener("click", () => removeFromCart(item.id));
@@ -538,7 +568,7 @@ function renderCart() {
if (increaseBtn) {
increaseBtn.addEventListener("click", () => addToCart(item.id));
}
-
+
const removeBtn = cartItem.querySelector(".cart-item-remove");
if (removeBtn) {
removeBtn.addEventListener("click", () => {
@@ -548,20 +578,20 @@ function renderCart() {
renderCart();
});
}
-
+
cartItemsContainer.appendChild(cartItem);
});
-
+
updateCartSummary();
-
+
// Render Loyalty Points Widget
- const points = typeof loyalty !== 'undefined' ? loyalty.getBalance() : 0;
- const loyaltyDiv = document.createElement("div");
+ const points = typeof loyalty !== 'undefined' ? loyalty.getBalance() : 0;
+ const loyaltyDiv = document.createElement("div");
loyaltyDiv.className = "cart-loyalty-widget";
-
- const total = cart.reduce((sum, ci) => sum + ci.item.price * ci.quantity, 0);
+
+ const total = cart.reduce((sum, ci) => sum + ci.item.price * ci.quantity, 0);
const discountVal = Math.min(points, total);
-
+
loyaltyDiv.innerHTML = `
`}
`;
-
+
cartItemsContainer.appendChild(loyaltyDiv);
-
+
const checkbox = loyaltyDiv.querySelector("#apply-loyalty-checkbox");
if (checkbox) {
checkbox.addEventListener("change", (e) => {
loyaltyPointsApplied = e.target.checked;
- const freshDiscount = Math.min(points, total);
- let totalHtml = "";
-
+ const freshDiscount = Math.min(points, total);
+ let totalHtml = "";
+
if (loyaltyPointsApplied && points > 0) {
const finalTotal = total - freshDiscount;
totalHtml = `
@@ -606,11 +636,11 @@ function renderCart() {
} else {
totalHtml = `Total: ${formatPrice(total)}`;
}
-
+
if (cartTotal) cartTotal.innerHTML = totalHtml;
});
}
-
+
// Initial total display
let totalHtml = "";
if (loyaltyPointsApplied && points > 0) {
@@ -625,20 +655,20 @@ function renderCart() {
} else {
totalHtml = `Total: ${formatPrice(total)}`;
}
-
- if (cartTotal) cartTotal.innerHTML = totalHtml;
+
+ if (cartTotal) cartTotal.innerHTML = totalHtml;
if (checkoutBtn) checkoutBtn.disabled = false;
-
+
}, 600);
}
-
+
function updateCartCount() {
if (cartCount) {
const totalCount = cart.reduce((sum, cartItem) => sum + cartItem.quantity, 0);
cartCount.textContent = totalCount;
}
}
-
+
function updateFavCount() {
const favCount = document.getElementById("fav-count");
if (favCount && typeof RecentlyViewed !== 'undefined') {
@@ -652,39 +682,39 @@ function saveCart() {
cartManager.saveToStorage();
}
}
-
+
// ===== My Orders Dashboard =====
-
+
function updateOrderStatuses() {
let changed = false;
- const now = Date.now();
-
+ const now = Date.now();
+
orders.forEach(order => {
if (order.status === "Delivered") return;
-
+
const elapsedSeconds = (now - order.timestamp) / 1000;
let targetStatus = "Pending";
-
- if(elapsedSeconds >= 45) targetStatus = "Delivered";
- else if(elapsedSeconds >= 25) targetStatus = "On the Way";
- else if(elapsedSeconds >= 10) targetStatus = "Preparing";
-
+
+ if (elapsedSeconds >= 45) targetStatus = "Delivered";
+ else if (elapsedSeconds >= 25) targetStatus = "On the Way";
+ else if (elapsedSeconds >= 10) targetStatus = "Preparing";
+
if (order.status !== targetStatus) {
order.status = targetStatus;
changed = true;
}
});
-
+
if (changed) {
localStorage.setItem('chaatOrders', JSON.stringify(orders));
renderOrdersList();
}
}
-
+
function renderOrdersList() {
const container = document.getElementById("orders-container");
if (!container) return;
-
+
if (orders.length === 0) {
container.innerHTML = `
@@ -695,19 +725,19 @@ function renderOrdersList() {
`;
return;
}
-
+
container.innerHTML = "";
-
+
orders.forEach(order => {
const card = document.createElement("article");
card.className = "order-card";
-
+
const isPreparing = (order.status === "Preparing" || order.status === "On the Way" || order.status === "Delivered") ? "active" : "";
- const isOnWay = (order.status === "On the Way" || order.status === "Delivered") ? "active" : "";
+ const isOnWay = (order.status === "On the Way" || order.status === "Delivered") ? "active" : "";
const isDelivered = order.status === "Delivered" ? "active" : "";
-
+
const statusClass = "status-" + order.status.toLowerCase().replace(/\s+/g, "-");
-
+
let itemsHtml = "";
order.items.forEach(ci => {
itemsHtml += `
@@ -717,7 +747,7 @@ function renderOrdersList() {
`;
});
-
+
card.innerHTML = `
`;
- container.appendChild(card);
+ container.appendChild(card);
});
}
-
+
// ===== Global Window Handlers =====
-
+
window.filterCategory = function (category) {
currentCategory = category;
applyAllFilters();
-
- const buttons = document.querySelectorAll(".filter-btn, .filter button");
+
+ const buttons = document.querySelectorAll(".filter-btn, .filter button");
buttons.forEach(btn => {
const filterAttr = btn.dataset.filter || (btn.getAttribute("onclick") ? btn.getAttribute("onclick").match(/'([^']+)'/)[1] : "");
if (filterAttr === category || btn.textContent.trim() === category) {
@@ -797,42 +827,42 @@ window.filterCategory = function (category) {
}
});
};
-
+
// FIX: checkout — removed duplicate const declarations that caused SyntaxError
window.checkout = async function () {
if (cart.length === 0) {
alert("Your cart is empty!");
return false;
}
-
+
const validationResult = await validateDeliveryLocation();
-
+
if (!validationResult.valid) {
- localStorage.setItem(
- "deliveryError",
- JSON.stringify({
- error: validationResult.error,
- distance: validationResult.distance,
- restaurantLocation: validationResult.restaurantLocation
- })
- );
+ localStorage.setItem(
+ "deliveryError",
+ JSON.stringify({
+ error: validationResult.error,
+ distance: validationResult.distance,
+ restaurantLocation: validationResult.restaurantLocation
+ })
+ );
return { deliveryAvailable: false };
}
-
+
// Loyalty points discount
const subtotal = getCartSubtotal();
const couponDiscount = calculateCouponDiscount(subtotal);
const subtotalAfterCoupon = Math.max(subtotal - couponDiscount, 0);
- let loyaltyDiscount = 0;
- let pointsRedeemed = 0;
-
+ let loyaltyDiscount = 0;
+ let pointsRedeemed = 0;
+
if (loyaltyPointsApplied && typeof loyalty !== 'undefined') {
- const balance = loyalty.getBalance();
- pointsRedeemed = Math.min(balance, subtotal);
- loyaltyDiscount = pointsRedeemed;
+ const balance = loyalty.getBalance();
+ pointsRedeemed = Math.min(balance, subtotal);
+ loyaltyDiscount = pointsRedeemed;
loyalty.redeemPoints(pointsRedeemed);
}
-
+
const finalTotal = Math.max(subtotalAfterCoupon - pointsDiscount, 0);
const totalDiscount = couponDiscount + pointsDiscount;
@@ -841,128 +871,128 @@ window.checkout = async function () {
if (typeof loyalty !== 'undefined') {
pointsEarned = loyalty.awardPoints(finalTotal);
}
-
+
const newOrder = {
- id: "CB-" + Math.floor(100000 + Math.random() * 900000),
- date: new Date().toLocaleDateString(undefined, {
+ id: "CB-" + Math.floor(100000 + Math.random() * 900000),
+ date: new Date().toLocaleDateString(undefined, {
month: 'short', day: 'numeric', year: 'numeric',
- hour: '2-digit', minute: '2-digit'
+ hour: '2-digit', minute: '2-digit'
}),
- timestamp: Date.now(),
- items: JSON.parse(JSON.stringify(cart)),
- subtotal: subtotal,
- discount: loyaltyDiscount + couponDiscount,
- coupon: activeCoupon?.code || null,
+ timestamp: Date.now(),
+ items: JSON.parse(JSON.stringify(cart)),
+ subtotal: subtotal,
+ discount: loyaltyDiscount + couponDiscount,
+ coupon: activeCoupon?.code || null,
pointsRedeemed,
pointsEarned,
- total: finalTotal,
- status: "Pending",
+ total: finalTotal,
+ status: "Pending",
deliveryAddress: {
- latitude: validationResult.userLocation.latitude,
+ latitude: validationResult.userLocation.latitude,
longitude: validationResult.userLocation.longitude,
- source: validationResult.userLocation.source
+ source: validationResult.userLocation.source
},
- deliveryDistance: validationResult.distance,
+ deliveryDistance: validationResult.distance,
restaurantLocation: validationResult.restaurantLocation
};
-
+
orders.unshift(newOrder);
localStorage.setItem('chaatOrders', JSON.stringify(orders));
-
+
loyaltyPointsApplied = false;
- activeCoupon = null;
+ activeCoupon = null;
saveCouponToStorage();
-
+
cartManager.clear();
updateCartCount();
updateFavCount();
renderCart();
-
+
if (typeof window.triggerDeliverySimulation === 'function') {
window.triggerDeliverySimulation();
} else {
console.warn('Delivery tracker not ready. Order has been placed.');
}
-
+
return true;
};
-
+
window.reorderOrder = function (orderId) {
const pastOrder = orders.find(o => o.id === orderId);
if (!pastOrder) return;
-
+
pastOrder.items.forEach(orderItem => {
cartManager.addItem(orderItem.item, orderItem.quantity);
});
-
+
updateCartCount();
updateFavCount();
renderCart();
alert("Items added back to your cart successfully!");
-
+
const sidebar = document.getElementById("cart-sidebar");
if (sidebar) {
sidebar.setAttribute("aria-hidden", "false");
sidebar.classList.add("open");
}
};
-
+
// Cart Operations
// Toast Notification
-
+
// ===== Toast Notification =====
-
+
function showToast(message) {
const toast = document.getElementById("toast-notification");
if (!toast) return;
-
+
toast.textContent = message;
toast.classList.add("show");
-
+
clearTimeout(toast.hideTimeout);
toast.hideTimeout = setTimeout(() => {
toast.classList.remove("show");
}, 2500);
}
-
+
// ===== Cart Operations =====
-
+
function addToCart(id) {
const item = menuItems.find(i => i.id === id);
if (!item) return;
-
+
const isAvailable = item.available !== undefined ? item.available : true;
if (!isAvailable) {
alert(`${item.name} is currently out of stock!`);
return;
}
-
+
cartManager.addItem(item, 1);
updateCartCount();
updateFavCount();
renderCart();
showToast(`🛒 ${item.name} added to cart`);
-
+
if (cartCount) {
cartCount.classList.add("cart-bounce");
setTimeout(() => cartCount.classList.remove("cart-bounce"), 400);
}
-
+
if (cartSidebar) {
cartSidebar.setAttribute("aria-hidden", "false");
cartSidebar.classList.add("open");
}
}
-
+
function removeFromCart(id) {
const cartIndex = cart.findIndex(ci => ci.item.id === id);
if (cartIndex === -1) return;
-
+
const removedItem = cart[cartIndex].item;
cartManager.decreaseQuantity(id);
@@ -1084,9 +1114,9 @@ window.reorderOrder = function (orderId) {
renderCart();
showToast(`🗑️ ${removedItem.name} removed from cart`);
}
-
+
// ===== Event Listeners =====
-
+
function setupFilterButtons() {
const filterButtons = document.querySelectorAll(".filter-btn");
filterButtons.forEach(btn => {
@@ -1102,23 +1132,23 @@ function setupFilterButtons() {
});
});
}
-
+
function setupCartToggle() {
const cartOpenBtn = document.getElementById("cart-open-btn");
const cartCloseBtn = document.getElementById("cart-close");
if (!cartOpenBtn || !cartCloseBtn || !cartSidebar) return;
-
+
cartOpenBtn.addEventListener("click", (e) => {
e.preventDefault();
cartSidebar.setAttribute("aria-hidden", "false");
cartSidebar.classList.add("open");
});
-
+
cartCloseBtn.addEventListener("click", () => {
cartSidebar.setAttribute("aria-hidden", "true");
cartSidebar.classList.remove("open");
});
-
+
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && cartSidebar.getAttribute("aria-hidden") === "false") {
cartSidebar.setAttribute("aria-hidden", "true");
@@ -1126,38 +1156,38 @@ function setupCartToggle() {
}
});
}
-
+
function setupOrderNowScroll() {
const orderNowBtn = document.getElementById("order-now-btn");
const menuSection = document.getElementById("menu");
if (!orderNowBtn || !menuSection) return;
-
+
orderNowBtn.addEventListener("click", () => {
menuSection.scrollIntoView({ behavior: "smooth" });
});
}
-
+
// ===== Autocomplete & Search =====
-
+
function setupSearchSuggestions() {
- const searchInput = document.getElementById("search-input");
+ const searchInput = document.getElementById("search-input");
const suggestionsContainer = document.getElementById("search-suggestions");
if (!searchInput || !suggestionsContainer) return;
-
+
function showSuggestions() {
const query = searchInput.value.trim().toLowerCase();
suggestionsContainer.innerHTML = "";
-
+
if (!query) {
suggestionsContainer.style.display = "none";
return;
}
-
+
const matches = menuItems.filter(item =>
item.name.toLowerCase().includes(query) ||
(item.category && item.category.toLowerCase().includes(query))
).slice(0, 5);
-
+
if (matches.length === 0) {
const div = document.createElement("div");
div.className = "suggestion-item no-matches";
@@ -1166,7 +1196,7 @@ function setupSearchSuggestions() {
suggestionsContainer.style.display = "block";
return;
}
-
+
matches.forEach(item => {
const div = document.createElement("div");
div.className = "suggestion-item";
@@ -1177,39 +1207,39 @@ function setupSearchSuggestions() {
div.addEventListener("click", () => {
searchInput.value = item.name;
suggestionsContainer.style.display = "none";
-
+
// On menu.html scroll to top of menu section; on index.html scroll to #menu
const menuSection = document.getElementById("menu") || document.querySelector(".menu-page");
if (menuSection) menuSection.scrollIntoView({ behavior: "smooth" });
-
+
applyAllFilters();
});
suggestionsContainer.appendChild(div);
});
-
+
suggestionsContainer.style.display = "block";
}
-
+
searchInput.addEventListener("input", showSuggestions);
searchInput.addEventListener("focus", showSuggestions);
document.addEventListener("click", (e) => {
if (!searchInput.contains(e.target) && !suggestionsContainer.contains(e.target)) {
suggestionsContainer.style.display = "none";
}
-});
+ });
}
-
+
function setupSearch() {
const searchInput = document.getElementById("search-input");
- const searchBtn = document.getElementById("search-btn");
+ const searchBtn = document.getElementById("search-btn");
if (!searchInput || !searchBtn) return;
-
+
function handleSearchClick() {
const menuSection = document.getElementById("menu") || document.querySelector(".menu-page");
if (menuSection) menuSection.scrollIntoView({ behavior: "smooth" });
applyAllFilters();
}
-
+
searchBtn.addEventListener("click", handleSearchClick);
searchInput.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
@@ -1219,14 +1249,14 @@ function setupSearch() {
}
});
}
-
+
// ===== Advanced Filters Panel =====
-
+
function setupAdvancedFilters() {
- const toggleBtn = document.getElementById("filter-toggle-btn");
+ const toggleBtn = document.getElementById("filter-toggle-btn");
const filterPanel = document.getElementById("advanced-filters");
if (!toggleBtn || !filterPanel) return;
-
+
toggleBtn.addEventListener("click", () => {
const isExpanded = toggleBtn.getAttribute("aria-expanded") === "true";
toggleBtn.setAttribute("aria-expanded", !isExpanded);
@@ -1238,8 +1268,8 @@ function setupAdvancedFilters() {
toggleBtn.classList.add("active");
}
});
-
- const priceSlider = document.getElementById("price-range-slider");
+
+ const priceSlider = document.getElementById("price-range-slider");
const priceSliderVal = document.getElementById("price-slider-val");
if (priceSlider && priceSliderVal) {
priceSlider.addEventListener("input", () => {
@@ -1248,23 +1278,23 @@ function setupAdvancedFilters() {
applyAllFilters();
});
}
-
+
const spiceSelect = document.getElementById("spice-level-select");
- if(spiceSelect)
+ if (spiceSelect)
spiceSelect.addEventListener("change", applyAllFilters);
-
+
const ratingSelect = document.getElementById("rating-select");
- if(ratingSelect)
+ if (ratingSelect)
ratingSelect.addEventListener("change", applyAllFilters);
-
+
const veganCheck = document.getElementById("dietary-vegan");
- if(veganCheck)
+ if (veganCheck)
veganCheck.addEventListener("change", applyAllFilters);
-
+
const gfCheck = document.getElementById("dietary-gf");
- if(gfCheck)
+ if (gfCheck)
gfCheck.addEventListener("change", applyAllFilters);
-
+
const resetBtn = document.getElementById("reset-filters-btn");
if (resetBtn) {
resetBtn.addEventListener("click", () => {
@@ -1273,16 +1303,16 @@ function setupAdvancedFilters() {
if (priceSliderVal) priceSliderVal.textContent = `₹${priceSlider.max}`;
priceSlider.setAttribute("aria-valuenow", priceSlider.max);
}
- if (spiceSelect) spiceSelect.value = "All";
+ if (spiceSelect) spiceSelect.value = "All";
if (ratingSelect) ratingSelect.value = "All";
- if (veganCheck) veganCheck.checked = false;
- if (gfCheck) gfCheck.checked = false;
-
+ if (veganCheck) veganCheck.checked = false;
+ if (gfCheck) gfCheck.checked = false;
+
const searchInput = document.getElementById("search-input");
if (searchInput) searchInput.value = "";
-
+
currentCategory = "All";
-
+
const buttons = document.querySelectorAll(".filter-btn, .filter button");
buttons.forEach(btn => {
const filterAttr = btn.dataset.filter || (btn.getAttribute("onclick") ? btn.getAttribute("onclick").match(/'([^']+)'/)[1] : "");
@@ -1294,41 +1324,41 @@ function setupAdvancedFilters() {
btn.setAttribute("aria-pressed", "false");
}
});
- applyAllFilters();
+ applyAllFilters();
});
}
}
-
+
// ===== Contact Form =====
-
+
function setupContactForm() {
const form = document.getElementById("contact-form");
const formSuccess = document.getElementById("form-success");
if (!form || !formSuccess) return;
-
- const nameInput = form.querySelector("#name");
- const emailInput = form.querySelector("#email");
+
+ const nameInput = form.querySelector("#name");
+ const emailInput = form.querySelector("#email");
const messageInput = form.querySelector("#message");
- const errorName = form.querySelector("#error-name");
- const errorEmail = form.querySelector("#error-email");
+ const errorName = form.querySelector("#error-name");
+ const errorEmail = form.querySelector("#error-email");
const errorMessage = form.querySelector("#error-message");
-
+
form.addEventListener("submit", (e) => {
e.preventDefault();
- errorName.textContent = "";
- errorEmail.textContent = "";
+ errorName.textContent = "";
+ errorEmail.textContent = "";
errorMessage.textContent = "";
formSuccess.style.display = "none";
-
+
const validation = validateAndSanitizeContactForm(nameInput.value, emailInput.value, messageInput.value);
-
+
if (!validation.valid) {
- if (validation.errors.name) errorName.textContent = validation.errors.name;
- if (validation.errors.email) errorEmail.textContent = validation.errors.email;
+ if (validation.errors.name) errorName.textContent = validation.errors.name;
+ if (validation.errors.email) errorEmail.textContent = validation.errors.email;
if (validation.errors.message) errorMessage.textContent = validation.errors.message;
return;
}
-
+
formSuccess.style.display = "block";
setTimeout(() => {
form.reset();
@@ -1336,12 +1366,12 @@ function setupContactForm() {
}, 3000);
});
}
-
+
function setupNewsletterForm() {
const newsletterForm = document.getElementById("newsletter-form");
if (!newsletterForm) return;
const emailInput = newsletterForm.querySelector("#newsletter-email");
-
+
newsletterForm.addEventListener("submit", (e) => {
e.preventDefault();
const validation = validateAndSanitizeEmail(emailInput.value);
@@ -1353,28 +1383,28 @@ function setupNewsletterForm() {
newsletterForm.reset();
});
}
-
+
function setupActiveNavbar() {
const navLinks = document.querySelectorAll(".nav-link");
const sections = document.querySelectorAll("section");
-
+
navLinks.forEach(link => {
link.addEventListener("click", () => {
navLinks.forEach(nav => nav.classList.remove("active"));
link.classList.add("active");
});
});
-
+
window.addEventListener("scroll", () => {
let current = "";
sections.forEach((section) => {
- const sectionTop = section.offsetTop - 120;
+ const sectionTop = section.offsetTop - 120;
const sectionHeight = section.clientHeight;
if (window.scrollY >= sectionTop && window.scrollY < sectionTop + sectionHeight) {
current = section.getAttribute("id");
}
});
-
+
navLinks.forEach((link) => {
link.classList.remove("active");
const href = link.getAttribute("href");
@@ -1384,7 +1414,7 @@ function setupActiveNavbar() {
});
});
}
-
+
function setupDropdownFilterLinks() {
const dropdownFilters = document.querySelectorAll(".menu-filter");
dropdownFilters.forEach(link => {
@@ -1415,20 +1445,20 @@ function setupDropdownFilterLinks() {
});
});
}
-
+
// FIX: only one saveCart declaration
function saveCart() {
localStorage.setItem("cart", JSON.stringify(cart));
}
-
+
// ===== FIX: Read ?filter= URL param on menu.html load =====
function applyUrlFilterParam() {
- const params = new URLSearchParams(window.location.search);
- const filter = params.get("filter");
+ const params = new URLSearchParams(window.location.search);
+ const filter = params.get("filter");
if (!filter) return;
-
+
currentCategory = filter;
-
+
document.querySelectorAll(".filter-btn").forEach(btn => {
const isActive = btn.dataset.filter === filter;
btn.classList.toggle("active", isActive);
@@ -1446,12 +1476,12 @@ function applyUrlFilterParam() {
const menuContainer = document.getElementById('menu-container');
if (menuContainer) menuContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
-
+
// ===== Initialization =====
-
+
async function init() {
setupCartManager();
-
+
// Bind UI interactions immediately
setupCartToggle();
setupFilterButtons();
@@ -1464,9 +1494,9 @@ async function init() {
setupNewsletterForm();
setupActiveNavbar();
setupDropdownFilterLinks();
-
-if (checkoutBtn) {
+
+ if (checkoutBtn) {
checkoutBtn.addEventListener("click", async (e) => {
e.preventDefault();
const success = await window.checkout();
@@ -1475,13 +1505,13 @@ if (checkoutBtn) {
}
});
}
-
-
- // Load menu data, then render
+
+
+ // Load menu data, then render
await loadMenuData();
- // FIX: apply ?filter= param AFTER data is loaded and filter buttons exist
+ // FIX: apply ?filter= param AFTER data is loaded and filter buttons exist
applyUrlFilterParam();
-
+
renderSpecials();
renderRecentlyViewed();
renderFavorites();
@@ -1489,20 +1519,20 @@ if (checkoutBtn) {
updateCartCount();
updateFavCount();
renderCart();
-
+
renderOrdersList();
updateOrderStatuses();
setInterval(updateOrderStatuses, 3000);
}
-
+
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
-
+
// ===== Skeleton UI Helpers =====
-
+
function createSkeletonCard() {
const el = document.createElement("div");
@@ -1517,7 +1547,7 @@ function createSkeletonCard() {
`;
return el;
}
-
+
function showSkeletonCards(container, count = 3) {
if (!container) return;
@@ -1526,7 +1556,7 @@ function showSkeletonCards(container, count = 3) {
container.appendChild(createSkeletonCard());
}
}
-
+
function createSkeletonCartItem() {
const el = document.createElement("div");
el.className = "skeleton-cart-item";
@@ -1542,27 +1572,44 @@ function createSkeletonCartItem() {
`;
return el;
}
-
+
function showSkeletonCartItems(count = 2) {
if (!cartItemsContainer) return;
cartItemsContainer.innerHTML = "";
for (let i = 0; i < count; i++) cartItemsContainer.appendChild(createSkeletonCartItem());
}
-
-// ===== Dark Mode =====
-const toggleBtn = document.getElementById("theme-toggle");
-
+
+// ==========================
+// Global Theme Toggle Script
+// ==========================
+
document.addEventListener("DOMContentLoaded", () => {
- if (localStorage.getItem("theme") === "dark") {
- document.body.classList.add("dark");
+ const toggleBtn = document.getElementById("theme-toggle");
+
+ // Load saved theme from localStorage
+ const savedTheme = localStorage.getItem("theme");
+ if (savedTheme === "light") {
+ document.body.classList.add("light-theme");
+ } else {
+ document.body.classList.add("dark-theme"); // default
+ }
+
+ if (toggleBtn) {
+ toggleBtn.addEventListener("click", () => {
+ const isDark = document.body.classList.contains("dark-theme");
+
+ if (isDark) {
+ document.body.classList.remove("dark-theme");
+ document.body.classList.add("light-theme");
+ localStorage.setItem("theme", "light");
+ } else {
+ document.body.classList.remove("light-theme");
+ document.body.classList.add("dark-theme");
+ localStorage.setItem("theme", "dark");
+ }
+ });
}
});
-
-if (toggleBtn) {
- toggleBtn.addEventListener("click", () => {
- document.body.classList.toggle("dark");
- localStorage.setItem("theme", document.body.classList.contains("dark") ? "dark" : "light");
- });
-}
-
+
+