DynamicMenu3 / app.py
nagasurendra's picture
Update app.py
b6b08be verified
raw
history blame
23.1 kB
import gradio as gr
import pandas as pd
from bcrypt import hashpw, gensalt, checkpw
import bcrypt
import gradio as gr
from simple_salesforce import Salesforce
# Salesforce Connection
# Salesforce Connection
sf = Salesforce(username='surendra@sathkrutha.com',
password='Lavanyanaga@123',
security_token='z7Wvk6mys7n8XjqbYKf3bwBh7')
def save_user(name, phone, email, password):
"""Save user details to Salesforce."""
try:
# Check if email already exists in Salesforce
query = f"SELECT Id FROM User_Login__c WHERE Email__c = '{email}'"
result = sf.query(query)
if len(result['records']) > 0:
# Email already exists
return False, "Email already exists. Please use a different email."
# Hash the password
hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
# Create the new user record in Salesforce
sf.User_Login__c.create({
'Name__c': name.strip(),
'Phone__c': phone.strip(),
'Email__c': email.strip(),
'Password3__c': hashed_password # Use Password3__c to store hashed passwords
})
return True, "Signup successful! You can now login."
except Exception as e:
print(f"Error saving user to Salesforce: {e}")
return False, "An error occurred during signup. Please try again."
def check_credentials(email, password):
"""Check user credentials during login."""
try:
# Query Salesforce for user details
query = f"SELECT Password3__c FROM User_Login__c WHERE Email__c = '{email}'"
result = sf.query(query)
if len(result['records']) == 0:
# Email not found
return False, "Invalid email or password."
# Retrieve the stored hashed password
stored_password = result['records'][0]['Password3__c']
# Verify the entered password with the stored hashed password
if bcrypt.checkpw(password.encode(), stored_password.encode()):
return True, "Login successful!"
else:
return False, "Invalid email or password."
except Exception as e:
print(f"Error checking credentials: {e}")
return False, "An error occurred during login. Please try again."
# Function to load the menu data
def load_menu():
menu_file = "menu.xlsx" # Ensure this file exists in the same directory
try:
return pd.read_excel(menu_file)
except Exception as e:
raise ValueError(f"Error loading menu file: {e}")
# Function to filter menu items based on preference
def filter_menu(preference):
menu_data = load_menu()
if preference == "Halal/Non-Veg":
filtered_data = menu_data[menu_data["Ingredients"].str.contains("Chicken|Mutton|Fish|Prawns|Goat", case=False, na=False)]
elif preference == "Vegetarian":
filtered_data = menu_data[~menu_data["Ingredients"].str.contains("Chicken|Mutton|Fish|Prawns|Goat", case=False, na=False)]
elif preference == "Guilt-Free":
filtered_data = menu_data[menu_data["Description"].str.contains(r"Fat: ([0-9]|10)g", case=False, na=False)]
else:
filtered_data = menu_data
html_content = ""
for _, item in filtered_data.iterrows():
html_content += f"""
<div style="display: flex; align-items: center; border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin-bottom: 10px; box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);">
<div style="flex: 1; margin-right: 15px;">
<h3 style="margin: 0; font-size: 18px;">{item['Dish Name']}</h3>
<p style="margin: 5px 0; font-size: 16px; color: #888;">${item['Price ($)']}</p>
<p style="margin: 5px 0; font-size: 14px; color: #555;">{item['Description']}</p>
</div>
<div style="flex-shrink: 0; text-align: center;">
<img src="{item['Image URL']}" alt="{item['Dish Name']}" style="width: 100px; height: 100px; border-radius: 8px; object-fit: cover; margin-bottom: 10px;">
<button style="background-color: #28a745; color: white; border: none; padding: 8px 15px; font-size: 14px; border-radius: 5px; cursor: pointer;" onclick="openModal('{item['Dish Name']}', '{item['Image 2 URL']}', '{item['Description']}', '{item['Price ($)']}')">Add</button>
</div>
</div>
"""
return html_content
# Preserving your JavaScript for modal and cart functionality (Your original JavaScript logic)
modal_and_cart_js = """
<style>
.cart-container {
width: 90%; /* Ensure it fits the screen */
margin: auto;
display: flex;
flex-direction: column;
gap: 10px; /* Add space between cart items */
}
.cart-item {
display: flex;
flex-wrap: wrap; /* Wrap content to the next line if needed */
align-items: center; /* Vertically align items */
justify-content: space-between; /* Distribute space between items */
width: 100%; /* Ensure it takes full width */
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
}
.cart-item span {
margin-right: 10px;
flex: 1; /* Allow text to take available space */
min-width: 50px; /* Prevent collapsing */
}
.cart-item .quantity-container {
display: flex;
align-items: center;
gap: 5px;
}
.cart-item button {
background-color: red;
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
border-radius: 5px;
flex-shrink: 0; /* Prevent the button from shrinking */
}
.cart-total {
font-size: 1.2em;
font-weight: bold;
text-align: center;
}
button {
margin-top: 10px;
background-color: #007bff;
color: white;
border: none;
padding: 10px;
border-radius: 5px;
width: 100%;
cursor: pointer;
}
@media (max-width: 768px) {
.cart-item {
flex-direction: column; /* Stack items on mobile */
align-items: flex-start; /* Align to the left */
}
.cart-item button {
align-self: flex-end; /* Place the remove button at the end */
margin-top: 5px; /* Add some space on top */
}
}
</style>
<script>
let cart = [];
const extrasPrices = {
"Thums up": 2,
"Sprite": 2,
"Extra Raitha": 1,
"Extra Salan": 2,
"Extra Onion & Lemon": 2,
"Chilli Chicken": 14,
"Veg Manchurian": 12
};
let finalized = false;
function openModal(name, image2, description, price) {
if (finalized) {
alert("You cannot add more items after finalizing your order.");
return;
}
const modal = document.getElementById('modal');
modal.style.display = 'block';
modal.style.position = 'fixed';
if (window.innerWidth <= 768) {
modal.style.width = '90%';
modal.style.top = `${event.touches ? event.touches[0].screenY : event.clientY}px`;
} else {
modal.style.width = '30%';
modal.style.top = `${event.clientY}px`;// Use mouse Y position for laptop
}
modal.style.left = '50%';
modal.style.transform = 'translate(-50%, -50%)';
document.getElementById('modal-image').src = image2;
document.getElementById('modal-name').innerText = name;
document.getElementById('modal-description').innerText = description;
document.getElementById('modal-price').innerText = price;
const extrasInputs = document.querySelectorAll('input[name="biryani-extra"]');
extrasInputs.forEach(input => input.checked = false);
document.getElementById('quantity').value = 1;
document.getElementById('special-instructions').value = '';
}
function closeModal() {
document.getElementById('modal').style.display = 'none';
}
function addToCart() {
if (finalized) {
alert("You cannot add more items after finalizing your order.");
return;
}
const name = document.getElementById('modal-name').innerText;
const price = parseFloat(document.getElementById('modal-price').innerText.replace('$', ''));
const quantity = parseInt(document.getElementById('quantity').value) || 1;
const instructions = document.getElementById('special-instructions').value;
const extras = Array.from(document.querySelectorAll('input[name="biryani-extra"]:checked')).map(extra => extra.value);
const extrasCost = extras.reduce((sum, extra) => sum + (extrasPrices[extra] || 0), 0);
const itemTotal = (price + extrasCost) * quantity;
const cartItem = { name, price, quantity, instructions, extras, itemTotal, extrasQuantities: extras.map(() => 1) };
cart.push(cartItem);
alert(`${name} added to cart!`);
updateCartDisplay();
closeModal();
}
function updateCartDisplay() {
let totalBill = 0;
let cartHTML = "<div class='cart-container'>";
cart.forEach((item, index) => {
totalBill += item.itemTotal;
const extras = item.extras.map((extra, i) => {
const extraQuantity = item.extrasQuantities ? item.extrasQuantities[i] || 1 : 1;
const extraTotal = extrasPrices[extra] * extraQuantity;
totalBill += extraTotal;
return `<div class='cart-item'>
<span>${extra}</span>
<span>Price: $${extrasPrices[extra].toFixed(2)}</span>
<div class='quantity-container'>
<label for='extra-quantity-${index}-${i}'>Quantity:</label>
<input type='number' id='extra-quantity-${index}-${i}' value='${extraQuantity}' min='1' style='width: 50px;' onchange='updateExtraQuantity(${index}, ${i}, this.value)'>
</div>
<span>Total: $${extraTotal.toFixed(2)}</span>
<button style='background-color: red; color: white; border: none; padding: 5px 10px; cursor: pointer;' onclick='removeExtra(${index}, ${i})'>Remove</button>
</div>`;
}).join('');
cartHTML += `<div class='cart-item'>
<span>${item.name}</span>
<span>Item Price: $${item.price.toFixed(2)}</span>
<div class='quantity-container'>
<label for='item-quantity-${index}'>Quantity:</label>
<input type='number' id='item-quantity-${index}' value='${item.quantity}' min='1' style='width: 50px;' onchange='updateItemQuantity(${index}, this.value)'>
</div>
<span>Total: $${(item.price * item.quantity).toFixed(2)}</span>
<button style='background-color: red; color: white; border: none; padding: 5px 10px; cursor: pointer;' onclick='removeItem(${index})'>Remove</button>
</div>
${extras}
<div class='cart-item'><strong>Instructions:</strong> ${item.instructions || "None"}</div>`;
});
cartHTML += `</div><p class='cart-total'>Total Bill: $${totalBill.toFixed(2)}</p>`;
cartHTML += `<button style='margin-top: 10px; background-color: #007bff; color: white; border: none; padding: 10px; border-radius: 5px; width: 100%; cursor: pointer;' onclick='submitCart()'>Submit</button>`;
document.getElementById('floating-cart').innerHTML = cartHTML;
}
function updateItemQuantity(index, newQuantity) {
const quantity = parseInt(newQuantity) || 1;
cart[index].quantity = quantity;
cart[index].itemTotal = cart[index].price * quantity;
updateCartDisplay();
}
function updateExtraQuantity(cartIndex, extraIndex, newQuantity) {
const quantity = parseInt(newQuantity) || 1;
cart[cartIndex].extrasQuantities = cart[cartIndex].extrasQuantities || [];
cart[cartIndex].extrasQuantities[extraIndex] = quantity;
updateCartDisplay();
}
function removeExtra(cartIndex, extraIndex) {
cart[cartIndex].extras.splice(extraIndex, 1);
if (cart[cartIndex].extrasQuantities) {
cart[cartIndex].extrasQuantities.splice(extraIndex, 1);
}
updateCartDisplay();
}
function removeItem(index) {
cart.splice(index, 1);
updateCartDisplay();
}
function submitCart() {
let finalOrderHTML = "<h3>Final Order:</h3><ul>";
let totalBill = 0;
cart.forEach(item => {
totalBill += item.itemTotal;
const extras = item.extras.map((extra, i) => {
const extraQuantity = item.extrasQuantities ? item.extrasQuantities[i] || 1 : 1;
const extraTotal = extrasPrices[extra] * extraQuantity;
totalBill += extraTotal;
return `${extra} (x${extraQuantity}) - $${extraTotal.toFixed(2)}`;
}).join(', ');
finalOrderHTML += `<li>
${item.name} (x${item.quantity}) - $${item.itemTotal.toFixed(2)}
<br>Extras: ${extras}
<br>Instructions: ${item.instructions || "None"}
</li>`;
});
finalOrderHTML += `</ul><p><strong>Total Bill: $${totalBill.toFixed(2)}</strong></p>`;
document.getElementById('final-order').innerHTML = finalOrderHTML;
// Trigger navigation to the final order page
const finalOrderPage = document.getElementById('final-order-section');
const cartPage = document.getElementById('cart-section');
cartPage.style.display = 'none';
finalOrderPage.style.display = 'block';
}
</script>
"""
# Authentication and Navigation Logic
def authenticate_user(email, password):
if check_credentials(email, password):
return gr.update(visible=False), gr.update(visible=True), ""
else:
return gr.update(visible=True), gr.update(visible=False), "Invalid email or password. Try again."
def navigate_to_signup():
return gr.update(visible=False), gr.update(visible=True)
def create_account(name, phone, email, password):
if save_user(name, phone, email, password):
return "Account created successfully! You can now log in.", gr.update(visible=True), gr.update(visible=False)
else:
return "Email already exists. Try logging in.", gr.update(visible=False), gr.update(visible=True)
def navigate_to_login():
return gr.update(visible=True), gr.update(visible=False)
# Gradio App
def app():
with gr.Blocks() as demo:
# Login Page
with gr.Column(visible=True) as login_section:
gr.Markdown("# Login Page")
login_email = gr.Textbox(label="Email", placeholder="Enter your email")
login_password = gr.Textbox(label="Password", placeholder="Enter your password", type="password")
login_error = gr.Label("")
login_button = gr.Button("Login")
go_to_signup = gr.Button("Create an Account")
# Signup Page
with gr.Column(visible=False) as signup_section:
gr.Markdown("# Signup Page")
signup_name = gr.Textbox(label="Name", placeholder="Enter your full name")
signup_phone = gr.Textbox(label="Phone", placeholder="Enter your phone number")
signup_email = gr.Textbox(label="Email", placeholder="Enter your email")
signup_password = gr.Textbox(label="Password", placeholder="Enter your password", type="password")
signup_message = gr.Label("")
signup_button = gr.Button("Sign Up")
go_to_login = gr.Button("Back to Login")
# Menu Page
with gr.Column(visible=False) as menu_section:
gr.Markdown("### Menu Page (Accessible Only After Login)")
# View Cart Button (Top Position)
view_cart_button_top = gr.Button("View Cart")
# Radio button for selecting preference
selected_preference = gr.Radio(
choices=["All", "Vegetarian", "Halal/Non-Veg", "Guilt Free"],
value="All",
label="Choose a Preference",
)
empty_div = gr.HTML('<div style="height: 20px;"></div>')
# Output area for menu items
menu_output = gr.HTML(value=filter_menu("All"))
# View Cart Button (Original Position)
view_cart_button_bottom = gr.Button("View Cart")
empty_div = gr.HTML('<div style="height: 300px;"></div>')
# Modal window
modal_window = gr.HTML("""
<div id="modal" style="display: none; position: fixed; background: white; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); padding: 20px; z-index: 1000;">
<div style="text-align: right;">
<button onclick="closeModal()" style="background: none; border: none; font-size: 18px; cursor: pointer;">&times;</button>
</div>
<img id="modal-image" style="width: 100%; height: 300px; border-radius: 8px; margin-bottom: 20px;" />
<h2 id="modal-name"></h2>
<p id="modal-description"></p>
<p id="modal-price"></p>
<!-- Biryani Extras -->
<label for="biryani-extras"><strong>Add-ons :</strong></label>
<div id="biryani-extras-options" style="display: flex; flex-wrap: wrap; gap: 10px; margin: 10px 0;">
<label><input type="checkbox" name="biryani-extra" value="Thums up" /> Thums up + $2.00</label>
<label><input type="checkbox" name="biryani-extra" value="Sprite" /> Sprite + $2.00</label>
<label><input type="checkbox" name="biryani-extra" value="Extra Raitha" /> Extra Raitha + $1.00</label>
<label><input type="checkbox" name="biryani-extra" value="Extra Salan" /> Extra Salan + $2.00</label>
<label><input type="checkbox" name="biryani-extra" value="Extra Onion & Lemon" /> Extra Onion & Lemon + $2.00</label>
<label><input type="checkbox" name="biryani-extra" value="Chilli Chicken" /> Chilli Chicken + $14.00</label>
<label><input type="checkbox" name="biryani-extra" value="Veg Manchurian" /> Veg Manchurian + $12.00</label>
</div>
<!-- Quantity and Special Instructions -->
<label for="quantity">Quantity:</label>
<input type="number" id="quantity" value="1" min="1" style="width: 50px;" />
<br><br>
<textarea id="special-instructions" placeholder="Add your special instructions here..." style="width: 100%; height: 60px;"></textarea>
<br><br>
<!-- Add to Cart Button -->
<button style="background-color: #28a745; color: white; border: none; padding: 10px 20px; font-size: 14px; border-radius: 5px; cursor: pointer;" onclick="addToCart()">Add to Cart</button>
</div>
""")
# Update menu dynamically based on preference
selected_preference.change(filter_menu, inputs=[selected_preference], outputs=[menu_output])
# Layout
gr.Row(view_cart_button_top) # View Cart button at the top
gr.Row([selected_preference])
gr.Row(menu_output)
gr.Row(view_cart_button_bottom) # View Cart button at the bottom
gr.Row(modal_window)
gr.HTML(modal_and_cart_js)
# Cart & Final Order Page
with gr.Column(visible=False, elem_id="cart-section") as cart_section:
gr.Markdown("### Cart Page")
cart_output = gr.HTML(value="Your cart is empty.", elem_id="floating-cart")
#submitCart_button = gr.Button("Submit Cart") # Keep only this button
back_to_menu_button = gr.Button("Back to Menu")
with gr.Column(visible=False, elem_id="final-order-section") as final_order_section:
gr.Markdown("### Final Order")
final_order_output = gr.HTML(value="", elem_id="final-order")
# Floating cart display
#cart_output = gr.HTML(value="Your cart is empty.", elem_id="floating-cart")
# Final order display
#final_order_output = gr.HTML(value="", elem_id="final-order")
# Button to navigate back to Menu Page
#back_to_menu_button = gr.Button("Back to Menu")
gr.Row(cart_output)
gr.Row(final_order_output)
# Button Bindings
# Login Button
login_button.click(
lambda email, password: (gr.update(visible=False), gr.update(visible=True), "") if check_credentials(email, password) else (gr.update(), gr.update(), "Invalid email or password."),
inputs=[login_email, login_password],
outputs=[login_section, menu_section, login_error],
)
# Signup Button
signup_button.click(
lambda name, phone, email, password: ("Signup successful! Please login.", gr.update(visible=True), gr.update(visible=False)) if save_user(name, phone, email, password) else ("Email already exists.", gr.update(visible=False), gr.update(visible=True)),
inputs=[signup_name, signup_phone, signup_email, signup_password],
outputs=[signup_message, login_section, signup_section],
)
# Navigate to Signup Page
go_to_signup.click(
lambda: (gr.update(visible=False), gr.update(visible=True)),
outputs=[login_section, signup_section],
)
# Navigate Back to Login Page
go_to_login.click(
lambda: (gr.update(visible=True), gr.update(visible=False)),
outputs=[login_section, signup_section],
)
# Navigate to Cart Page (Both Buttons Use the Same Logic)
view_cart_button_top.click(
lambda: (gr.update(visible=False), gr.update(visible=True)),
outputs=[menu_section, cart_section],
)
view_cart_button_bottom.click(
lambda: (gr.update(visible=False), gr.update(visible=True)),
outputs=[menu_section, cart_section],
)
# Navigate to Final Order Page
# Navigate to Final Order Page
#submitCart_button.click(
#lambda: (gr.update(visible=False), gr.update(visible=True)),
#outputs=[cart_section, final_order_section],
#)
# Navigate Back to Menu Page
back_to_menu_button.click(
lambda: (gr.update(visible=True), gr.update(visible=False)),
outputs=[menu_section, cart_section], # Show menu, hide cart
)
return demo
if __name__ == "__main__":
app().launch()