diff --git a/app/backend/database.py b/app/backend/database.py index d394b3c74e3b02c20656f0708626169bb9e33ec8..340a31feb280d4c31374a3f97629d4ac2dc4bac6 100644 --- a/app/backend/database.py +++ b/app/backend/database.py @@ -1,11 +1,14 @@ from sqlmodel import SQLModel, Session, create_engine from app.core.config import settings +from backend.dummy_data import insert_dummy_data engine = create_engine(settings.database_url, echo=settings.debug) def init_db(): SQLModel.metadata.create_all(engine, checkfirst=True) + with Session(engine) as session: + insert_dummy_data(session) def get_session(): diff --git a/app/backend/dummy_data.py b/app/backend/dummy_data.py new file mode 100644 index 0000000000000000000000000000000000000000..e2550dfc5253d893607822247aa3681f1a514bf9 --- /dev/null +++ b/app/backend/dummy_data.py @@ -0,0 +1,90 @@ +from sqlmodel import Session +from backend.models.models import ( + User, + Shop, + Category, + Product, + ProductImage, + Order, + OrderItem, +) +from backend.utils.hashing import hash_password + + +def insert_dummy_data(session: Session): + if not session.query(User).first(): + users = [ + User( + username="string", + email="user@example.com", + password=hash_password("string"), + role="customer", + ), + User( + username="shop_owner", + email="owner@example.com", + password=hash_password("string"), + role="shop_owner", + ), + ] + session.add_all(users) + session.commit() + + if not session.query(Shop).first(): + shops = [ + Shop( + owner_id=2, + name=f"Shop{i}", + description=f"Description for Shop {i}", + image_url="app/static/default/default_shop.png", + ) + for i in range(1, 6) + ] + session.add_all(shops) + session.commit() + + if not session.query(Category).first(): + categories = [Category(name=f"Category{i}") for i in range(1, 6)] + session.add_all(categories) + session.commit() + + if not session.query(Product).first(): + products = [ + Product( + shop_id=i, + category_id=i, + name=f"Product{i}", + description=f"Description for Product {i}", + price=99.99 + i, + stock=10 * i, + ) + for i in range(1, 6) + ] + session.add_all(products) + session.commit() + + if not session.query(ProductImage).first(): + images = [ + ProductImage( + product_id=i, image_url="app/static/default/default_product.png" + ) + for i in range(1, 6) + ] + session.add_all(images) + session.commit() + + if not session.query(Order).first(): + orders = [ + Order(user_id=1, shop_id=i, total_price=100 + (i * 50), status="pending") + for i in range(1, 6) + ] + session.add_all(orders) + session.commit() + + if not session.query(OrderItem).first(): + order_items = [ + OrderItem(order_id=i, product_id=i, quantity=i, price=99.99 + i) + for i in range(1, 6) + ] + session.add_all(order_items) + session.commit() diff --git a/app/backend/main.py b/app/backend/main.py index dfca6ef8856c50598f0224c007e0065f35ba2be7..3931b19e40c3f6cc0e218e3681bb167d3940567a 100644 --- a/app/backend/main.py +++ b/app/backend/main.py @@ -4,7 +4,11 @@ import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from fastapi import FastAPI +<<<<<<< HEAD from backend.routes import auth, shop, product, category, order, orderitem +======= +from backend.routes import auth, shop, product, category, search +>>>>>>> c8696ef4b9c116385985bf25a8a83fe6f3317225 from backend.database import init_db from core.config import settings @@ -14,6 +18,7 @@ app = FastAPI(title="Shopping App", version="1.0.0", debug=settings.debug) init_db() # Include API routes +app.include_router(search.router, prefix="/search", tags=["search"]) app.include_router(auth.router, prefix="/auth", tags=["auth"]) app.include_router(shop.router, prefix="/shops", tags=["shops"]) app.include_router(product.router, prefix="/product", tags=["product"]) @@ -21,7 +26,6 @@ app.include_router(category.router, prefix="/category", tags=["category"]) app.include_router(order.router, prefix="/order", tags=["order"]) app.include_router(orderitem.router, prefix="/orderitem", tags=["orderitem"]) - @app.get("/") async def root(): return {"message": "Welcome to the shopping app api"} diff --git a/app/backend/routes/product.py b/app/backend/routes/product.py index 4ccdeb6cb7937622a51f0ffa7ffd07c9293bd946..8c16bf78de6e50c0763fe3cb2f7cef1aaeb582b7 100644 --- a/app/backend/routes/product.py +++ b/app/backend/routes/product.py @@ -1,10 +1,11 @@ from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form from sqlmodel import Session from datetime import datetime -from backend.models.models import Product, ProductImage, User -from backend.schemas.product import ProductRead, ProductImageRead +from backend.models.models import Product, ProductImage, User, Shop +from backend.schemas.product import ProductRead, ProductUpdate from backend.database import get_session from backend.routes.auth import get_current_user +from core.config import settings import shutil import os @@ -22,7 +23,7 @@ def create_product( stock: int = Form(...), shop_id: int = Form(...), category_id: int = Form(None), - images: UploadFile = File(None), # Ensuring image is correctly set + images: list[UploadFile] = File(None), session: Session = Depends(get_session), ): product = Product( @@ -38,16 +39,33 @@ def create_product( session.commit() session.refresh(product) - # Handling image upload - if images and images.filename: - file_ext = os.path.splitext(images.filename)[1] # Get file extension - file_name = f"product_{product.id}{file_ext}" - file_location = os.path.join(static_dir, file_name) - - with open(file_location, "wb") as buffer: - shutil.copyfileobj(images.file, buffer) - - # Save image record in ProductImage + # Get the shop to create the correct directory + shop = session.get(Shop, shop_id) + if not shop: + raise HTTPException(status_code=404, detail="Shop not found") + + shop_dir = os.path.join(settings.static_dir, f"shop_{shop.name}") + os.makedirs(shop_dir, exist_ok=True) + + product_dir = os.path.join(shop_dir, f"product_{product.name}") + os.makedirs(product_dir, exist_ok=True) + + # Handling multiple image uploads + if images: + for image in images: + if image.filename: + file_location = os.path.join(product_dir, image.filename) + with open(file_location, "wb") as buffer: + shutil.copyfileobj(image.file, buffer) + + # Save image record in ProductImage + product_image = ProductImage( + product_id=product.id, image_url=file_location + ) + session.add(product_image) + session.commit() + else: # Save default image + file_location = os.path.join(settings.static_dir, "default/default_product.png") product_image = ProductImage(product_id=product.id, image_url=file_location) session.add(product_image) session.commit() @@ -55,6 +73,12 @@ def create_product( return product +@router.get("/list", response_model=list[ProductRead]) +def read_all_products(session: Session = Depends(get_session)): + products = session.query(Product).all() + return products + + @router.get("/{product_id}", response_model=ProductRead) def read_product(product_id: int, session: Session = Depends(get_session)): product = session.get(Product, product_id) @@ -63,44 +87,57 @@ def read_product(product_id: int, session: Session = Depends(get_session)): return product -@router.put("/{product_id}", response_model=ProductRead) +@router.put("/{product_id}", response_model=ProductUpdate) def update_product( product_id: int, - name: str = Form(None), - description: str = Form(None), - price: float = Form(None), - stock: int = Form(None), - category_id: int = Form(None), - file: UploadFile = File(None), + name: str = Form(...), + description: str = Form(...), + price: float = Form(...), + stock: int = Form(...), + category_id: int = Form(...), + images: list[UploadFile] = File(...), session: Session = Depends(get_session), ): product = session.get(Product, product_id) if not product: raise HTTPException(status_code=404, detail="Product not found") - - if name: - product.name = name - if description: - product.description = description - if price is not None: - product.price = price - if stock is not None: - product.stock = stock - if category_id is not None: - product.category_id = category_id + + # if name: + # product.name = name + # if description: + # product.description = description + # if price is not None: + # product.price = price + # if stock is not None: + # product.stock = stock + # if category_id is not None: + # product.category_id = category_id session.add(product) session.commit() session.refresh(product) - if file and file.filename: - file_location = os.path.join(static_dir, f"product_{product.id}_{file.filename}") - with open(file_location, "wb") as buffer: - shutil.copyfileobj(file.file, buffer) - - image = ProductImage(product_id=product.id, image_url=file_location) - session.add(image) - session.commit() + # Get the shop to create the correct directory + shop = session.get(Shop, product.shop_id) + if not shop: + raise HTTPException(status_code=404, detail="Shop not found") + + shop_dir = os.path.join(settings.static_dir, f"shop_{shop.name}") + os.makedirs(shop_dir, exist_ok=True) + + product_dir = os.path.join(shop_dir, f"product_{product.name}") + os.makedirs(product_dir, exist_ok=True) + + if images: + for file in images: + if file.filename: + file_location = os.path.join(product_dir, file.filename) + with open(file_location, "wb") as buffer: + shutil.copyfileobj(file.file, buffer) + + image = ProductImage(product_id=product.id, image_url=file_location) + session.add(image) + session.commit() return product @@ -113,41 +150,3 @@ def delete_product(product_id: int, session: Session = Depends(get_session)): session.delete(product) session.commit() return {"message": "Product deleted successfully"} - - -@router.post("/ProductImage/", response_model=ProductImageRead) -def upload_product_image( - product_id: int = Form(...), - file: UploadFile = File(...), - session: Session = Depends(get_session), -): - product = session.get(Product, product_id) - if not product: - raise HTTPException(status_code=404, detail="Product not found") - - file_location = os.path.join(static_dir, f"product_{product.id}_{file.filename}") - with open(file_location, "wb") as buffer: - shutil.copyfileobj(file.file, buffer) - - image = ProductImage(product_id=product.id, image_url=file_location) - session.add(image) - session.commit() - return image - - -@router.get("/ProductImage/{product_id}", response_model=list[ProductImageRead]) -def get_product_images(product_id: int, session: Session = Depends(get_session)): - images = session.query(ProductImage).filter(ProductImage.product_id == product_id).all() - if not images: - raise HTTPException(status_code=404, detail="No images found for this product") - return images - - -@router.delete("/ProductImage/{image_id}") -def delete_product_image(image_id: int, session: Session = Depends(get_session)): - image = session.get(ProductImage, image_id) - if not image: - raise HTTPException(status_code=404, detail="Image not found") - session.delete(image) - session.commit() - return {"message": "Product image deleted successfully"} diff --git a/app/backend/routes/search.py b/app/backend/routes/search.py new file mode 100644 index 0000000000000000000000000000000000000000..1c5999d77e9c19cfbdaf31ac5b4de06c61ab4e92 --- /dev/null +++ b/app/backend/routes/search.py @@ -0,0 +1,37 @@ +from fastapi import APIRouter, Depends, Query +from sqlmodel import Session, select +from backend.database import get_session +from backend.models.models import Shop, Product, Category +from typing import List, Union + +router = APIRouter() + + +@router.get("/search", response_model=List[Union[Shop, Product]]) +def search( + name: str = Query(None, description="Name to search"), + category: str = Query(None, description="Category to filter by (for products)"), + search_type: str = Query( + "both", description="Search type: 'shops', 'products', or 'both'" + ), + db: Session = Depends(get_session), +): + results = [] + + if search_type in ["shops", "both"]: + shop_query = select(Shop) + if name: + shop_query = shop_query.where(Shop.name.ilike(f"%{name}%")) + results.extend(db.exec(shop_query).all()) + + if search_type in ["products", "both"]: + product_query = ( + select(Product).join(Category).where(True) + ) # Ensures valid syntax + if name: + product_query = product_query.where(Product.name.ilike(f"%{name}%")) + if category: + product_query = product_query.where(Category.name.ilike(f"%{category}%")) + results.extend(db.exec(product_query).all()) + + return results diff --git a/app/backend/routes/shop.py b/app/backend/routes/shop.py index ffea6d91b86b52a89864fbf089686081e929589d..e6f364d49b56ec0afb917e0361fa8014aa2ca0c1 100644 --- a/app/backend/routes/shop.py +++ b/app/backend/routes/shop.py @@ -4,14 +4,12 @@ from backend.models.models import Shop, User from backend.schemas.shop import ShopCreate, ShopRead from backend.database import get_session from backend.routes.auth import get_current_user +from core.config import settings import shutil import os router = APIRouter() -static_dir = os.path.join("app", "static") -os.makedirs(static_dir, exist_ok=True) - @router.post("/", response_model=ShopRead) def create_shop( @@ -20,34 +18,38 @@ def create_shop( file: UploadFile = File(None), session: Session = Depends(get_session), # current_user: User = Depends(get_current_user), - owner_id = int, - + owner_id=int, ): # shop = ShopCreate(name=name, description=description, owner_id=current_user.id) shop = ShopCreate(name=name, description=description, owner_id=owner_id) db_shop = Shop.from_orm(shop) + session.add(db_shop) + session.commit() + session.refresh(db_shop) + + shop_dir = os.path.join(settings.static_dir, f"shop_{db_shop.name}") + os.makedirs(shop_dir, exist_ok=True) + if file and file.filename: - # Save the image to the static directory - file_location = os.path.join(static_dir, f"{db_shop.name}_{file.filename}") + file_location = os.path.join(shop_dir, file.filename) with open(file_location, "wb") as buffer: shutil.copyfileobj(file.file, buffer) - db_shop.image_url = file_location else: - # Set a default image URL if no file is uploaded - db_shop.image_url = os.path.join(static_dir, "default_shop_image.png") + db_shop.image_url = os.path.join( + settings.static_dir, "default/default_shop.png" + ) - session.add(db_shop) - session.commit() - session.refresh(db_shop) - - if file: - # Delete the image file after session commit - os.remove(file_location) return db_shop +@router.get("/list", response_model=list[ShopRead]) +def get_all_shops(session: Session = Depends(get_session)): + shops = session.query(Shop).all() + return shops + + @router.get("/{shop_id}", response_model=ShopRead) def read_shop(shop_id: int, session: Session = Depends(get_session)): shop = session.get(Shop, shop_id) @@ -73,25 +75,23 @@ def update_shop( if description: db_shop.description = description + shop_dir = os.path.join(settings.static_dir, f"shop_{db_shop.name}") + os.makedirs(shop_dir, exist_ok=True) + if file and file.filename: - # Save the image to the static directory - file_location = os.path.join(static_dir, f"{db_shop.name}_{file.filename}") + file_location = os.path.join(shop_dir, file.filename) with open(file_location, "wb") as buffer: shutil.copyfileobj(file.file, buffer) - db_shop.image_url = file_location else: - # Set a default image URL if no file is uploaded - db_shop.image_url = os.path.join(static_dir, "default_shop_image.png") + db_shop.image_url = os.path.join( + settings.static_dir, "default/default_shop.png" + ) session.add(db_shop) session.commit() session.refresh(db_shop) - if file: - # Delete the image file after session commit - os.remove(file_location) - return db_shop diff --git a/app/core/config.py b/app/core/config.py index 9666c8a4a488a66f56b3f690756a9d3aac9d3985..7e46517e091343dbc570e8b530b041ae3991dd5f 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,5 +1,6 @@ from pathlib import Path from pydantic_settings import BaseSettings, SettingsConfigDict +import os class Settings(BaseSettings): @@ -16,6 +17,10 @@ class Settings(BaseSettings): def database_url(self) -> str: return f"mysql+pymysql://{self.database_username}:{self.database_password}@{self.database_host}/{self.database_name}" + @property + def static_dir(self) -> str: + return os.path.join("app", "static") + model_config = SettingsConfigDict( env_file=str(Path(__file__).resolve().parent.parent / ".env"), env_file_encoding="utf-8", diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index 64a9f81c32cf23e640fc4999041a6574144781a6..1c7baeb1a6c9a3502caa178c898b74150f19f54f 100644 --- a/app/frontend/components/auth/login.py +++ b/app/frontend/components/auth/login.py @@ -23,7 +23,8 @@ def login_frame(parent, switch_func, API_URL): if response.status_code == 200: access_token = response_data["access_token"] messagebox.showinfo("Login Successful", f"Welcome back, {email}!") - switch_func("create_shop", access_token) + switch_func("dashboard", access_token) + print(f"Access Token in login: {access_token}") # Debugging line else: messagebox.showerror( "Login Failed", response_data.get("detail", "Invalid credentials") diff --git a/app/frontend/components/dashboard.py b/app/frontend/components/dashboard.py new file mode 100644 index 0000000000000000000000000000000000000000..32a128a096bba1ff38b15fd47f128b03212bfadc --- /dev/null +++ b/app/frontend/components/dashboard.py @@ -0,0 +1,205 @@ +import customtkinter as ctk +import requests +from PIL import Image, ImageTk +from tkinter import messagebox +import io + +SHOPPING = "#00c1ff" + +def dashboard_frame(parent, switch_func, API_URL, token): + + # Main container frame + frame = ctk.CTkFrame(parent, fg_color="transparent") + + # ------------- HEADER (Top Bar) ------------- + header_frame = ctk.CTkFrame(frame, fg_color=SHOPPING) + header_frame.place(relx=0, rely=0, relwidth=1, relheight=0.1) + + # App Logo (Left) + logo_label = ctk.CTkLabel( + header_frame, + text="Shopping App", + text_color="white", + font=("Helvetica", 20, "bold") + ) + logo_label.place(relx=0.01, rely=0.1, relwidth=0.15, relheight=0.8) + + # Search bar (Center) + search_entry = ctk.CTkEntry( + header_frame, + placeholder_text="Search in Shop...", + height=30 + ) + search_entry.place(relx=0.25, rely=0.25, relwidth=0.45, relheight=0.5) + + def perform_search(): + """Call an endpoint to search products by keyword.""" + keyword = search_entry.get().strip() + if not keyword: + messagebox.showinfo("Info", "Please enter a search keyword.") + return + try: + headers = {"Authorization": f"Bearer {token}"} + resp = requests.get(f"{API_URL}/product?search={keyword}", headers=headers) + if resp.status_code == 200: + products = resp.json() + display_products(products, bottom_products_frame) + else: + messagebox.showerror("Error", "Failed to fetch search results.") + except Exception as e: + messagebox.showerror("Error", f"Request error: {e}") + + # Search button + search_button = ctk.CTkButton( + header_frame, + text="Search", + fg_color="white", + text_color="black", + command=perform_search + ) + search_button.place(relx=0.71, rely=0.25, relwidth=0.08, relheight=0.5) + + # ------------- MIDDLE (Featured/Top Shops) ------------- + middle_frame = ctk.CTkFrame(frame, fg_color="transparent") + middle_frame.place(relx=0, rely=0.1, relwidth=1, relheight=0.25) + + # Section Title + featured_label = ctk.CTkLabel( + middle_frame, + text="TOP SHOP", + font=("Helvetica", 16, "bold") + ) + featured_label.pack(pady=5) + + # A frame to hold the featured shops + top_shops_frame = ctk.CTkFrame(middle_frame, fg_color="transparent") + top_shops_frame.pack(fill="both", expand=True, padx=10, pady=5) + + def display_shops(shops, container): + """Given a list of shop dicts, display them in the given container.""" + # Clear old widgets + for widget in container.winfo_children(): + widget.destroy() + + if not shops: + ctk.CTkLabel(container, text="No shops found.").pack(pady=10) + return + + # Display shops in a vertical list + for shop in shops: + sframe = ctk.CTkFrame(container, corner_radius=5, fg_color="#2b2b2b") + sframe.pack(fill="x", padx=5, pady=5) + + # Shop image/logo on left + image_label = ctk.CTkLabel(sframe, text="") + image_label.pack(side="left", padx=5, pady=5) + + # Load shop logo if available (assumes key "logo_url") + logo_url = shop.get("logo_url") + if logo_url: + try: + resp = requests.get(logo_url) + if resp.status_code == 200: + pil_img = Image.open(io.BytesIO(resp.content)).resize((50, 50)) + tk_img = ImageTk.PhotoImage(pil_img) + image_label.configure(image=tk_img, text="") + image_label.image = tk_img + except Exception as e: + print(f"Shop image error: {e}") + + # Shop info on right + info_frame = ctk.CTkFrame(sframe, fg_color="transparent") + info_frame.pack(side="left", fill="both", expand=True, padx=10) + + ctk.CTkLabel(info_frame, text=shop.get("name", "No Name"), font=("Helvetica", 13, "bold")).pack(anchor="w") + # Optionally add more shop details, for example a rating if available: + if shop.get("rating"): + ctk.CTkLabel(info_frame, text=f"Rating: {shop['rating']}").pack(anchor="w") + + # ------------- BOTTOM (Recommendations - Products) ------------- + bottom_frame = ctk.CTkFrame(frame, fg_color="transparent") + bottom_frame.place(relx=0, rely=0.35, relwidth=1, relheight=0.65) + + # Section Title + recommend_label = ctk.CTkLabel( + bottom_frame, + text="TODAY'S RECOMMENDATIONS", + font=("Helvetica", 16, "bold") + ) + recommend_label.pack(pady=5) + + bottom_products_frame = ctk.CTkFrame(bottom_frame, fg_color="transparent") + bottom_products_frame.pack(fill="both", expand=True, padx=10, pady=5) + + def display_products(products, container): + """Given a list of product dicts, display them in the given container.""" + # Clear old widgets + for widget in container.winfo_children(): + widget.destroy() + + if not products: + ctk.CTkLabel(container, text="No products found.").pack(pady=10) + return + + # Display products in a vertical list + for product in products: + pframe = ctk.CTkFrame(container, corner_radius=5, fg_color="#2b2b2b") + pframe.pack(fill="x", padx=5, pady=5) + + # Product image on left + image_label = ctk.CTkLabel(pframe, text="") + image_label.pack(side="left", padx=5, pady=5) + + # Load first image if available + if product.get("images"): + try: + img_url = product["images"][0]["image_url"] + resp = requests.get(img_url) + if resp.status_code == 200: + pil_img = Image.open(io.BytesIO(resp.content)).resize((50, 50)) + tk_img = ImageTk.PhotoImage(pil_img) + image_label.configure(image=tk_img, text="") + image_label.image = tk_img + except Exception as e: + print(f"Product image error: {e}") + + # Product info on right + info_frame = ctk.CTkFrame(pframe, fg_color="transparent") + info_frame.pack(side="left", fill="both", expand=True, padx=10) + + ctk.CTkLabel(info_frame, text=product.get("name", "No Name"), font=("Helvetica", 13, "bold")).pack(anchor="w") + price = product.get("price", 0.0) + ctk.CTkLabel(info_frame, text=f"Price: {price:.2f}").pack(anchor="w") + + def fetch_featured_shops(): + """Fetch some 'featured' or 'top' shops for the middle section.""" + headers = {"Authorization": f"Bearer {token}"} + try: + resp = requests.get(f"{API_URL}/shops/list", headers=headers) + if resp.status_code == 200: + shops = resp.json() + # Slice the list to display only a handful of top shops + display_shops(shops[:5], top_shops_frame) + else: + messagebox.showerror("Error", "Failed to fetch featured shops.") + except Exception as e: + messagebox.showerror("Error", f"Request error: {e}") + + def fetch_recommendations(): + """Fetch recommended products for the bottom section.""" + headers = {"Authorization": f"Bearer {token}"} + try: + resp = requests.get(f"{API_URL}/product/list", headers=headers) + if resp.status_code == 200: + products = resp.json() + display_products(products, bottom_products_frame) + else: + messagebox.showerror("Error", "Failed to fetch recommended products.") + except Exception as e: + messagebox.showerror("Error", f"Request error: {e}") + + # Load data on start + fetch_featured_shops() + fetch_recommendations() + + return frame diff --git a/app/frontend/components/shop/create_shop.py b/app/frontend/components/shop/create_shop.py index 5319ebe790fe8ce412bb9eb9b5f25fa5f89f4a71..44d4d79a66ee3cdb39d263915ce81b2131ae50af 100644 --- a/app/frontend/components/shop/create_shop.py +++ b/app/frontend/components/shop/create_shop.py @@ -3,9 +3,14 @@ from tkinter import messagebox, filedialog import requests import os - def create_shop_frame(parent, switch_func, API_URL, token): frame = ctk.CTkFrame(parent) + frame.access_token = token + + def update_token(new_token): + frame.access_token = new_token + + frame.update_token = update_token selected_file_path = [None] @@ -37,14 +42,14 @@ def create_shop_frame(parent, switch_func, API_URL, token): messagebox.showerror("File Error", f"Unable to open file: {str(e)}") return - if not token: + if not frame.access_token: messagebox.showerror( "Token Error", "Access token not found. Please log in." ) return - headers = {"Authorization": f"Bearer {token}"} - print(f"Access Token in create_shop: {token}") # Debugging line + headers = {"Authorization": f"Bearer {frame.access_token}"} + print(f"Access Token in create_shop: {frame.access_token}") # Debugging line try: response = requests.post(url, data=data, files=files, headers=headers) @@ -90,4 +95,4 @@ def create_shop_frame(parent, switch_func, API_URL, token): command=lambda: switch_func("login"), ).pack(pady=5) - return frame + return frame \ No newline at end of file diff --git a/app/frontend/components/shop/view_shop.py b/app/frontend/components/shop/view_shop.py index a5b9141d56308133b285a7556cc7e7867ddf8bf8..e7aa1583c30e38415253056ba8e7ed16b370d531 100644 --- a/app/frontend/components/shop/view_shop.py +++ b/app/frontend/components/shop/view_shop.py @@ -35,7 +35,7 @@ def view_shop_frame(parent, switch_func, API_URL, token): headers = {"Authorization": f"Bearer {token}"} try: response = requests.get( - f"{API_URL}/shops/my-shop", headers=headers + f"{API_URL}/shops/1", headers=headers ) # Adjust the endpoint as needed if response.status_code == 200: shop_data = response.json() @@ -137,4 +137,4 @@ def view_shop_frame(parent, switch_func, API_URL, token): # Fetch shop data on load fetch_shop_data() - return frame + return frame \ No newline at end of file diff --git a/app/frontend/main.py b/app/frontend/main.py index 2b14d6bfb1226f74e7656a0e8a250797731df2b6..62b98c49810c96e5b161119af7f9a733f2e5c711 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -1,3 +1,4 @@ +# main.py import customtkinter as ctk from components.auth.login import login_frame from components.auth.register import register_frame @@ -5,40 +6,41 @@ from components.shop.create_shop import create_shop_frame from components.shop.view_shop import view_shop_frame from components.product.create_product import product_frame from components.admin.category import category_frame +from components.dashboard import dashboard_frame # import the dashboard -# Backend API URL API_URL = "http://127.0.0.1:8000" - -# Global variable to store the access token access_token = None -# Function to switch between frames def switch_frame(frame_name, token=None): global access_token if token: access_token = token - frames.get(frame_name, login).tkraise() # Default to login if frame_name is invalid + frame = frames.get(frame_name, login) + if hasattr(frame, "update_token"): + frame.update_token(access_token) + frame.tkraise() -# Create main window -ctk.set_appearance_mode("dark") # Light, Dark, or System +ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") root = ctk.CTk() root.title("Shopping App") root.geometry("900x800") -# Create Frames inside the main window +# Create Frames login = login_frame(root, switch_frame, API_URL) register = register_frame(root, switch_frame, API_URL) create_shop = create_shop_frame(root, switch_frame, API_URL, access_token) view_shop = view_shop_frame(root, switch_frame, API_URL, access_token) product = product_frame(root, switch_frame, API_URL, access_token) category = category_frame(root, switch_frame, API_URL, access_token) +dashboard = dashboard_frame( + root, switch_frame, API_URL, access_token +) # new dashboard frame -# Store frames in a dictionary for easier management frames = { "login": login, "register": register, @@ -46,13 +48,13 @@ frames = { "create_product": product, "category": category, "view_shop": view_shop, + "dashboard": dashboard, # add dashboard here } -# Place all frames responsively for frame in frames.values(): - frame.place(relx=0, rely=0.2, relwidth=1, relheight=0.8) + frame.place(relx=0, rely=0, relwidth=1, relheight=0.8) -# Show the login frame first -switch_frame("view_shop") +# Show the login frame first (or switch to dashboard as needed) +switch_frame("login") # switch to dashboard root.mainloop() diff --git a/app/static/default/default_product.png b/app/static/default/default_product.png new file mode 100644 index 0000000000000000000000000000000000000000..f8a26b2d08887153a17797e6762ee17d71a89d08 Binary files /dev/null and b/app/static/default/default_product.png differ diff --git a/app/static/default/default_shop.png b/app/static/default/default_shop.png new file mode 100644 index 0000000000000000000000000000000000000000..ed0ec74950384a12f8f40a8e3bba9d1247c4c562 Binary files /dev/null and b/app/static/default/default_shop.png differ diff --git a/app/static/default_shop_image.png b/app/static/default_shop_image.png deleted file mode 100644 index cbe892f8fcae34a8022b471f94c9ef8b7140d7d6..0000000000000000000000000000000000000000 Binary files a/app/static/default_shop_image.png and /dev/null differ