From 4fec87dd18debe38d331bd5eaef1375793898eda Mon Sep 17 00:00:00 2001
From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk>
Date: Mon, 21 Apr 2025 22:06:23 +0700
Subject: [PATCH] modify the backends so that the cart feature can work
 correctly, connect the order and the payments. Update the view order of the
 user so that they can checkout and can view what they have checkouted. Add
 format ruff.

---
 app/backend/dummy_data.py                     |  16 +
 app/backend/main.py                           |   4 +-
 app/backend/models/models.py                  |  23 +
 app/backend/routes/cart.py                    | 342 ++++++++++
 app/backend/routes/order.py                   | 263 +++++++-
 app/backend/schemas/order.py                  |  27 +
 .../components/product/view_product.py        |   9 +-
 app/frontend/components/user_orders.py        | 621 +++++++++++++++++-
 app/frontend/main.py                          |  71 +-
 9 files changed, 1345 insertions(+), 31 deletions(-)
 create mode 100644 app/backend/routes/cart.py

diff --git a/app/backend/dummy_data.py b/app/backend/dummy_data.py
index a2a9989..a6035ee 100644
--- a/app/backend/dummy_data.py
+++ b/app/backend/dummy_data.py
@@ -8,6 +8,8 @@ from backend.models.models import (
     Order,
     OrderItem,
     Payment,
+    Cart,
+    CartItem,
 )
 from backend.utils.hashing import hash_password
 
@@ -196,3 +198,17 @@ def insert_dummy_data(session: Session):
         ]
         session.add_all(order_items)
         session.commit()
+        
+    if not session.query(Cart).first():
+        # Create example cart for the first user
+        cart = Cart(user_id=1)
+        session.add(cart)
+        session.commit()
+        
+        # Add a couple of items to the cart
+        cart_items = [
+            CartItem(cart_id=cart.id, product_id=3, quantity=2, price=102.99),
+            CartItem(cart_id=cart.id, product_id=4, quantity=1, price=103.99)
+        ]
+        session.add_all(cart_items)
+        session.commit()
diff --git a/app/backend/main.py b/app/backend/main.py
index 0bbd876..700b51b 100644
--- a/app/backend/main.py
+++ b/app/backend/main.py
@@ -5,7 +5,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
 from fastapi import FastAPI
 from fastapi.staticfiles import StaticFiles
-from backend.routes import auth, shop, product, category, search, order, payment
+from backend.routes import auth, shop, product, category, search, order, payment, cart
 from backend.database import init_db
 from core.config import settings
 
@@ -30,6 +30,8 @@ app.include_router(category.router, prefix="/category", tags=["category"])
 app.include_router(order.router, prefix="/order", tags=["order"])
 # Also include order router at /orders for backward compatibility
 app.include_router(order.router, prefix="/orders", tags=["orders"])
+# Use dedicated cart router
+app.include_router(cart.router, prefix="/cart", tags=["cart"])
 
 
 @app.get("/")
diff --git a/app/backend/models/models.py b/app/backend/models/models.py
index 366abc3..4c1f1d9 100644
--- a/app/backend/models/models.py
+++ b/app/backend/models/models.py
@@ -15,6 +15,7 @@ class User(SQLModel, table=True):
 
     shops: List["Shop"] = Relationship(back_populates="owner")
     orders: List["Order"] = Relationship(back_populates="user")
+    carts: List["Cart"] = Relationship(back_populates="user")
 
 
 class Shop(SQLModel, table=True):
@@ -102,3 +103,25 @@ class Payment(SQLModel, table=True):
     created_at: datetime = Field(default_factory=datetime.utcnow)
 
     user: User = Relationship(back_populates="payments")
