Spaces:
Sleeping
Sleeping
{% extends "base.html" %} | |
{% block title %}Dishes Management - Admin Dashboard{% endblock %} | |
{% block content %} | |
<div class="container mt-4"> | |
<div class="row"> | |
<div class="col-12"> | |
<div class="d-flex justify-content-between align-items-center mb-4"> | |
<h2> | |
<i class="fas fa-utensils me-2"></i> | |
Dishes Management | |
</h2> | |
<div class="d-flex gap-2"> | |
<a href="/admin" class="btn btn-outline-secondary"> | |
<i class="fas fa-arrow-left me-1"></i>Back to Dashboard | |
</a> | |
<button class="btn btn-success" onclick="addNewDish()"> | |
<i class="fas fa-plus me-1"></i>Add New Dish | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Filters and Search --> | |
<div class="row mb-4"> | |
<div class="col-12"> | |
<div class="card"> | |
<div class="card-body"> | |
<div class="row align-items-center"> | |
<div class="col-md-3"> | |
<label class="form-label">Search Dishes:</label> | |
<input type="text" class="form-control" id="searchInput" | |
placeholder="Search by name..." onkeyup="filterDishes()"> | |
</div> | |
<div class="col-md-2"> | |
<label class="form-label">Category:</label> | |
<select class="form-select" id="categoryFilter" onchange="filterDishes()"> | |
<option value="">All Categories</option> | |
<option value="starters">Starters</option> | |
<option value="main-course">Main Course</option> | |
<option value="beverages">Beverages</option> | |
<option value="desserts">Desserts</option> | |
</select> | |
</div> | |
<div class="col-md-2"> | |
<label class="form-label">Availability:</label> | |
<select class="form-select" id="availabilityFilter" onchange="filterDishes()"> | |
<option value="">All</option> | |
<option value="available">Available</option> | |
<option value="unavailable">Unavailable</option> | |
</select> | |
</div> | |
<div class="col-md-2"> | |
<label class="form-label">Price Range:</label> | |
<select class="form-select" id="priceFilter" onchange="filterDishes()"> | |
<option value="">All Prices</option> | |
<option value="0-100">₹0 - ₹100</option> | |
<option value="100-300">₹100 - ₹300</option> | |
<option value="300+">₹300+</option> | |
</select> | |
</div> | |
<div class="col-md-3 d-flex align-items-end gap-2"> | |
<button class="btn btn-outline-secondary flex-fill" onclick="clearFilters()"> | |
<i class="fas fa-times me-1"></i>Clear | |
</button> | |
<button class="btn btn-outline-primary flex-fill" onclick="exportDishes()"> | |
<i class="fas fa-download me-1"></i>Export | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Dishes Grid --> | |
<div class="row" id="dishes-container"> | |
<!-- Sample Dish 1 --> | |
<div class="col-md-4 col-sm-6 mb-4 dish-item" data-category="main-course" data-price="250" data-available="true"> | |
<div class="card h-100"> | |
<img src="https://images.unsplash.com/photo-1563379091339-03246963d96c?w=400&h=200&fit=crop" | |
class="card-img-top" style="height: 200px; object-fit: cover;" alt="Chicken Biryani"> | |
<div class="card-body"> | |
<div class="d-flex justify-content-between align-items-start mb-2"> | |
<h6 class="card-title mb-0">Chicken Biryani</h6> | |
<div class="d-flex gap-1"> | |
<span class="badge bg-warning">Main Course</span> | |
<span class="badge bg-success">Available</span> | |
</div> | |
</div> | |
<p class="card-text text-muted small"> | |
Aromatic basmati rice cooked with tender chicken and traditional spices | |
</p> | |
<div class="d-flex justify-content-between align-items-center mb-3"> | |
<strong class="text-success fs-5">₹250</strong> | |
<div class="d-flex gap-1"> | |
<i class="fas fa-star text-warning"></i> | |
<span class="small">4.5 (24 reviews)</span> | |
</div> | |
</div> | |
<div class="d-flex gap-2"> | |
<button class="btn btn-primary btn-sm flex-fill" onclick="editDish(1)"> | |
<i class="fas fa-edit me-1"></i>Edit | |
</button> | |
<button class="btn btn-warning btn-sm" onclick="toggleAvailability(1)"> | |
<i class="fas fa-eye-slash me-1"></i> | |
</button> | |
<button class="btn btn-danger btn-sm" onclick="deleteDish(1)"> | |
<i class="fas fa-trash me-1"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Sample Dish 2 --> | |
<div class="col-md-4 col-sm-6 mb-4 dish-item" data-category="starters" data-price="180" data-available="true"> | |
<div class="card h-100"> | |
<img src="https://images.unsplash.com/photo-1599487488170-d11ec9c172f0?w=400&h=200&fit=crop" | |
class="card-img-top" style="height: 200px; object-fit: cover;" alt="Paneer Tikka"> | |
<div class="card-body"> | |
<div class="d-flex justify-content-between align-items-start mb-2"> | |
<h6 class="card-title mb-0">Paneer Tikka</h6> | |
<div class="d-flex gap-1"> | |
<span class="badge bg-primary">Starters</span> | |
<span class="badge bg-success">Available</span> | |
</div> | |
</div> | |
<p class="card-text text-muted small"> | |
Marinated cottage cheese cubes grilled to perfection with Indian spices | |
</p> | |
<div class="d-flex justify-content-between align-items-center mb-3"> | |
<strong class="text-success fs-5">₹180</strong> | |
<div class="d-flex gap-1"> | |
<i class="fas fa-star text-warning"></i> | |
<span class="small">4.2 (18 reviews)</span> | |
</div> | |
</div> | |
<div class="d-flex gap-2"> | |
<button class="btn btn-primary btn-sm flex-fill" onclick="editDish(2)"> | |
<i class="fas fa-edit me-1"></i>Edit | |
</button> | |
<button class="btn btn-warning btn-sm" onclick="toggleAvailability(2)"> | |
<i class="fas fa-eye-slash me-1"></i> | |
</button> | |
<button class="btn btn-danger btn-sm" onclick="deleteDish(2)"> | |
<i class="fas fa-trash me-1"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Sample Dish 3 --> | |
<div class="col-md-4 col-sm-6 mb-4 dish-item" data-category="beverages" data-price="80" data-available="false"> | |
<div class="card h-100"> | |
<img src="https://images.unsplash.com/photo-1571091718767-18b5b1457add?w=400&h=200&fit=crop" | |
class="card-img-top" style="height: 200px; object-fit: cover;" alt="Mango Lassi"> | |
<div class="card-body"> | |
<div class="d-flex justify-content-between align-items-start mb-2"> | |
<h6 class="card-title mb-0">Mango Lassi</h6> | |
<div class="d-flex gap-1"> | |
<span class="badge bg-info">Beverages</span> | |
<span class="badge bg-danger">Unavailable</span> | |
</div> | |
</div> | |
<p class="card-text text-muted small"> | |
Refreshing yogurt-based drink with fresh mango pulp and a hint of cardamom | |
</p> | |
<div class="d-flex justify-content-between align-items-center mb-3"> | |
<strong class="text-success fs-5">₹80</strong> | |
<div class="d-flex gap-1"> | |
<i class="fas fa-star text-warning"></i> | |
<span class="small">4.7 (35 reviews)</span> | |
</div> | |
</div> | |
<div class="d-flex gap-2"> | |
<button class="btn btn-primary btn-sm flex-fill" onclick="editDish(3)"> | |
<i class="fas fa-edit me-1"></i>Edit | |
</button> | |
<button class="btn btn-success btn-sm" onclick="toggleAvailability(3)"> | |
<i class="fas fa-eye me-1"></i> | |
</button> | |
<button class="btn btn-danger btn-sm" onclick="deleteDish(3)"> | |
<i class="fas fa-trash me-1"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Empty State --> | |
<div id="no-dishes" class="text-center py-5" style="display: none;"> | |
<i class="fas fa-search fa-3x text-muted mb-3"></i> | |
<p class="text-muted">No dishes match your filters</p> | |
<button class="btn btn-primary" onclick="clearFilters()"> | |
Clear Filters | |
</button> | |
</div> | |
</div> | |
<!-- Add/Edit Dish Modal --> | |
<div class="modal fade" id="dishModal" tabindex="-1"> | |
<div class="modal-dialog modal-lg"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h5 class="modal-title" id="dishModalTitle"> | |
<i class="fas fa-plus me-2"></i>Add New Dish | |
</h5> | |
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> | |
</div> | |
<div class="modal-body"> | |
<form id="dishForm"> | |
<div class="row"> | |
<div class="col-md-6"> | |
<div class="mb-3"> | |
<label class="form-label">Dish Name *</label> | |
<input type="text" class="form-control" id="dishName" required> | |
</div> | |
</div> | |
<div class="col-md-6"> | |
<div class="mb-3"> | |
<label class="form-label">Category *</label> | |
<select class="form-select" id="dishCategory" required> | |
<option value="">Select Category</option> | |
<option value="starters">Starters</option> | |
<option value="main-course">Main Course</option> | |
<option value="beverages">Beverages</option> | |
<option value="desserts">Desserts</option> | |
</select> | |
</div> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-md-6"> | |
<div class="mb-3"> | |
<label class="form-label">Price (₹) *</label> | |
<input type="number" class="form-control" id="dishPrice" min="0" step="0.01" required> | |
</div> | |
</div> | |
<div class="col-md-6"> | |
<div class="mb-3"> | |
<label class="form-label">Cooking Time (minutes)</label> | |
<input type="number" class="form-control" id="cookingTime" min="0"> | |
</div> | |
</div> | |
</div> | |
<div class="mb-3"> | |
<label class="form-label">Description</label> | |
<textarea class="form-control" id="dishDescription" rows="3" | |
placeholder="Describe the dish..."></textarea> | |
</div> | |
<div class="row"> | |
<div class="col-md-6"> | |
<div class="mb-3"> | |
<label class="form-label">Image Upload</label> | |
<input type="file" class="form-control" id="dishImage" accept="image/*"> | |
<small class="text-muted">Upload dish image (JPG, PNG)</small> | |
</div> | |
</div> | |
<div class="col-md-6"> | |
<div class="mb-3"> | |
<div class="form-check"> | |
<input class="form-check-input" type="checkbox" id="isAvailable" checked> | |
<label class="form-check-label" for="isAvailable"> | |
Available for orders | |
</label> | |
</div> | |
<div class="form-check"> | |
<input class="form-check-input" type="checkbox" id="isSpecial"> | |
<label class="form-check-label" for="isSpecial"> | |
Today's special | |
</label> | |
</div> | |
<div class="form-check"> | |
<input class="form-check-input" type="checkbox" id="isVegetarian"> | |
<label class="form-check-label" for="isVegetarian"> | |
Vegetarian | |
</label> | |
</div> | |
</div> | |
</div> | |
</div> | |
</form> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> | |
Cancel | |
</button> | |
<button type="button" class="btn btn-success" onclick="saveDish()"> | |
<i class="fas fa-save me-2"></i>Save Dish | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} | |
{% block extra_js %} | |
<script> | |
let editingDishId = null; | |
function filterDishes() { | |
const searchTerm = document.getElementById('searchInput').value.toLowerCase(); | |
const categoryFilter = document.getElementById('categoryFilter').value; | |
const availabilityFilter = document.getElementById('availabilityFilter').value; | |
const priceFilter = document.getElementById('priceFilter').value; | |
const dishes = document.querySelectorAll('.dish-item'); | |
let visibleCount = 0; | |
dishes.forEach(dish => { | |
let show = true; | |
// Search filter | |
if (searchTerm && !dish.textContent.toLowerCase().includes(searchTerm)) { | |
show = false; | |
} | |
// Category filter | |
if (categoryFilter && dish.dataset.category !== categoryFilter) { | |
show = false; | |
} | |
// Availability filter | |
if (availabilityFilter) { | |
const isAvailable = dish.dataset.available === 'true'; | |
if ((availabilityFilter === 'available' && !isAvailable) || | |
(availabilityFilter === 'unavailable' && isAvailable)) { | |
show = false; | |
} | |
} | |
// Price filter | |
if (priceFilter) { | |
const price = parseInt(dish.dataset.price); | |
if (priceFilter === '0-100' && (price < 0 || price > 100)) show = false; | |
if (priceFilter === '100-300' && (price < 100 || price > 300)) show = false; | |
if (priceFilter === '300+' && price < 300) show = false; | |
} | |
dish.style.display = show ? 'block' : 'none'; | |
if (show) visibleCount++; | |
}); | |
document.getElementById('no-dishes').style.display = visibleCount === 0 ? 'block' : 'none'; | |
} | |
function clearFilters() { | |
document.getElementById('searchInput').value = ''; | |
document.getElementById('categoryFilter').value = ''; | |
document.getElementById('availabilityFilter').value = ''; | |
document.getElementById('priceFilter').value = ''; | |
filterDishes(); | |
} | |
function addNewDish() { | |
editingDishId = null; | |
document.getElementById('dishModalTitle').innerHTML = '<i class="fas fa-plus me-2"></i>Add New Dish'; | |
document.getElementById('dishForm').reset(); | |
document.getElementById('isAvailable').checked = true; | |
new bootstrap.Modal(document.getElementById('dishModal')).show(); | |
} | |
function editDish(dishId) { | |
editingDishId = dishId; | |
document.getElementById('dishModalTitle').innerHTML = '<i class="fas fa-edit me-2"></i>Edit Dish'; | |
// In production, this would fetch dish data from API | |
// For demo, populating with sample data | |
document.getElementById('dishName').value = 'Chicken Biryani'; | |
document.getElementById('dishCategory').value = 'main-course'; | |
document.getElementById('dishPrice').value = '250'; | |
document.getElementById('cookingTime').value = '30'; | |
document.getElementById('dishDescription').value = 'Aromatic basmati rice cooked with tender chicken and traditional spices'; | |
document.getElementById('isAvailable').checked = true; | |
new bootstrap.Modal(document.getElementById('dishModal')).show(); | |
} | |
function saveDish() { | |
const name = document.getElementById('dishName').value; | |
const category = document.getElementById('dishCategory').value; | |
const price = document.getElementById('dishPrice').value; | |
if (!name || !category || !price) { | |
alert('Please fill in all required fields'); | |
return; | |
} | |
const action = editingDishId ? 'updated' : 'added'; | |
alert(`Dish ${action} successfully! (In production, this would save to the database)`); | |
bootstrap.Modal.getInstance(document.getElementById('dishModal')).hide(); | |
// In production, refresh the dishes list | |
} | |
function toggleAvailability(dishId) { | |
if (confirm('Toggle dish availability?')) { | |
alert(`Dish availability toggled! (In production, this would update the database)`); | |
// In production, update the UI to reflect the change | |
} | |
} | |
function deleteDish(dishId) { | |
if (confirm('Are you sure you want to delete this dish? This action cannot be undone.')) { | |
alert(`Dish deleted! (In production, this would remove from database)`); | |
// In production, remove the dish element from the DOM | |
} | |
} | |
function exportDishes() { | |
alert('Export dishes feature - would generate CSV/Excel file with all dish data'); | |
} | |
// File upload preview | |
document.getElementById('dishImage').addEventListener('change', function(e) { | |
const file = e.target.files[0]; | |
if (file) { | |
console.log('Image selected:', file.name); | |
// In production, show image preview | |
} | |
}); | |
</script> | |
{% endblock %} |