household_services_app/
├── app/
│ ├── __init__.py # App factory and initialization
│ ├── models.py # Database models
│ ├── routes.py # All routes (auth, admin, customer,
professional)
│ ├── forms.py # WTForms for Login, Register, CRUD
│ ├── templates/ # Jinja2 HTML templates
│ │ ├── base.html
│ │ ├── user/
│ │ │ ├── login.html
│ │ │ ├── register.html
│ │ ├── admin/
│ │ │ ├── dashboard.html
│ │ │ ├── manage_services.html
│ │ ├── professional/
│ │ │ ├── requests.html
│ │ ├── customer/
│ │ ├── book_service.html
│ │ ├── service_requests.html
│ ├── static/ # Static assets
│ │ ├── css/
│ │ │ ├── styles.css
│ │ ├── js/
│ │ ├── scripts.js
├── migrations/ # For database migrations
├── requirements.txt # Python dependencies
├── run.py # Application entry point
└── README.md # Project description
# Cart Route
@app.route('/cart', methods=['GET'])
def cart():
user_id = session.get('user_id')
if not user_id:
return redirect(url_for('index'))
cart_items = Cart.query.filter_by(uid=user_id).all()
total_cost = sum(item.pcount * Product.query.get(item.pid).pprice for
item in cart_items)
return render_template("cart.html", cart_items=cart_items,
total_cost=total_cost)
# Add to Cart
@app.route('/cart/add/<int:product_id>', methods=['POST'])
def add_to_cart(product_id):
user_id = session.get('user_id')
if not user_id:
return jsonify({"success": False, "error": "User not logged
in"}), 403
product = Product.query.get(product_id)
if not product:
return jsonify({"success": False, "error": "Product not found"}),
404
try:
quantity = int(request.form.get(f'quantity_{product_id}', '1'))
if quantity <= 0:
raise ValueError("Quantity must be greater than zero")
except ValueError as e:
return jsonify({"success": False, "error": str(e)}), 400
if quantity > product.pcount:
return jsonify({"success": False, "error": "Insufficient stock
available"}), 400
cart_item = Cart.query.filter_by(uid=user_id, pid=product_id).first()
if cart_item:
cart_item.pcount += quantity
else:
cart_item = Cart(uid=user_id, pid=product_id, pcount=quantity)
db.session.add(cart_item)
product.pcount -= quantity
db.session.commit()
return jsonify({"success": True, "message": "Item added to cart"}),
200
# Update Cart
@app.route('/cart/update/<int:item_id>', methods=['POST'])
def update_cart(item_id):
user_id = session.get('user_id')
if not user_id:
return jsonify({"success": False, "error": "User not logged
in"}), 403
cart_item = Cart.query.filter_by(uid=user_id, pid=item_id).first()
product = Product.query.get(item_id)
if not cart_item or not product:
return jsonify({"success": False, "error": "Item not found"}),
404
try:
data = request.get_json()
new_quantity = int(data.get("quantity", 0))
if new_quantity <= 0:
raise ValueError("Quantity must be greater than zero")
except (ValueError, KeyError) as e:
return jsonify({"success": False, "error": str(e)}), 400
if new_quantity > product.pcount + cart_item.pcount:
return jsonify({"success": False, "error": "Not enough stock
available"}), 400
product.pcount += cart_item.pcount - new_quantity
cart_item.pcount = new_quantity
db.session.commit()
return jsonify({"success": True, "new_total": cart_item.pcount *
product.pprice}), 200
# Remove from Cart
@app.route('/cart/remove/<int:item_id>', methods=['POST'])
def remove_from_cart(item_id):
user_id = session.get('user_id')
if not user_id:
return jsonify({"success": False, "error": "User not logged
in"}), 403
cart_item = Cart.query.filter_by(uid=user_id, pid=item_id).first()
if not cart_item:
return jsonify({"success": False, "error": "Item not found"}),
404
product = Product.query.get(item_id)
if product:
product.pcount += cart_item.pcount
db.session.delete(cart_item)
db.session.commit()
return jsonify({"success": True, "message": "Item removed from
cart"}), 200
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-
scale=1.0">
<title>Your Cart</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
margin: 0 auto;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
th, td {
padding: 12px;
border-bottom: 1px solid #ddd;
text-align: left;
}
th {
background-color: #f2f2f2;
color: #333;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
tr:hover {
background-color: #f1f1f1;
}
.total-row {
font-weight: bold;
background-color: #f2f2f2;
}
.remove-btn, .update-btn {
background-color: #dc3545;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
}
.remove-btn:hover {
background-color: #c82333;
}
.update-btn {
background-color: #007bff;
}
.update-btn:hover {
background-color: #0056b3;
}
.quantity-input {
width: 60px;
padding: 5px;
text-align: center;
border-radius: 4px;
border: 1px solid #ddd;
}
.total-section {
text-align: right;
margin-top: 20px;
font-size: 18px;
}
.checkout-btn {
display: inline-block;
background-color: #28a745;
color: white;
padding: 10px 20px;
border-radius: 4px;
text-decoration: none;
font-size: 16px;
margin-top: 10px;
}
.checkout-btn:hover {
background-color: #218838;
}
.error-message {
color: #dc3545;
font-size: 14px;
margin-top: 5px;
}
/* Mobile responsiveness */
@media (max-width: 600px) {
table {
font-size: 14px;
}
th, td {
padding: 10px;
}
.quantity-input {
width: 50px;
}
.total-section {
font-size: 16px;
}
.checkout-btn {
width: 100%;
font-size: 18px;
padding: 15px 0;
}
}
</style>
</head>
<body>
<h1>Your Cart</h1>
{% if cart_items %}
<table>
<thead>
<tr>
<th>Product Name</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for item in cart_items %}
<tr id="row-{{ item.pid }}">
<td>{{ item.pname }}</td>
<td>${{ item.pprice }}</td>
<td>
<input type="number" id="quantity-{{ item.pid }}"
class="quantity-input" value="{{ item.pcount }}" min="1">
<p id="error-{{ item.pid }}" class="error-
message" style="display:none;"></p>
</td>
<td id="total-{{ item.pid }}" class="item-total"
data-price="{{ item.pprice }}" data-product-id="{{ item.pid }}">
${{ item.pprice * item.pcount }}
</td>
<td>
<button class="update-btn"
onclick="updateTotal('{{ item.pid }}')">Update</button>
<form method="POST"
action="{{ url_for('remove_from_cart', item_id=item.pid) }}"
style="display:inline;">
<button type="submit" class="remove-
btn">Remove</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="total-section">
<p>Total: $<span id="cart-total">{{ total_cost }}</span></p>
<a href="{{ url_for('checkout') }}" class="checkout-btn">Proceed
to Checkout</a>
</div>
{% else %}
<p>Your cart is empty. <a href="{{ url_for('index') }}">Continue
shopping</a>.</p>
{% endif %}
<script>
// Function to update the total when quantity changes
function updateTotal(productId) {
const quantityInput = document.getElementById(`quantity-$
{productId}`);
const newQuantity = parseInt(quantityInput.value, 10);
const itemTotal = document.getElementById(`total-$
{productId}`);
const errorField = document.getElementById(`error-$
{productId}`);
const price = parseFloat(itemTotal.dataset.price);
// Clear previous errors
errorField.style.display = 'none';
// Send updated quantity to backend via AJAX
fetch(`/cart/update/${productId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ quantity: newQuantity }),
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Update the total for the item
itemTotal.textContent = `$${(price *
newQuantity).toFixed(2)}`;
updateCartTotal();
} else {
// Display error message
errorField.textContent = data.error;
errorField.style.display = 'block';
}
})
.catch(err => {
console.error("Error updating cart:", err);
});
}
// Function to update the total for the entire cart
function updateCartTotal() {
let total = 0;
const itemTotals = document.querySelectorAll('.item-total');
itemTotals.forEach(itemTotal => {
total += parseFloat(itemTotal.textContent.replace('$',
''));
});
document.getElementById('cart-total').textContent =
total.toFixed(2);
}
</script>
</body>
</html>