+
+
+class Cart(SQLModel, table=True):
+    id: Optional[int] = Field(default=None, primary_key=True)
+    user_id: int = Field(foreign_key="user.id")
+    created_at: datetime = Field(default_factory=datetime.utcnow)
+    
+    # Relationships
+    user: Optional["User"] = Relationship(back_populates="carts")
+    cart_items: List["CartItem"] = Relationship(back_populates="cart")
+
+
+class CartItem(SQLModel, table=True):
+    id: Optional[int] = Field(default=None, primary_key=True)
+    cart_id: int = Field(foreign_key="cart.id")
+    product_id: int = Field(foreign_key="product.id")
+    quantity: int
+    price: float
+    
+    # Relationships
+    cart: Optional["Cart"] = Relationship(back_populates="cart_items")
+    product: Optional["Product"] = Relationship()
diff --git a/app/backend/routes/cart.py b/app/backend/routes/cart.py
new file mode 100644
index 0000000..b481a2d
--- /dev/null
+++ b/app/backend/routes/cart.py
@@ -0,0 +1,342 @@
+from fastapi import APIRouter, Depends, HTTPException
+from sqlmodel import Session, select, delete
+from geopy.geocoders import Nominatim
+from geopy.distance import geodesic
+from backend.database import get_session
+from backend.routes.auth import get_current_user
+from backend.models.models import Order, OrderItem, User, Product, Shop, Payment, Cart, CartItem
+from backend.schemas.order import OrderCreate, OrderRead, OrderUpdate, CartItemCreate, CartItemRead, CartRead
+
+router = APIRouter()
+
+
+@router.post("/add")
+def add_to_cart(
+    cart_item: CartItemCreate,
+    session: Session = Depends(get_session),
+    current_user: User = Depends(get_current_user),
+):
+    """Add a product to the user's cart"""
+    # Validate product exists and has enough stock
+    product = session.get(Product, cart_item.product_id)
+    if not product:
+        raise HTTPException(status_code=404, detail="Product not found")
+    
+    if product.stock < cart_item.quantity:
+        raise HTTPException(status_code=400, detail="Not enough stock available")
+    
+    # Verify the shop exists
+    shop = session.get(Shop, cart_item.shop_id)
+    if not shop:
+        raise HTTPException(status_code=404, detail="Shop not found")
+    
+    # Get or create a cart for the user
+    cart = session.exec(
+        select(Cart).where(Cart.user_id == current_user.id)
+    ).first()
+    
+    if not cart:
+        cart = Cart(user_id=current_user.id)
+        session.add(cart)
+        session.commit()
+        session.refresh(cart)
+    
+    # Check if the item already exists in the cart
+    existing_item = session.exec(
+        select(CartItem).where(
+            CartItem.cart_id == cart.id,
+            CartItem.product_id == cart_item.product_id
+        )
+    ).first()
+    
+    if existing_item:
+        # Update quantity if item already exists
+        existing_item.quantity += cart_item.quantity
+        session.add(existing_item)
+    else:
+        # Create new cart item
+        new_cart_item = CartItem(
+            cart_id=cart.id,
+            product_id=cart_item.product_id,
+            quantity=cart_item.quantity,
+            price=product.price
+        )
+        session.add(new_cart_item)
+    
+    session.commit()
+    
+    return {"message": "Item added to cart successfully"}
+
+
+@router.get("/items")
+def get_cart_items(
+    session: Session = Depends(get_session),
+    current_user: User = Depends(get_current_user),
+):
+    """Get all items in the user's cart with product details"""
+    # Get user's cart
+    cart = session.exec(
+        select(Cart).where(Cart.user_id == current_user.id)
+    ).first()
+    
+    if not cart:
+        return {"items": []}
+    
+    # Get cart items with product details
+    cart_items = session.exec(
+        select(CartItem).where(CartItem.cart_id == cart.id)
+    ).all()
+    
+    items_with_details = []
+    for item in cart_items:
+        product = session.get(Product, item.product_id)
+        if product:
+            product_dict = {
+                "id": item.id,  # Use cart item ID for operations
+                "product_id": product.id,
+                "name": product.name,
+                "price": product.price,
+                "shop_id": product.shop_id,
+                "shop_name": product.shop.name if product.shop else "Unknown Shop",
+                "images": product.images,
+                "quantity": item.quantity,
+                "subtotal": item.quantity * product.price
+            }
+            items_with_details.append(product_dict)
+    
+    total_price = sum(item.get("subtotal", 0) for item in items_with_details)
+    
+    return {
+        "items": items_with_details,
+        "total_price": total_price,
+        "item_count": len(items_with_details)
+    }
+
+
+@router.post("/checkout")
+def checkout_cart(
+    checkout_data: dict,
+    session: Session = Depends(get_session),
+    current_user: User = Depends(get_current_user),
+):
+    """Process checkout for selected cart items"""
+    # Extract parameters from request body
+    delivery_address = checkout_data.get("delivery_address")
+    payment_id = checkout_data.get("payment_id")
+    selected_items = checkout_data.get("selected_items", [])
+    
+    if not delivery_address or not payment_id:
+        raise HTTPException(
+            status_code=400, 
+            detail="Missing required parameters: delivery_address and payment_id"
+        )
+    
+    if not selected_items:
+        raise HTTPException(
+            status_code=400,
+            detail="No items selected for checkout"
+        )
+    
+    # Validate payment method
+    payment = session.get(Payment, payment_id)
+    if not payment or payment.user_id != current_user.id:
+        raise HTTPException(
+            status_code=400, 
+            detail="Invalid or unauthorized payment method"
+        )
+    
+    # Get user's cart
+    cart = session.exec(
+        select(Cart).where(Cart.user_id == current_user.id)
+    ).first()
+    
+    if not cart:
+        raise HTTPException(status_code=404, detail="Cart not found")
+    
+    # Get selected cart items
+    cart_items = []
+    for item_id in selected_items:
+        item = session.exec(
+            select(CartItem).where(
+                CartItem.id == item_id,
+                CartItem.cart_id == cart.id
+            )
+        ).first()
+        
+        if item:
+            cart_items.append(item)
+    
+    if not cart_items:
+        raise HTTPException(status_code=400, detail="No valid items selected")
+    
+    # Group cart items by shop
+    shop_items = {}
+    for item in cart_items:
+        product = session.get(Product, item.product_id)
+        if not product or product.stock < item.quantity:
+            raise HTTPException(
+                status_code=400, detail=f"Product {item.product_id} is out of stock"
+            )
+        
+        if product.shop_id not in shop_items:
+            shop_items[product.shop_id] = []
+        
+        shop_items[product.shop_id].append(item)
+    
+    # Create orders for each shop
+    orders = []
+    for shop_id, items in shop_items.items():
+        shop = session.get(Shop, shop_id)
+        
+        # Geocode the delivery address
+        geolocator = Nominatim(user_agent="order_locator")
+        try:
+            delivery_location = geolocator.geocode(delivery_address)
+            if not delivery_location:
+                # Fall back to London coordinates if geocoding fails
+                delivery_location = type('obj', (object,), {
+                    'latitude': 51.5074,
+                    'longitude': -0.1278
+                })
+        except Exception as e:
+            print(f"Geocoding error: {e}")
+            # Fall back to London coordinates
+            delivery_location = type('obj', (object,), {
+                'latitude': 51.5074,
+                'longitude': -0.1278
+            })
+        
+        # Calculate the distance between the shop and the delivery location
+        shop_location = (shop.latitude, shop.longitude)
+        delivery_coordinates = (delivery_location.latitude, delivery_location.longitude)
+        distance_km = geodesic(shop_location, delivery_coordinates).kilometers
+        
+        # Calculate the shipping price ($1 per km)
+        shipping_price = distance_km * 1.0
+        
+        # Calculate the total price
+        product_total = sum(item.price * item.quantity for item in items)
+        total_price = shipping_price + product_total
+        
+        # Create the order
+        new_order = Order(
+            user_id=current_user.id,
+            shop_id=shop_id,
+            payment_id=payment_id,
+            total_price=total_price,
+            shipping_price=shipping_price,
+            status="pending",
+            delivery_address=delivery_address,
+            delivery_latitude=delivery_location.latitude,
+            delivery_longitude=delivery_location.longitude,
+        )
+        session.add(new_order)
+        session.commit()
+        session.refresh(new_order)
+        
+        # Create order items and update product stock
+        for item in items:
+            order_item = OrderItem(
+                order_id=new_order.id,
+                product_id=item.product_id,
+                quantity=item.quantity,
+                price=item.price,
+            )
+            session.add(order_item)
+            
+            # Update product stock
+            product = session.get(Product, item.product_id)
+            product.stock -= item.quantity
+            session.add(product)
+            
+            # Remove checked out item from cart
+            session.delete(item)
+        
+        orders.append(new_order.id)
+    
+    session.commit()
+    
+    return {"message": "Orders created successfully", "order_ids": orders}
+
+
+@router.delete("/remove/{item_id}")
+def remove_from_cart(
+    item_id: int,
+    session: Session = Depends(get_session),
+    current_user: User = Depends(get_current_user),
+):
+    """Remove an item from the cart"""
+    # Get user's cart
+    cart = session.exec(
+        select(Cart).where(Cart.user_id == current_user.id)
+    ).first()
+    
+    if not cart:
+        raise HTTPException(status_code=404, detail="Cart not found")
+    
+    # Find the cart item
+    cart_item = session.exec(
+        select(CartItem).where(
+            CartItem.id == item_id,
+            CartItem.cart_id == cart.id
+        )
+    ).first()
+    
+    if not cart_item:
+        raise HTTPException(status_code=404, detail="Item not found in cart")
+    
+    # Remove the item
+    session.delete(cart_item)
+    session.commit()
+    
+    return {"message": "Item removed from cart successfully"}
+
+
+@router.put("/update/{item_id}")
+def update_cart_item(
+    item_id: int,
+    update_data: dict,
+    session: Session = Depends(get_session),
+    current_user: User = Depends(get_current_user),
+):
+    """Update the quantity of a cart item"""
+    # Get user's cart
+    cart = session.exec(
+        select(Cart).where(Cart.user_id == current_user.id)
+    ).first()
+    
+    if not cart:
+        raise HTTPException(status_code=404, detail="Cart not found")
+    
+    # Find the cart item
+    cart_item = session.exec(
+        select(CartItem).where(
+            CartItem.id == item_id,
+            CartItem.cart_id == cart.id
+        )
+    ).first()
+    
+    if not cart_item:
+        raise HTTPException(status_code=404, detail="Item not found in cart")
+    
+    # Update quantity
+    new_quantity = update_data.get("quantity")
+    if new_quantity is not None:
+        if new_quantity <= 0:
+            # If quantity is 0 or negative, remove the item
+            session.delete(cart_item)
+        else:
+            # Check if product has enough stock
+            product = session.get(Product, cart_item.product_id)
+            if not product:
+                raise HTTPException(status_code=404, detail="Product not found")
+            
+            if product.stock < new_quantity:
+                raise HTTPException(status_code=400, detail="Not enough stock available")
+            
+            cart_item.quantity = new_quantity
+            session.add(cart_item)
+    
+    session.commit()
+    
+    return {"message": "Cart item updated successfully"} 
\ No newline at end of file
diff --git a/app/backend/routes/order.py b/app/backend/routes/order.py
index ee9c075..46f456f 100644
--- a/app/backend/routes/order.py
+++ b/app/backend/routes/order.py
@@ -4,12 +4,271 @@ from geopy.geocoders import Nominatim
 from geopy.distance import geodesic
 from backend.database import get_session
 from backend.routes.auth import get_current_user
