| import os |
| import uuid |
| import time |
| import pandas as pd |
| import gradio as gr |
| from groq import Groq |
|
|
| |
| |
| |
| def starter_menu(): |
| |
| return [ |
| { |
| "id": "PZ001", |
| "name": "Pepperoni Pizza", |
| "category": "Pizza", |
| "price": 1299, |
| "desc": "Cheesy, crispy, and a lil’ spicy.", |
| "tags": ["spicy", "halal"], |
| "image": "https://images.unsplash.com/photo-1601924638867-3ec4d4baf4e1?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "PZ002", |
| "name": "Margherita Pizza", |
| "category": "Pizza", |
| "price": 1099, |
| "desc": "Classic tomato + basil vibes.", |
| "tags": ["veg", "halal"], |
| "image": "https://images.unsplash.com/photo-1604068549290-dea0e4a305ca?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "BBQ001", |
| "name": "BBQ Chicken Burger", |
| "category": "Burgers", |
| "price": 699, |
| "desc": "Smoky BBQ sauce + crunchy bite.", |
| "tags": ["halal"], |
| "image": "https://images.unsplash.com/photo-1568901346375-23c9450c58cd?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "BBQ002", |
| "name": "Zinger Burger", |
| "category": "Burgers", |
| "price": 749, |
| "desc": "Spicy fried chicken — main character energy.", |
| "tags": ["spicy", "halal"], |
| "image": "https://images.unsplash.com/photo-1550547660-d9450f859349?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "PA001", |
| "name": "Alfredo Pasta", |
| "category": "Pasta", |
| "price": 999, |
| "desc": "Creamy comfort with extra sauce points.", |
| "tags": ["halal"], |
| "image": "https://images.unsplash.com/photo-1523986371872-9d3ba2e2f5a8?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "PA002", |
| "name": "Arrabbiata Pasta", |
| "category": "Pasta", |
| "price": 949, |
| "desc": "Spicy tomato pasta for heat lovers.", |
| "tags": ["spicy", "veg", "halal"], |
| "image": "https://images.unsplash.com/photo-1611270629569-8b357cb88da9?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "BBQ003", |
| "name": "BBQ Platter", |
| "category": "BBQ", |
| "price": 1799, |
| "desc": "Mixed grill — shareable, but you won’t.", |
| "tags": ["halal"], |
| "image": "https://images.unsplash.com/photo-1544025162-d76694265947?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "SD001", |
| "name": "Loaded Fries", |
| "category": "Sides", |
| "price": 499, |
| "desc": "Cheese + sauce + chaos (the good kind).", |
| "tags": ["veg", "halal"], |
| "image": "https://images.unsplash.com/photo-1541592106381-b31e9677c0e5?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "SD002", |
| "name": "Nuggets (6 pcs)", |
| "category": "Sides", |
| "price": 399, |
| "desc": "Crispy bites for quick wins.", |
| "tags": ["halal"], |
| "image": "https://images.unsplash.com/photo-1606755962773-d324e0a13086?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "DR001", |
| "name": "Mint Lemonade", |
| "category": "Drinks", |
| "price": 249, |
| "desc": "Fresh, chill, and super refreshing.", |
| "tags": ["veg"], |
| "image": "https://images.unsplash.com/photo-1551024709-8f23befc6f87?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "DR002", |
| "name": "Iced Coffee", |
| "category": "Drinks", |
| "price": 349, |
| "desc": "For assignments + deadlines (we get it).", |
| "tags": ["veg"], |
| "image": "https://images.unsplash.com/photo-1517701604599-bb29b565090c?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "DS001", |
| "name": "Chocolate Brownie", |
| "category": "Desserts", |
| "price": 399, |
| "desc": "Fudgy, gooey, happiness.", |
| "tags": ["veg"], |
| "image": "https://images.unsplash.com/photo-1606313564200-e75d5e30476c?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "DS002", |
| "name": "Cheesecake Slice", |
| "category": "Desserts", |
| "price": 499, |
| "desc": "Creamy slice of peace.", |
| "tags": ["veg"], |
| "image": "https://images.unsplash.com/photo-1542826438-5b0b65e02c76?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "VL001", |
| "name": "Veggie Bowl", |
| "category": "Healthy", |
| "price": 699, |
| "desc": "Light, filling, and gym-friendly.", |
| "tags": ["veg", "high-protein"], |
| "image": "https://images.unsplash.com/photo-1512621776951-a57141f2eefd?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| { |
| "id": "VL002", |
| "name": "Grilled Chicken Bowl", |
| "category": "Healthy", |
| "price": 799, |
| "desc": "Protein + flavor = W.", |
| "tags": ["high-protein", "halal"], |
| "image": "https://images.unsplash.com/photo-1546069901-ba9599a7e63c?auto=format&fit=crop&w=800&q=60", |
| "available": True, |
| }, |
| ] |
|
|
|
|
| |
| |
| |
| def money(pkr: int) -> str: |
| return f"Rs {pkr:,}" |
|
|
| def menu_df(menu): |
| rows = [] |
| for m in menu: |
| if m.get("available", True): |
| rows.append({ |
| "ID": m["id"], |
| "Item": m["name"], |
| "Category": m["category"], |
| "Price (PKR)": m["price"], |
| "Tags": ", ".join(m.get("tags", [])), |
| "Available": "Yes" if m.get("available", True) else "No", |
| }) |
| df = pd.DataFrame(rows) |
| if not df.empty: |
| df = df.sort_values(["Category", "Item"]).reset_index(drop=True) |
| return df |
|
|
| def categories(menu): |
| cats = sorted(set([m["category"] for m in menu])) |
| return ["All"] + cats |
|
|
| def items_by_category(menu, cat): |
| items = [] |
| for m in menu: |
| if not m.get("available", True): |
| continue |
| if cat == "All" or m["category"] == cat: |
| items.append(m) |
| return items |
|
|
| def gallery_items(menu, cat): |
| items = items_by_category(menu, cat) |
| gal = [] |
| for m in items: |
| caption = f"{m['name']} • {money(m['price'])} • {m['category']} • {', '.join(m.get('tags', []))}" |
| gal.append((m["image"], caption)) |
| return gal |
|
|
| def dropdown_choices(menu, cat): |
| items = items_by_category(menu, cat) |
| |
| return [f"{m['name']} ({money(m['price'])}) [{m['id']}]" for m in items] |
|
|
| def find_item(menu, item_id): |
| for m in menu: |
| if m["id"] == item_id: |
| return m |
| return None |
|
|
| def parse_id_from_choice(choice): |
| |
| if not choice: |
| return None |
| if "[" in choice and "]" in choice: |
| return choice.split("[")[-1].split("]")[0].strip() |
| return None |
|
|
| def cart_to_df(cart, menu): |
| rows = [] |
| subtotal = 0 |
| for item_id, qty in cart.items(): |
| it = find_item(menu, item_id) |
| if not it: |
| continue |
| line = it["price"] * qty |
| subtotal += line |
| rows.append({ |
| "Item": it["name"], |
| "Qty": qty, |
| "Unit": it["price"], |
| "Line Total": line, |
| }) |
| df = pd.DataFrame(rows) |
| if df.empty: |
| df = pd.DataFrame([{"Item":"(Cart is empty)", "Qty":"-", "Unit":"-", "Line Total":"-"}]) |
| return df, subtotal |
|
|
| def calc_totals(subtotal, service_pct=5, tax_pct=0): |
| service = int(round(subtotal * (service_pct / 100))) |
| tax = int(round(subtotal * (tax_pct / 100))) |
| total = subtotal + service + tax |
| return service, tax, total |
|
|
|
|
| |
| |
| |
| def groq_reply(user_text: str, menu: list) -> str: |
| |
| import os |
| from groq import Groq |
|
|
| if not os.environ.get("GROQ_API_KEY"): |
| return "⚠️ Groq key missing. Please set `os.environ['GROQ_API_KEY']` in Cell 2." |
|
|
| client = Groq( |
| api_key=os.environ.get("GROQ_API_KEY"), |
| ) |
|
|
| |
| menu_summary = [] |
| for m in menu: |
| if m.get("available", True): |
| menu_summary.append({ |
| "id": m["id"], |
| "name": m["name"], |
| "category": m["category"], |
| "price_pkr": m["price"], |
| "tags": m.get("tags", []), |
| "desc": m.get("desc", ""), |
| }) |
|
|
| system_msg = ( |
| "You are a friendly restaurant assistant for undergraduate students. " |
| "Use short, clear answers. Recommend items based on budget/taste. " |
| "If user mentions allergies, advise to confirm with restaurant staff (disclaimer). " |
| "Return 3-5 suggestions max with prices in PKR. Keep it GenZ but respectful." |
| ) |
|
|
| chat_completion = client.chat.completions.create( |
| messages=[ |
| {"role": "system", "content": system_msg}, |
| {"role": "system", "content": f"MENU_JSON: {menu_summary}"}, |
| {"role": "user", "content": user_text}, |
| ], |
| model="llama-3.3-70b-versatile", |
| ) |
|
|
| return chat_completion.choices[0].message.content |
|
|
|
|
| |
| |
| |
| def init_states(): |
| menu = starter_menu() |
| cart = {} |
| orders = [] |
| return menu, cart, orders |
|
|
|
|
| |
| |
| |
| def customer_refresh(menu, cat): |
| gal = gallery_items(menu, cat) |
| choices = dropdown_choices(menu, cat) |
| df = menu_df(menu) |
| return gal, gr.update(choices=choices, value=None), df |
|
|
| def show_item_preview(menu, item_choice): |
| item_id = parse_id_from_choice(item_choice) |
| it = find_item(menu, item_id) if item_id else None |
| if not it: |
| return None, "Pick an item to preview 👀" |
| return it["image"], f"**{it['name']}**\n\n{it['desc']}\n\n**Price:** {money(it['price'])}\n\n**Tags:** {', '.join(it.get('tags', []))}" |
|
|
| def add_to_cart(menu, cart, item_choice, qty): |
| item_id = parse_id_from_choice(item_choice) |
| if not item_id: |
| return cart, "⚠️ Please select an item first.", *cart_view(menu, cart) |
| if qty is None or qty < 1: |
| return cart, "⚠️ Quantity must be at least 1.", *cart_view(menu, cart) |
|
|
| it = find_item(menu, item_id) |
| if not it or not it.get("available", True): |
| return cart, "⚠️ That item is not available right now.", *cart_view(menu, cart) |
|
|
| cart = dict(cart) |
| cart[item_id] = cart.get(item_id, 0) + int(qty) |
| msg = f"✅ Added **{it['name']}** x{qty} to cart. Let’s gooo 🛒✨" |
| return cart, msg, *cart_view(menu, cart) |
|
|
| def remove_from_cart(menu, cart, item_choice): |
| item_id = parse_id_from_choice(item_choice) |
| if not item_id: |
| return cart, "⚠️ Select an item to remove.", *cart_view(menu, cart) |
| cart = dict(cart) |
| if item_id in cart: |
| del cart[item_id] |
| it = find_item(menu, item_id) |
| name = it["name"] if it else item_id |
| return cart, f"🗑️ Removed **{name}** from cart.", *cart_view(menu, cart) |
| return cart, "ℹ️ Item not found in cart.", *cart_view(menu, cart) |
|
|
| def clear_cart(menu, cart): |
| cart = {} |
| return cart, "🧹 Cart cleared. Fresh start.", *cart_view(menu, cart) |
|
|
| def cart_view(menu, cart): |
| df, subtotal = cart_to_df(cart, menu) |
| service, tax, total = calc_totals(subtotal, service_pct=5, tax_pct=0) |
| summary = ( |
| f"**Subtotal:** {money(subtotal)} \n" |
| f"**Service (5%):** {money(service)} \n" |
| f"**Tax (0%):** {money(tax)} \n" |
| f"### **Total:** {money(total)}" |
| ) |
| return df, summary |
|
|
| def checkout(menu, cart, orders, cust_name, phone, method, address, notes): |
| |
| if not cart: |
| return orders, "⚠️ Your cart is empty. Add something tasty first." |
| if not cust_name or len(cust_name.strip()) < 2: |
| return orders, "⚠️ Please enter your name." |
| if not phone or len(phone.strip()) < 7: |
| return orders, "⚠️ Please enter a valid phone number." |
| if method == "Delivery" and (not address or len(address.strip()) < 5): |
| return orders, "⚠️ Delivery needs an address." |
|
|
| |
| df, subtotal = cart_to_df(cart, menu) |
| service, tax, total = calc_totals(subtotal, 5, 0) |
|
|
| order_id = "ORD-" + uuid.uuid4().hex[:8].upper() |
| order = { |
| "order_id": order_id, |
| "time": time.strftime("%Y-%m-%d %H:%M:%S"), |
| "name": cust_name.strip(), |
| "phone": phone.strip(), |
| "method": method, |
| "address": (address.strip() if address else ""), |
| "notes": (notes.strip() if notes else ""), |
| "items": dict(cart), |
| "subtotal": subtotal, |
| "service": service, |
| "tax": tax, |
| "total": total, |
| "status": "Pending", |
| } |
|
|
| orders = list(orders) |
| orders.insert(0, order) |
|
|
| |
| slip_lines = [f"🧾 **Order Slip — {order_id}**", f"**Time:** {order['time']}", ""] |
| for item_id, qty in cart.items(): |
| it = find_item(menu, item_id) |
| if it: |
| slip_lines.append(f"- {it['name']} x{qty} ({money(it['price'])} each)") |
| slip_lines += [ |
| "", |
| f"**Subtotal:** {money(subtotal)}", |
| f"**Service (5%):** {money(service)}", |
| f"**Total:** **{money(total)}**", |
| "", |
| f"**Method:** {method}", |
| f"**Customer:** {order['name']} ({order['phone']})", |
| ] |
| if method == "Delivery": |
| slip_lines.append(f"**Address:** {order['address']}") |
| if order["notes"]: |
| slip_lines.append(f"**Notes:** {order['notes']}") |
|
|
| slip = "\n".join(slip_lines) |
| return orders, f"✅ Order placed! {order_id} is in progress. You ate that 😌🔥\n\n{slip}" |
|
|
|
|
| |
| |
| |
| def orders_table(menu, orders): |
| rows = [] |
| for o in orders: |
| rows.append({ |
| "Order ID": o["order_id"], |
| "Time": o["time"], |
| "Customer": o["name"], |
| "Method": o["method"], |
| "Total": o["total"], |
| "Status": o["status"], |
| }) |
| df = pd.DataFrame(rows) |
| if df.empty: |
| df = pd.DataFrame([{"Order ID":"(No orders yet)", "Time":"-", "Customer":"-", "Method":"-", "Total":"-", "Status":"-"}]) |
| return df |
|
|
| def order_detail_text(menu, orders, order_id): |
| if not order_id: |
| return "Pick an Order ID to view details." |
| o = next((x for x in orders if x["order_id"] == order_id), None) |
| if not o: |
| return "Order not found." |
| lines = [ |
| f"## {o['order_id']}", |
| f"**Time:** {o['time']}", |
| f"**Customer:** {o['name']} — {o['phone']}", |
| f"**Method:** {o['method']}", |
| ] |
| if o["method"] == "Delivery": |
| lines.append(f"**Address:** {o['address']}") |
| if o.get("notes"): |
| lines.append(f"**Notes:** {o['notes']}") |
| lines.append("") |
| lines.append("### Items") |
| for item_id, qty in o["items"].items(): |
| it = find_item(menu, item_id) |
| if it: |
| lines.append(f"- {it['name']} x{qty} ({money(it['price'])} each)") |
| lines.append("") |
| lines.append(f"**Subtotal:** {money(o['subtotal'])}") |
| lines.append(f"**Service:** {money(o['service'])}") |
| lines.append(f"**Total:** **{money(o['total'])}**") |
| lines.append(f"**Status:** {o['status']}") |
| return "\n".join(lines) |
|
|
| def update_order_status(orders, order_id, new_status): |
| if not order_id: |
| return orders, "⚠️ Select an Order ID first." |
| orders = list(orders) |
| for o in orders: |
| if o["order_id"] == order_id: |
| o["status"] = new_status |
| return orders, f"✅ Status updated: {order_id} → **{new_status}**" |
| return orders, "⚠️ Order not found." |
|
|
| def admin_add_item(menu, item_id, name, category, price, desc, tags_csv, image_url, available): |
| |
| if not item_id or len(item_id.strip()) < 3: |
| return menu, "⚠️ Item ID is required (e.g., BB001).", menu_df(menu) |
| if not name or len(name.strip()) < 2: |
| return menu, "⚠️ Item name is required.", menu_df(menu) |
| if not category or len(category.strip()) < 2: |
| return menu, "⚠️ Category is required.", menu_df(menu) |
| try: |
| price = int(price) |
| if price <= 0: |
| raise ValueError() |
| except: |
| return menu, "⚠️ Price must be a positive number.", menu_df(menu) |
| if not image_url or not image_url.startswith("http"): |
| return menu, "⚠️ Image URL must be a valid http(s) link.", menu_df(menu) |
|
|
| tags = [t.strip() for t in (tags_csv or "").split(",") if t.strip()] |
|
|
| menu = list(menu) |
| |
| if any(m["id"] == item_id.strip() for m in menu): |
| return menu, "⚠️ Item ID already exists. Use Edit flow (or change ID).", menu_df(menu) |
|
|
| menu.append({ |
| "id": item_id.strip(), |
| "name": name.strip(), |
| "category": category.strip(), |
| "price": price, |
| "desc": (desc or "").strip(), |
| "tags": tags, |
| "image": image_url.strip(), |
| "available": bool(available), |
| }) |
| return menu, f"✅ Added **{name.strip()}** to menu.", menu_df(menu) |
|
|
| def admin_delete_item(menu, delete_id): |
| if not delete_id: |
| return menu, "⚠️ Provide an Item ID to delete.", menu_df(menu) |
| menu = [m for m in menu if m["id"] != delete_id.strip()] |
| return menu, f"🗑️ Deleted item **{delete_id.strip()}** (if it existed).", menu_df(menu) |
|
|
| def admin_toggle_availability(menu, toggle_id): |
| if not toggle_id: |
| return menu, "⚠️ Provide an Item ID to toggle.", menu_df(menu) |
| menu = list(menu) |
| for m in menu: |
| if m["id"] == toggle_id.strip(): |
| m["available"] = not m.get("available", True) |
| state = "Available ✅" if m["available"] else "Unavailable 🚫" |
| return menu, f"🔁 {m['name']} is now: **{state}**", menu_df(menu) |
| return menu, "⚠️ Item not found.", menu_df(menu) |
|
|
|
|
| |
| |
| |
| def chat_send(chat_history, user_msg, menu): |
| user_msg = (user_msg or "").strip() |
| if not user_msg: |
| return chat_history, "" |
| chat_history = list(chat_history) |
| chat_history.append(("You", user_msg)) |
|
|
| try: |
| bot = groq_reply(user_msg, menu) |
| except Exception as e: |
| bot = f"⚠️ LLM error: {e}" |
|
|
| chat_history.append(("Assistant", bot)) |
| return chat_history, "" |
|
|
|
|
| |
| |
| |
| menu0, cart0, orders0 = init_states() |
|
|
| with gr.Blocks( |
| title="UniBites — Restaurant Web App (Python + Gradio + Groq)", |
| theme=gr.themes.Soft(), |
| css=""" |
| .title {font-size: 26px; font-weight: 800;} |
| .subtitle {opacity: 0.85;} |
| .card {border-radius: 16px;} |
| """ |
| ) as demo: |
|
|
| menu_state = gr.State(menu0) |
| cart_state = gr.State(cart0) |
| orders_state = gr.State(orders0) |
|
|
| gr.Markdown( |
| """ |
| <div class="title">🍔 UniBites — Single Restaurant App</div> |
| <div class="subtitle">Browse • Add to cart • Checkout • Admin • AI menu helper (Groq)</div> |
| """, |
| ) |
|
|
| with gr.Tabs(): |
| |
| with gr.Tab("🍽️ Customer"): |
| with gr.Row(): |
| category = gr.Dropdown(choices=categories(menu0), value="All", label="Choose Category") |
| refresh_btn = gr.Button("🔄 Refresh Menu", variant="secondary") |
|
|
| gallery = gr.Gallery(label="Menu (tap to vibe)", columns=3, rows=2, height=360) |
| menu_table = gr.Dataframe(label="Menu Table (for clarity)", interactive=False) |
|
|
| with gr.Row(): |
| item_choice = gr.Dropdown(choices=dropdown_choices(menu0, "All"), label="Pick Item (for cart)") |
| qty = gr.Slider(1, 10, value=1, step=1, label="Qty") |
| with gr.Row(): |
| preview_img = gr.Image(label="Preview Image", height=220) |
| preview_txt = gr.Markdown("Pick an item to preview 👀") |
|
|
| with gr.Row(): |
| add_btn = gr.Button("➕ Add to Cart", variant="primary") |
| remove_btn = gr.Button("➖ Remove from Cart", variant="secondary") |
| clear_btn = gr.Button("🧹 Clear Cart", variant="secondary") |
|
|
| cart_msg = gr.Markdown("") |
| cart_df = gr.Dataframe(label="Cart", interactive=False) |
| cart_summary = gr.Markdown("") |
|
|
| gr.Markdown("## ✅ Checkout (no stress, just fill basics)") |
| with gr.Row(): |
| cust_name = gr.Textbox(label="Your Name", placeholder="e.g., Ali / Ayesha") |
| phone = gr.Textbox(label="Phone", placeholder="03xx-xxxxxxx") |
| method = gr.Radio(["Delivery", "Pickup"], value="Delivery", label="Delivery or Pickup?") |
| address = gr.Textbox(label="Address (required for delivery)", placeholder="Hostel / home address") |
| notes = gr.Textbox(label="Notes (optional)", placeholder="Less spicy / no onions / etc.") |
| place_btn = gr.Button("🧾 Place Order", variant="primary") |
| order_out = gr.Markdown("") |
|
|
| |
| with gr.Tab("🧑🍳 Admin"): |
| gr.Markdown("### Admin Panel (simple, beginner-friendly)") |
| admin_menu_df = gr.Dataframe(label="Current Menu", interactive=False) |
|
|
| with gr.Accordion("➕ Add New Menu Item", open=False): |
| with gr.Row(): |
| a_id = gr.Textbox(label="Item ID", placeholder="e.g., SD010") |
| a_name = gr.Textbox(label="Name", placeholder="e.g., Garlic Bread") |
| a_cat = gr.Textbox(label="Category", placeholder="e.g., Sides") |
| with gr.Row(): |
| a_price = gr.Number(label="Price (PKR)", value=399) |
| a_tags = gr.Textbox(label="Tags (comma separated)", placeholder="veg, halal") |
| a_avail = gr.Checkbox(label="Available", value=True) |
| a_desc = gr.Textbox(label="Description", placeholder="Short description for customers") |
| a_img = gr.Textbox(label="Image URL", placeholder="https://... (public image link)") |
| add_item_btn = gr.Button("✅ Add Item", variant="primary") |
| admin_msg = gr.Markdown("") |
|
|
| with gr.Accordion("🗑️ Delete Item / 🔁 Toggle Availability", open=False): |
| with gr.Row(): |
| del_id = gr.Textbox(label="Item ID to Delete", placeholder="e.g., SD010") |
| del_btn = gr.Button("🗑️ Delete", variant="secondary") |
| with gr.Row(): |
| tog_id = gr.Textbox(label="Item ID to Toggle", placeholder="e.g., SD001") |
| tog_btn = gr.Button("🔁 Toggle Availability", variant="secondary") |
| admin_msg2 = gr.Markdown("") |
|
|
| gr.Markdown("### Orders") |
| orders_df = gr.Dataframe(label="Orders List", interactive=False) |
| with gr.Row(): |
| order_id_pick = gr.Dropdown(choices=[], label="Select Order ID") |
| new_status = gr.Dropdown( |
| ["Pending", "Preparing", "Ready", "Completed", "Cancelled"], |
| value="Preparing", |
| label="Update Status" |
| ) |
| with gr.Row(): |
| view_order_btn = gr.Button("👀 View Order Detail", variant="secondary") |
| update_status_btn = gr.Button("✅ Update Status", variant="primary") |
|
|
| order_detail = gr.Markdown("Pick an order to see details.") |
| admin_order_msg = gr.Markdown("") |
|
|
| |
| with gr.Tab("🤖 AI Assistant"): |
| gr.Markdown( |
| """ |
| ### Ask me anything about the menu 😌 |
| Try: |
| - “I have Rs 800, recommend something spicy” |
| - “Suggest high-protein items” |
| - “I’m lactose intolerant, what’s safe?” (I’ll still tell you to confirm with staff) |
| """ |
| ) |
| chatbot = gr.Chatbot(label="Chat", height=360) |
| user_msg = gr.Textbox(label="Message", placeholder="Type here…") |
| send_btn = gr.Button("Send 🚀", variant="primary") |
|
|
|
|
| |
| def initial_load(menu): |
| cat = "All" |
| gal = gallery_items(menu, cat) |
| choices = dropdown_choices(menu, cat) |
| return ( |
| gal, |
| gr.update(choices=categories(menu), value=cat), |
| gr.update(choices=choices, value=None), |
| menu_df(menu), |
| menu_df(menu), |
| orders_table(menu, []), |
| gr.update(choices=[], value=None), |
| *cart_view(menu, {}) |
| ) |
|
|
| demo.load( |
| initial_load, |
| inputs=[menu_state], |
| outputs=[gallery, category, item_choice, menu_table, admin_menu_df, orders_df, order_id_pick, cart_df, cart_summary] |
| ) |
|
|
| |
| refresh_btn.click( |
| customer_refresh, |
| inputs=[menu_state, category], |
| outputs=[gallery, item_choice, menu_table] |
| ) |
| category.change( |
| customer_refresh, |
| inputs=[menu_state, category], |
| outputs=[gallery, item_choice, menu_table] |
| ) |
| item_choice.change( |
| show_item_preview, |
| inputs=[menu_state, item_choice], |
| outputs=[preview_img, preview_txt] |
| ) |
|
|
| add_btn.click( |
| add_to_cart, |
| inputs=[menu_state, cart_state, item_choice, qty], |
| outputs=[cart_state, cart_msg, cart_df, cart_summary] |
| ) |
| remove_btn.click( |
| remove_from_cart, |
| inputs=[menu_state, cart_state, item_choice], |
| outputs=[cart_state, cart_msg, cart_df, cart_summary] |
| ) |
| clear_btn.click( |
| clear_cart, |
| inputs=[menu_state, cart_state], |
| outputs=[cart_state, cart_msg, cart_df, cart_summary] |
| ) |
|
|
| place_btn.click( |
| checkout, |
| inputs=[menu_state, cart_state, orders_state, cust_name, phone, method, address, notes], |
| outputs=[orders_state, order_out] |
| ).then( |
| lambda menu, orders: (orders_table(menu, orders), gr.update(choices=[o["order_id"] for o in orders], value=(orders[0]["order_id"] if orders else None))), |
| inputs=[menu_state, orders_state], |
| outputs=[orders_df, order_id_pick] |
| ).then( |
| lambda menu: (*cart_view(menu, {}), {}), |
| inputs=[menu_state], |
| outputs=[cart_df, cart_summary, cart_state] |
| ) |
|
|
| |
| add_item_btn.click( |
| admin_add_item, |
| inputs=[menu_state, a_id, a_name, a_cat, a_price, a_desc, a_tags, a_img, a_avail], |
| outputs=[menu_state, admin_msg, admin_menu_df] |
| ).then( |
| customer_refresh, |
| inputs=[menu_state, category], |
| outputs=[gallery, item_choice, menu_table] |
| ) |
|
|
| del_btn.click( |
| admin_delete_item, |
| inputs=[menu_state, del_id], |
| outputs=[menu_state, admin_msg2, admin_menu_df] |
| ).then( |
| customer_refresh, |
| inputs=[menu_state, category], |
| outputs=[gallery, item_choice, menu_table] |
| ) |
|
|
| tog_btn.click( |
| admin_toggle_availability, |
| inputs=[menu_state, tog_id], |
| outputs=[menu_state, admin_msg2, admin_menu_df] |
| ).then( |
| customer_refresh, |
| inputs=[menu_state, category], |
| outputs=[gallery, item_choice, menu_table] |
| ) |
|
|
| view_order_btn.click( |
| order_detail_text, |
| inputs=[menu_state, orders_state, order_id_pick], |
| outputs=[order_detail] |
| ) |
|
|
| update_status_btn.click( |
| update_order_status, |
| inputs=[orders_state, order_id_pick, new_status], |
| outputs=[orders_state, admin_order_msg] |
| ).then( |
| lambda menu, orders: (orders_table(menu, orders)), |
| inputs=[menu_state, orders_state], |
| outputs=[orders_df] |
| ).then( |
| order_detail_text, |
| inputs=[menu_state, orders_state, order_id_pick], |
| outputs=[order_detail] |
| ) |
|
|
| |
| send_btn.click( |
| chat_send, |
| inputs=[chatbot, user_msg, menu_state], |
| outputs=[chatbot, user_msg] |
| ) |
| user_msg.submit( |
| chat_send, |
| inputs=[chatbot, user_msg, menu_state], |
| outputs=[chatbot, user_msg] |
| ) |
|
|
| demo.launch(share=True, debug=True) |