-from backend.models.models import Order, OrderItem, User, Product, Shop, Payment
-from backend.schemas.order import OrderCreate, OrderRead, OrderUpdate
+from backend.models.models import Order, OrderItem, User, Product, Shop, Payment, Cart, CartItem
+from backend.schemas.order import OrderCreate, OrderRead, OrderUpdate, CartItemCreate
 
 router = APIRouter()
 
 
+@router.post("/cart/add")
+def add_to_cart(
+    cart_item: CartItemCreate,
+    session: Session = Depends(get_session),
+    current_user: User = Depends(get_current_user),
+):
+    # Validate product exists and has enough stock
+    product = session.get(Product, cart_item.product_id)
+    if not product:
+        raise HTTPException(status_code=404, detail="Product not found")
+    
+    if product.stock < cart_item.quantity:
+        raise HTTPException(status_code=400, detail="Not enough stock available")
+    
+    # Verify the shop exists
+    shop = session.get(Shop, cart_item.shop_id)
+    if not shop:
+        raise HTTPException(status_code=404, detail="Shop not found")
+    
+    # Get or create a cart for the user
+    cart = session.exec(
+        select(Cart).where(Cart.user_id == current_user.id)
+    ).first()
+    
+    if not cart:
+        cart = Cart(user_id=current_user.id)
+        session.add(cart)
+        session.commit()
+        session.refresh(cart)
+    
+    # Check if the item already exists in the cart
+    existing_item = session.exec(
+        select(CartItem).where(
+            CartItem.cart_id == cart.id,
+            CartItem.product_id == cart_item.product_id
+        )
+    ).first()
+    
+    if existing_item:
+        # Update quantity if item already exists
+        existing_item.quantity += cart_item.quantity
+        session.add(existing_item)
+    else:
+        # Create new cart item
+        new_cart_item = CartItem(
+            cart_id=cart.id,
+            product_id=cart_item.product_id,
+            quantity=cart_item.quantity,
+            price=product.price
+        )
+        session.add(new_cart_item)
+    
+    session.commit()
+    
+    return {"message": "Item added to cart successfully"}
+
+
+@router.get("/cart/view-items")
+def get_cart_items(
+    session: Session = Depends(get_session),
+    current_user: User = Depends(get_current_user),
+):
+    # Get user's cart
+    cart = session.exec(
+        select(Cart).where(Cart.user_id == current_user.id)
+    ).first()
+    
+    if not cart:
+        return {"items": []}
+    
+    # Get cart items with product details
+    cart_items = session.exec(
+        select(CartItem).where(CartItem.cart_id == cart.id)
+    ).all()
+    
+    items_with_details = []
+    for item in cart_items:
+        product = session.get(Product, item.product_id)
+        if product:
+            product_dict = {
+                "id": product.id,
+                "name": product.name,
+                "price": product.price,
+                "shop_id": product.shop_id,
+                "images": product.images,
+                "quantity": item.quantity
+            }
+            items_with_details.append(product_dict)
+    
+    return {"items": items_with_details}
+
+
+@router.post("/cart/checkout")
+def checkout_cart(
+    checkout_data: dict,
+    session: Session = Depends(get_session),
+    current_user: User = Depends(get_current_user),
+):
+    # Extract parameters from request body
+    delivery_address = checkout_data.get("delivery_address")
+    payment_id = checkout_data.get("payment_id")
+    
+    if not delivery_address or not payment_id:
+        raise HTTPException(status_code=400, detail="Missing required parameters: delivery_address and payment_id")
+    
+    # Get user's cart
+    cart = session.exec(
+        select(Cart).where(Cart.user_id == current_user.id)
+    ).first()
+    
+    if not cart:
+        raise HTTPException(status_code=404, detail="Cart not found")
+    
+    # Get cart items
+    cart_items = session.exec(
+        select(CartItem).where(CartItem.cart_id == cart.id)
+    ).all()
+    
+    if not cart_items:
+        raise HTTPException(status_code=400, detail="Cart is empty")
+    
+    # Validate payment method
+    payment = session.get(Payment, payment_id)
+    if not payment or payment.user_id != current_user.id:
+        raise HTTPException(
+            status_code=400, detail="Invalid or unauthorized payment method"
+        )
+    
+    # Group cart items by shop
+    shop_items = {}
+    for item in cart_items:
+        product = session.get(Product, item.product_id)
+        if not product or product.stock < item.quantity:
+            raise HTTPException(
+                status_code=400, detail=f"Product {item.product_id} is out of stock"
+            )
+        
+        if product.shop_id not in shop_items:
+            shop_items[product.shop_id] = []
+        
+        shop_items[product.shop_id].append(item)
+    
+    # Create orders for each shop
+    orders = []
+    for shop_id, items in shop_items.items():
+        shop = session.get(Shop, shop_id)
+        
+        # Geocode the delivery address
+        geolocator = Nominatim(user_agent="order_locator")
+        try:
+            delivery_location = geolocator.geocode(delivery_address)
+            if not delivery_location:
+                # Fall back to London coordinates if geocoding fails
+                delivery_location = type('obj', (object,), {
+                    'latitude': 51.5074,
+                    'longitude': -0.1278
+                })
+        except Exception as e:
+            print(f"Geocoding error: {e}")
+            # Fall back to London coordinates
+            delivery_location = type('obj', (object,), {
+                'latitude': 51.5074,
+                'longitude': -0.1278
+            })
+        
+        # Calculate the distance between the shop and the delivery location
+        shop_location = (shop.latitude, shop.longitude)
+        delivery_coordinates = (delivery_location.latitude, delivery_location.longitude)
+        distance_km = geodesic(shop_location, delivery_coordinates).kilometers
+        
+        # Calculate the shipping price ($1 per km)
+        shipping_price = distance_km * 1.0
+        
+        # Calculate the total price
+        product_total = sum(item.price * item.quantity for item in items)
+        total_price = shipping_price + product_total
+        
+        # Create the order
+        new_order = Order(
+            user_id=current_user.id,
+            shop_id=shop_id,
+            payment_id=payment_id,
+            total_price=total_price,
+            shipping_price=shipping_price,
+            status="pending",
+            delivery_address=delivery_address,
+            delivery_latitude=delivery_location.latitude,
+            delivery_longitude=delivery_location.longitude,
+        )
+        session.add(new_order)
+        session.commit()
+        session.refresh(new_order)
+        
+        # Create order items and update product stock
+        for item in items:
+            order_item = OrderItem(
+                order_id=new_order.id,
+                product_id=item.product_id,
+                quantity=item.quantity,
+                price=item.price,
+            )
+            session.add(order_item)
+            
+            # Update product stock
+            product = session.get(Product, item.product_id)
+            product.stock -= item.quantity
+            session.add(product)
+        
+        orders.append(new_order.id)
+    
+    # Clear the cart after checkout
+    session.exec(delete(CartItem).where(CartItem.cart_id == cart.id))
+    session.commit()
+    
+    return {"message": "Orders created successfully", "order_ids": orders}
+
+
+@router.delete("/cart/remove/{product_id}")
+def remove_from_cart(
+    product_id: int,
+    session: Session = Depends(get_session),
+    current_user: User = Depends(get_current_user),
+):
+    # Get user's cart
+    cart = session.exec(
+        select(Cart).where(Cart.user_id == current_user.id)
+    ).first()
+    
+    if not cart:
+        raise HTTPException(status_code=404, detail="Cart not found")
+    
+    # Find the cart item
+    cart_item = session.exec(
+        select(CartItem).where(
+            CartItem.cart_id == cart.id,
+            CartItem.product_id == product_id
+        )
+    ).first()
+    
+    if not cart_item:
+        raise HTTPException(status_code=404, detail="Item not found in cart")
+    
+    # Remove the item
+    session.delete(cart_item)
+    session.commit()
+    
+    return {"message": "Item removed from cart successfully"}
+
+
+@router.post("/add")
+def legacy_add_to_cart(
+    cart_item: CartItemCreate,
+    session: Session = Depends(get_session),
+    current_user: User = Depends(get_current_user),
+):
+    # For backward compatibility, redirect to new cart endpoint
+    return add_to_cart(cart_item, session, current_user)
+
+
 @router.post("/", response_model=OrderRead)
 def create_order(
     order_data: OrderCreate,
diff --git a/app/backend/schemas/order.py b/app/backend/schemas/order.py
index 8cb9ede..416f332 100644
--- a/app/backend/schemas/order.py
+++ b/app/backend/schemas/order.py
@@ -8,6 +8,33 @@ class OrderItemCreate(BaseModel):
     quantity: int
 
 
+class CartItemCreate(BaseModel):
+    shop_id: int
+    product_id: int
+    quantity: int
+    price: Optional[float] = None
+
+
+class CartItemRead(BaseModel):
+    id: int
+    product_id: int
+    quantity: int
+    price: float
+
+    class Config:
+        from_attributes = True
+
+
+class CartRead(BaseModel):
+    id: int
+    user_id: int
+    created_at: datetime
+    cart_items: List[CartItemRead]
+
+    class Config:
+        from_attributes = True
+
+
 class OrderCreate(BaseModel):
     shop_id: int
     payment_id: int
diff --git a/app/frontend/components/product/view_product.py b/app/frontend/components/product/view_product.py
index d6d3dab..f7af0b4 100644
--- a/app/frontend/components/product/view_product.py
+++ b/app/frontend/components/product/view_product.py
@@ -481,8 +481,13 @@ def view_product_frame(
 
             if response.status_code == 200:
                 messagebox.showinfo("Success", "Product added to cart successfully!")
-                # Optionally switch to cart view
-                switch_func("user_orders")
+                # Ask if user wants to view cart
+                view_cart = messagebox.askyesno(
+                    "View Cart", 
+                    "Would you like to view your cart?"
+                )
+                if view_cart:
+                    switch_func("user_orders")
             else:
                 error_msg = response.json().get("detail", "Unknown error occurred")
                 messagebox.showerror("Error", f"Failed to add to cart: {error_msg}")
diff --git a/app/frontend/components/user_orders.py b/app/frontend/components/user_orders.py
index c8fadbe..6ce2816 100644
--- a/app/frontend/components/user_orders.py
+++ b/app/frontend/components/user_orders.py
@@ -116,8 +116,572 @@ def user_orders_frame(parent, switch_func, API_URL, token):
     content_frame = ctk.CTkFrame(main_section, fg_color=CARD_BG, corner_radius=15)
     content_frame.pack(side="left", fill="both", expand=True, padx=(0, 20), pady=20)
 
-    # ORDER HISTORY
-    orders_frame = ctk.CTkScrollableFrame(content_frame, fg_color="transparent")
+    # Tab control for Cart and Order History
+    tab_view = ctk.CTkTabview(content_frame, fg_color="transparent")
+    tab_view.pack(fill="both", expand=True, padx=10, pady=10)
+    
+    # Create tabs
+    cart_tab = tab_view.add("My Cart")
+    orders_tab = tab_view.add("Order History")
+    
+    # CART TAB
+    cart_frame = ctk.CTkScrollableFrame(cart_tab, fg_color="transparent")
+    cart_frame.pack(fill="both", expand=True, padx=20, pady=20)
+    
+    # Cart title with refresh button
+    title_frame = ctk.CTkFrame(cart_frame, fg_color="transparent")
+    title_frame.pack(fill="x", pady=(0, 20))
+    
+    cart_title = ctk.CTkLabel(
+        title_frame,
+        text="Your Shopping Cart",
+        font=("Helvetica", 18, "bold"),
+        text_color=SHOPPING,
+    )
+    cart_title.pack(side="left", anchor="w")
+    
+    def refresh_cart():
+        """Function to reload cart items"""
+        load_cart_items()
+    
+    refresh_cart_btn = ctk.CTkButton(
+        title_frame,
+        text="🔄 Refresh",
+        font=("Helvetica", 12),
+        fg_color="#4A4A4A",
+        hover_color="#5A5A5A",
+        width=100,
+        height=28,
+        command=refresh_cart
+    )
+    refresh_cart_btn.pack(side="right", padx=5)
+    
+    cart_items_frame = ctk.CTkFrame(cart_frame, fg_color="transparent")
+    cart_items_frame.pack(fill="both", expand=True)
+    
+    # Checkout frame (initially empty)
+    checkout_frame = ctk.CTkFrame(cart_frame, fg_color="transparent")
+    checkout_frame.pack(fill="x", pady=10)
+    
+    def load_cart_items():
+        # Clear existing items
+        for widget in cart_items_frame.winfo_children():
+            widget.destroy()
+        
+        # Clear checkout frame
+        for widget in checkout_frame.winfo_children():
+            widget.destroy()
+        
+        try:
+            headers = {"Authorization": f"Bearer {frame.token}"}
+            response = requests.get(f"{API_URL}/cart/items", headers=headers)
+            
+            if response.status_code == 200:
+                cart_data = response.json()
+                cart_items = cart_data.get("items", [])
+                total_price = cart_data.get("total_price", 0)
+                
+                if not cart_items:
+                    empty_label = ctk.CTkLabel(
+                        cart_items_frame,
+                        text="Your cart is empty",
+                        font=("Helvetica", 14),
+                    )
+                    empty_label.pack(pady=20)
+                    return
+                
+                # Header row
+                header_frame = ctk.CTkFrame(cart_items_frame, fg_color="#333333")
+                header_frame.pack(fill="x", pady=(0, 10))
+                
+                # Configure the same grid layout as the items
+                header_frame.grid_columnconfigure(0, weight=0, minsize=40)    # Checkbox column
+                header_frame.grid_columnconfigure(1, weight=1, minsize=300)   # Product column (expandable)
+                header_frame.grid_columnconfigure(2, weight=0, minsize=100)   # Unit price column
+                header_frame.grid_columnconfigure(3, weight=0, minsize=100)   # Quantity column
+                header_frame.grid_columnconfigure(4, weight=0, minsize=100)   # Subtotal column
+                header_frame.grid_columnconfigure(5, weight=0, minsize=90)    # Actions column
+                
+                # Checkbox header (select all)
+                select_all_var = ctk.BooleanVar(value=False)
+                selected_items = {}  # Dictionary to track selected items {item_id: bool}
+                
+                def toggle_select_all():
+                    for item_id, checkbox_var in selected_items.items():
+                        checkbox_var.set(select_all_var.get())
+                
+                select_all_cb = ctk.CTkCheckBox(
+                    header_frame, 
+                    text="", 
+                    variable=select_all_var,
+                    command=toggle_select_all,
+                    width=30
+                )
+                select_all_cb.grid(row=0, column=0, padx=5, pady=10, sticky="w")
+                
+                ctk.CTkLabel(
+                    header_frame,
+                    text="Product",
+                    font=("Helvetica", 12, "bold"),
+                ).grid(row=0, column=1, padx=5, pady=10, sticky="w")
+                
+                ctk.CTkLabel(
+                    header_frame,
+                    text="Unit Price",
+                    font=("Helvetica", 12, "bold"),
+                ).grid(row=0, column=2, padx=5, pady=10)
+                
+                ctk.CTkLabel(
+                    header_frame,
+                    text="Quantity",
+                    font=("Helvetica", 12, "bold"),
+                ).grid(row=0, column=3, padx=5, pady=10)
+                
+                ctk.CTkLabel(
+                    header_frame,
+                    text="Subtotal",
+                    font=("Helvetica", 12, "bold"),
+                ).grid(row=0, column=4, padx=5, pady=10)
+                
+                ctk.CTkLabel(
+                    header_frame,
+                    text="Actions",
+                    font=("Helvetica", 12, "bold"),
+                ).grid(row=0, column=5, padx=5, pady=10)
+                
+                # Create items list
+                for item in cart_items:
+                    item_id = item.get("id")
+                    # Create checkbox variable for this item
+                    selected_items[item_id] = ctk.BooleanVar(value=False)
+                    
+                    item_frame = create_cart_item_frame(item, selected_items[item_id])
+                    item_frame.pack(fill="x", pady=5)
+                
+                # Add checkout section
+                divider = ctk.CTkFrame(checkout_frame, fg_color="#444444", height=1)
+                divider.pack(fill="x", pady=10)
+                
+                # Selected items summary
+                def update_checkout_summary():
+                    # Calculate selected items total
+                    selected_total = 0
+                    selected_count = 0
+                    
+                    for item in cart_items:
+                        item_id = item.get("id")
+                        if item_id in selected_items and selected_items[item_id].get():
+                            selected_total += item.get("subtotal", 0)
+                            selected_count += 1
+                    
+                    # Update summary label
+                    summary_label.configure(
+                        text=f"Selected Items: {selected_count} | Total: â‚«{selected_total:,.0f}"
+                    )
+                    
+                    # Update checkout button state
+                    if selected_count > 0:
+                        checkout_button.configure(state="normal")
+                    else:
+                        checkout_button.configure(state="disabled")
+                
+                # Create a frame for the summary
+                bottom_frame = ctk.CTkFrame(checkout_frame, fg_color="transparent")
+                bottom_frame.pack(fill="x", pady=15)
+                
+                # Create the summary label
+                summary_label = ctk.CTkLabel(
+                    bottom_frame,
+                    text="Selected Items: 0 | Total: â‚«0",
+                    font=("Helvetica", 14, "bold"),
+                    text_color=SHOPPING,
+                )
+                summary_label.pack(side="left", padx=10)
+                
+                # Function to update selection count (bound to each checkbox)
+                def on_checkbox_change():
+                    update_checkout_summary()
+                    
+                    # Check if all are selected
+                    all_selected = all(var.get() for var in selected_items.values())
+                    select_all_var.set(all_selected)
+                
+                # Bind the change function to all checkboxes
+                for var in selected_items.values():
+                    var.trace_add("write", lambda *args: on_checkbox_change())
+                
+                # Delivery address
+                address_frame = ctk.CTkFrame(checkout_frame, fg_color="transparent")
+                address_frame.pack(fill="x", pady=10)
+                
+                address_label = ctk.CTkLabel(
+                    address_frame,
+                    text="Delivery Address:",
+                    font=("Helvetica", 14),
+                    text_color="#AAAAAA", 
+                )
+                address_label.pack(side="left", padx=10)
+                
+                address_entry = ctk.CTkEntry(address_frame, width=300)
+                address_entry.pack(side="left", padx=10)
+                address_entry.insert(0, "10 Downing Street, London, UK")  # Default address
+                
+                # Payment selection
+                payment_frame = ctk.CTkFrame(checkout_frame, fg_color="transparent") 
+                payment_frame.pack(fill="x", pady=10)
+                
+                payment_label = ctk.CTkLabel(
+                    payment_frame,
+                    text="Payment Method:",
+                    font=("Helvetica", 14),
+                    text_color="#AAAAAA",
+                )
+                payment_label.pack(side="left", padx=10)
+                
+                # Function to load payment methods
+                def load_payment_methods():
+                    payment_options.clear()
+                    payment_ids.clear()
+                    
+                    # Get payment methods
+                    try:
+                        payments_response = requests.get(f"{API_URL}/payment/user", headers=headers)
+                        
+                        if payments_response.status_code == 200:
+                            payments = payments_response.json()
+                            for payment in payments:
+                                payment_text = f"{payment.get('payment_method')} ending in {payment.get('card_number')[-4:]}"
+                                payment_options.append(payment_text)
+                                payment_ids[payment_text] = payment.get('id')
+                        
+                        if not payment_options:
+                            payment_options.append("No payment methods available")
+                        
+                        # Update dropdown
+                        payment_dropdown.configure(values=payment_options)
+                        payment_var.set(payment_options[0] if payment_options else "")
+                    except Exception as e:
+                        print(f"Error loading payment methods: {e}")
+                
+                # Get payment methods
+                payment_options = []
+                payment_ids = {}
+                
+                # Create a frame to hold the dropdown and refresh button
+                payment_dropdown_frame = ctk.CTkFrame(payment_frame, fg_color="transparent")
+                payment_dropdown_frame.pack(side="left", fill="x", expand=True)
+                
+                payment_var = ctk.StringVar(value="")
+                payment_dropdown = ctk.CTkOptionMenu(
+                    payment_dropdown_frame,
+                    values=["Loading..."],
+                    variable=payment_var,
+                    width=250,
+                )
+                payment_dropdown.pack(side="left", padx=10)
+                
+                # Refresh button
+                refresh_btn = ctk.CTkButton(
+                    payment_dropdown_frame,
+                    text="🔄",
+                    width=40,
+                    height=28,
+                    command=load_payment_methods,
+                    fg_color="#4A4A4A",
+                    hover_color="#5A5A5A",
+                )
+                refresh_btn.pack(side="left", padx=5)
+                
+                # Load payment methods initially
+                load_payment_methods()
+                
+                def add_payment_method():
+                    """Function to redirect to payment methods page"""
+                    switch_func("user_payments")
+                
+                add_payment_btn = ctk.CTkButton(
+                    payment_frame,
+                    text="Add Payment Method",
+                    font=("Helvetica", 12),
+                    fg_color="#5A5A5A",
+                    hover_color="#6A6A6A",
+                    command=add_payment_method,
+                    width=150,
+                )
+                add_payment_btn.pack(side="left", padx=10)
+                
+                # Checkout button
+                def handle_checkout():
+                    # Get selected item IDs
+                    selected_item_ids = [
+                        item_id for item_id, var in selected_items.items() if var.get()
+                    ]
+                    
+                    if not selected_item_ids:
+                        messagebox.showerror("Error", "Please select at least one item")
+                        return
+                    
+                    if not payment_options or payment_options[0] == "No payment methods available":
+                        result = messagebox.askyesno(
+                            "No Payment Method", 
+                            "You don't have any payment methods. Would you like to add one now?"
+                        )
+                        if result:
+                            switch_func("user_payments")
+                        return
+                    
+                    delivery_addr = address_entry.get().strip()
+                    if not delivery_addr:
+                        messagebox.showerror("Error", "Please enter a delivery address")
+                        return
+                    
+                    selected_payment = payment_var.get()
+                    payment_id = payment_ids.get(selected_payment)
+                    
+                    if not payment_id:
+                        messagebox.showerror("Error", "Invalid payment method selected")
+                        return
+                    
+                    try:
+                        checkout_response = requests.post(
+                            f"{API_URL}/cart/checkout",
+                            json={
+                                "delivery_address": delivery_addr, 
+                                "payment_id": payment_id,
+                                "selected_items": selected_item_ids
+                            },
+                            headers=headers
+                        )
+                        
+                        if checkout_response.status_code == 200:
+                            messagebox.showinfo("Success", "Your order has been placed successfully!")
+                            # Refresh order history and clear cart view
+                            load_order_history()
+                            load_cart_items()
+                            # Switch to order history tab
+                            tab_view.set("Order History")
+                        else:
+                            error_msg = checkout_response.json().get("detail", "Unknown error")
+                            messagebox.showerror("Checkout Failed", f"Error: {error_msg}")
+                    
+                    except Exception as e:
+                        messagebox.showerror("Error", f"Checkout failed: {str(e)}")
+                
+                checkout_button = ctk.CTkButton(
+                    bottom_frame,
+                    text="Checkout Selected Items",
+                    font=("Helvetica", 14, "bold"),
+                    fg_color="#ff4242",
+                    hover_color="#ff6b6b",
+                    command=handle_checkout,
+                    state="disabled",  # Initially disabled until items are selected
+                    width=200,
+                    height=40
+                )
+                checkout_button.pack(side="right", padx=15)
+                
+            else:
+                error_msg = response.json().get("detail", "Unknown error")
+                messagebox.showerror("Error", f"Failed to load cart: {error_msg}")
+        
+        except Exception as e:
+            messagebox.showerror("Error", f"Failed to load cart: {str(e)}")
+    
+    def create_cart_item_frame(item, checkbox_var):
+        item_frame = ctk.CTkFrame(cart_items_frame, fg_color="#2b2b2b", corner_radius=5)
+        
+        # Create content frame - use grid layout for better alignment
+        content = ctk.CTkFrame(item_frame, fg_color="transparent")
+        content.pack(fill="x", padx=5, pady=10)
+        
+        # Configure grid columns with consistent widths
+        content.grid_columnconfigure(0, weight=0, minsize=40)    # Checkbox column
+        content.grid_columnconfigure(1, weight=1, minsize=300)   # Product column (expandable)
+        content.grid_columnconfigure(2, weight=0, minsize=100)   # Unit price column
+        content.grid_columnconfigure(3, weight=0, minsize=100)   # Quantity column
+        content.grid_columnconfigure(4, weight=0, minsize=100)   # Subtotal column
+        content.grid_columnconfigure(5, weight=0, minsize=90)    # Actions column
+        
+        # Checkbox - column 0
+        item_checkbox = ctk.CTkCheckBox(
+            content, 
+            text="",
+            variable=checkbox_var,
+            width=30
+        )
+        item_checkbox.grid(row=0, column=0, padx=5, sticky="w")
+        
+        # Product image and name - column 1
+        product_frame = ctk.CTkFrame(content, fg_color="transparent")
+        product_frame.grid(row=0, column=1, padx=5, sticky="w")
+        
+        # Create horizontal layout for image and text
+        img_frame = ctk.CTkFrame(product_frame, fg_color="#333333", width=60, height=60)
+        img_frame.pack(side="left", padx=5, pady=5)
+        img_frame.pack_propagate(False)
+        
+        img_label = ctk.CTkLabel(img_frame, text="")
+        img_label.place(relx=0.5, rely=0.5, anchor="center")
+        
+        # Try to load product image
+        images = item.get("images", [])
+        if images:
+            img_url = images[0].get("image_url")
+            if img_url:
+                fixed_url = fix_url(img_url)
+                try:
+                    resp = requests.get(fixed_url)
+                    if resp.status_code == 200:
+                        pil_img = Image.open(io.BytesIO(resp.content)).resize((50, 50))
+                        tk_img = ImageTk.PhotoImage(pil_img)
+                        img_label.configure(image=tk_img, text="")
+                        img_label.image = tk_img
+                except Exception as e:
+                    print(f"Failed to load product image: {e}")
+        
+        # Product details
+        details_frame = ctk.CTkFrame(product_frame, fg_color="transparent")
+        details_frame.pack(side="left", fill="both", expand=True, padx=10)
+        
+        # Product name
+        product_name = ctk.CTkLabel(
+            details_frame,
+            text=item.get("name", "Unknown Product"),
+            font=("Helvetica", 12, "bold"),
+            justify="left",
+            anchor="w",
+        )
+        product_name.pack(anchor="w")
+        
+        # Shop name
+        shop_name = ctk.CTkLabel(
+            details_frame,
+            text=f"Shop: {item.get('shop_name', 'Unknown Shop')}",
+            font=("Helvetica", 10),
+            text_color="#AAAAAA",
+            justify="left",
+            anchor="w",
+        )
+        shop_name.pack(anchor="w")
+        
+        # Unit price - column 2
+        unit_price = item.get("price", 0)
+        price_label = ctk.CTkLabel(
+            content,
+            text=f"â‚«{unit_price:,.0f}",
+            font=("Helvetica", 12),
+        )
+        price_label.grid(row=0, column=2, padx=5)
+        
+        # Quantity adjustment - column 3
+        quantity_frame = ctk.CTkFrame(content, fg_color="#1f1f1f")
+        quantity_frame.grid(row=0, column=3, padx=5)
+        
+        quantity = item.get("quantity", 1)
+        
+        def update_quantity(delta):
+            nonlocal quantity
+            item_id = item.get("id")
+            new_quantity = quantity + delta
+            
+            if 1 <= new_quantity <= 99:
+                try:
+                    headers = {"Authorization": f"Bearer {frame.token}"}
+                    response = requests.put(
+                        f"{API_URL}/cart/update/{item_id}",
+                        json={"quantity": new_quantity},
+                        headers=headers
+                    )
+                    
+                    if response.status_code == 200:
+                        # Update display
+                        quantity = new_quantity
+                        quantity_label.configure(text=str(quantity))
+                        
+                        # Update subtotal
+                        subtotal = unit_price * quantity
+                        subtotal_label.configure(text=f"â‚«{subtotal:,.0f}")
+                        
+                        # Reload cart to refresh totals
+                        load_cart_items()
+                    else:
+                        error_msg = response.json().get("detail", "Unknown error")
+                        messagebox.showerror("Error", f"Failed to update quantity: {error_msg}")
+                
+                except Exception as e:
+                    messagebox.showerror("Error", f"Failed to update quantity: {str(e)}")
+        
+        minus_btn = ctk.CTkButton(
+            quantity_frame, 
+            text="-", 
+            width=25,
+            height=25,
+            font=("Helvetica", 12),
+            command=lambda: update_quantity(-1)
+        )
+        minus_btn.pack(side="left", padx=2, pady=2)
+        
+        quantity_label = ctk.CTkLabel(
+            quantity_frame,
+            text=str(quantity),
+            width=30,
+            font=("Helvetica", 12),
+        )
+        quantity_label.pack(side="left")
+        
+        plus_btn = ctk.CTkButton(
+            quantity_frame, 
+            text="+", 
+            width=25,
+            height=25,
+            font=("Helvetica", 12),
+            command=lambda: update_quantity(1)
+        )
+        plus_btn.pack(side="left", padx=2, pady=2)
+        
+        # Subtotal - column 4
+        subtotal = item.get("subtotal", 0)
+        subtotal_label = ctk.CTkLabel(
+            content,
+            text=f"â‚«{subtotal:,.0f}",
+            font=("Helvetica", 12, "bold"),
+            text_color=SHOPPING,
+        )
+        subtotal_label.grid(row=0, column=4, padx=5)
+        
+        # Actions - column 5
+        def remove_item():
+            try:
+                headers = {"Authorization": f"Bearer {frame.token}"}
+                response = requests.delete(
+                    f"{API_URL}/cart/remove/{item.get('id')}",
+                    headers=headers
+                )
+                
+                if response.status_code == 200:
+                    load_cart_items()  # Refresh cart
+                else:
+                    error_msg = response.json().get("detail", "Unknown error")
+                    messagebox.showerror("Error", f"Failed to remove item: {error_msg}")
+            
+            except Exception as e:
+                messagebox.showerror("Error", f"Failed to remove item: {str(e)}")
+        
+        remove_btn = ctk.CTkButton(
+            content,
+            text="Remove",
+            font=("Helvetica", 12),
+            fg_color="#ff4242",
+            hover_color="#ff6b6b",
+            width=70,
+            command=remove_item,
+        )
+        remove_btn.grid(row=0, column=5, padx=5)
+        
+        return item_frame
+
+    # ORDER HISTORY TAB
+    orders_frame = ctk.CTkScrollableFrame(orders_tab, fg_color="transparent")
     orders_frame.pack(fill="both", expand=True, padx=20, pady=20)
 
     # Header for orders
@@ -226,6 +790,56 @@ def user_orders_frame(parent, switch_func, API_URL, token):
             font=("Helvetica", 12),
             text_color="white",
         ).pack(side="left")
+        
+        # Order items
+        items_frame = ctk.CTkFrame(details_frame, fg_color="transparent")
+        items_frame.pack(fill="x", pady=5)
+        
+        ctk.CTkLabel(
+            items_frame,
+            text="Ordered Items:",
+            font=("Helvetica", 12, "bold"),
+            text_color="#AAAAAA",
+        ).pack(anchor="w", padx=(10, 5), pady=(5, 0))
+        
+        # Create a frame for each order item
+        order_items = order.get("order_items", [])
+        for item in order_items:
+            item_frame = ctk.CTkFrame(items_frame, fg_color="#1e1e1e", corner_radius=5)
+            item_frame.pack(fill="x", padx=10, pady=2)
+            
+            # Get product details
+            try:
+                headers = {"Authorization": f"Bearer {frame.token}"}
+                product_id = item.get("product_id")
+                response = requests.get(f"{API_URL}/product/get/{product_id}", headers=headers)
+                
+                if response.status_code == 200:
+                    product = response.json()
+                    product_name = product.get("name", "Unknown Product")
+                else:
+                    product_name = f"Product #{product_id}"
+            except:
+                product_name = f"Product #{product_id}"
+            
+            # Product info
+            product_info = ctk.CTkLabel(
+                item_frame,
+                text=f"{product_name} × {item.get('quantity', 0)}",
+                font=("Helvetica", 12),
+                text_color="white",
+            )
+            product_info.pack(side="left", padx=10, pady=5)
+            
+            # Item price
+            item_price = item.get("price", 0) * item.get("quantity", 0)
+            price_label = ctk.CTkLabel(
+                item_frame,
+                text=f"â‚«{item_price:,.0f}",
+                font=("Helvetica", 12),
+                text_color="#AAAAAA",
+            )
+            price_label.pack(side="right", padx=10, pady=5)
 
         # Totals
         totals_frame = ctk.CTkFrame(details_frame, fg_color="transparent")
@@ -271,7 +885,8 @@ def user_orders_frame(parent, switch_func, API_URL, token):
 
         return order_frame
 
-    # Load orders when the frame is created
+    # Load orders and cart when the frame is created
     load_order_history()
+    load_cart_items()
 
     return frame
diff --git a/app/frontend/main.py b/app/frontend/main.py
index 7d3d133..7a2c8a8 100644
--- a/app/frontend/main.py
+++ b/app/frontend/main.py
@@ -1,3 +1,9 @@
+import sys
+import os
+
+# Add the current directory to the path
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
 import customtkinter as ctk
 from components.auth.login import login_frame
 from components.auth.register import register_frame
@@ -10,6 +16,8 @@ from components.dashboard import dashboard_frame
 from components.user_details import user_details_frame
 from components.user_orders import user_orders_frame
 from components.user_payments import user_payments_frame
+import traceback
+import sys
 
 API_URL = "http://127.0.0.1:8000"
 access_token = None  # Global token
@@ -82,33 +90,50 @@ def initialize_authenticated_frames(token):
             frame.place(relx=0, rely=0, relwidth=1, relheight=1)
 
 
-ctk.set_appearance_mode("dark")
-ctk.set_default_color_theme("blue")
+def main():
+    global root, frames
+    try:
+        print("Starting Shopping App...")
+        ctk.set_appearance_mode("dark")
+        ctk.set_default_color_theme("blue")
+
+        root = ctk.CTk()
+        root.title("Shopping App")
+        root.geometry("1000x800")
+        print("Window created successfully")
 
-root = ctk.CTk()
-root.title("Shopping App")
-root.geometry("1000x800")
+        # Create frames
+        frames = {}
 
-# Create frames
-frames = {}
+        # Login and register don't need tokens
+        print("Creating login and register frames...")
+        login = login_frame(root, switch_frame, API_URL)
+        register = register_frame(root, switch_frame, API_URL)
 
-# Login and register don't need tokens
-login = login_frame(root, switch_frame, API_URL)
-register = register_frame(root, switch_frame, API_URL)
+        # Collect all frames in a dictionary - start with unauthenticated frames
+        frames = {
+            "login": login,
+            "register": register,
+        }
 
-# Collect all frames in a dictionary - start with unauthenticated frames
-frames = {
-    "login": login,
-    "register": register,
-}
+        # Place login and register frames
+        frames["login"].place(relx=0, rely=0, relwidth=1, relheight=1)
+        frames["register"].place(relx=0, rely=0, relwidth=1, relheight=1)
+        print("Login and register frames placed")
+
+        # Create authenticated frames with empty token (they'll be recreated on login)
+        print("Initializing authenticated frames...")
+        initialize_authenticated_frames(access_token)
+        print("All frames initialized")
 
-# Place login and register frames
-frames["login"].place(relx=0, rely=0, relwidth=1, relheight=1)
-frames["register"].place(relx=0, rely=0, relwidth=1, relheight=1)
+        # Start with login frame
+        switch_frame("login")
+        print("Starting main loop...")
+        root.mainloop()
+    except Exception as e:
+        print(f"Error starting application: {e}")
+        traceback.print_exc()
 
-# Create authenticated frames with empty token (they'll be recreated on login)
-initialize_authenticated_frames(access_token)
 
-# Start with login frame
-switch_frame("login")
-root.mainloop()
+if __name__ == "__main__":
+    main()
-- 
GitLab