diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index 87b5082ff5682aa992a9ce58138dc956d55d6485..79570d5c1b180f2b907d6120c127ae6ad305b264 100644 --- a/app/frontend/components/auth/login.py +++ b/app/frontend/components/auth/login.py @@ -2,6 +2,7 @@ import customtkinter as ctk from tkinter import messagebox from utils.api_requests import login_api # Import the login function from login_api.py + def login_frame(parent, switch_func, API_URL): frame = ctk.CTkFrame(parent) @@ -22,7 +23,9 @@ def login_frame(parent, switch_func, API_URL): switch_func("view_shop", access_token) print(f"Access Token in login: {access_token}") # Debugging line else: - messagebox.showerror("Login Failed", response_data.get("detail", "Invalid credentials")) + messagebox.showerror( + "Login Failed", response_data.get("detail", "Invalid credentials") + ) ctk.CTkLabel(frame, text="Login", font=("Helvetica", 18, "bold")).pack(pady=10) diff --git a/app/frontend/components/auth/register.py b/app/frontend/components/auth/register.py index 01b1b75b4b65ee43b33c2bbea1faea42cfbcfb01..1be5aab735a0eb953c37c711a631aefbfcc93a50 100644 --- a/app/frontend/components/auth/register.py +++ b/app/frontend/components/auth/register.py @@ -2,6 +2,7 @@ import customtkinter as ctk from tkinter import messagebox from utils.api_requests import register_api # Import the API function + def register_frame(parent, switch_func, API_URL): frame = ctk.CTkFrame(parent) @@ -12,7 +13,13 @@ def register_frame(parent, switch_func, API_URL): password = entry_password.get() confirm_password = entry_confirm_password.get() - if not username or not email or not phone_number or not password or not confirm_password: + if ( + not username + or not email + or not phone_number + or not password + or not confirm_password + ): messagebox.showwarning("Input Error", "All fields are required!") return @@ -21,13 +28,17 @@ def register_frame(parent, switch_func, API_URL): return # Call the API function from register_api.py - status_code, response_data = register_api(username, email, phone_number, password, API_URL) + status_code, response_data = register_api( + username, email, phone_number, password, API_URL + ) if status_code == 200: messagebox.showinfo("Registration Successful", f"Welcome, {username}!") switch_func("login") else: - messagebox.showerror("Registration Failed", response_data.get("detail", "Unknown error")) + messagebox.showerror( + "Registration Failed", response_data.get("detail", "Unknown error") + ) ctk.CTkLabel(frame, text="Register", font=("Helvetica", 18, "bold")).pack(pady=10) diff --git a/app/frontend/components/dashboard.py b/app/frontend/components/dashboard.py index 32a128a096bba1ff38b15fd47f128b03212bfadc..934e685f890434bf3dee60b1ea50cdb686538809 100644 --- a/app/frontend/components/dashboard.py +++ b/app/frontend/components/dashboard.py @@ -6,8 +6,8 @@ import io SHOPPING = "#00c1ff" + def dashboard_frame(parent, switch_func, API_URL, token): - # Main container frame frame = ctk.CTkFrame(parent, fg_color="transparent") @@ -20,15 +20,13 @@ def dashboard_frame(parent, switch_func, API_URL, token): header_frame, text="Shopping App", text_color="white", - font=("Helvetica", 20, "bold") + 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 + header_frame, placeholder_text="Search in Shop...", height=30 ) search_entry.place(relx=0.25, rely=0.25, relwidth=0.45, relheight=0.5) @@ -55,7 +53,7 @@ def dashboard_frame(parent, switch_func, API_URL, token): text="Search", fg_color="white", text_color="black", - command=perform_search + command=perform_search, ) search_button.place(relx=0.71, rely=0.25, relwidth=0.08, relheight=0.5) @@ -65,26 +63,24 @@ def dashboard_frame(parent, switch_func, API_URL, token): # Section Title featured_label = ctk.CTkLabel( - middle_frame, - text="TOP SHOP", - font=("Helvetica", 16, "bold") + 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") @@ -111,10 +107,16 @@ def dashboard_frame(parent, switch_func, API_URL, token): 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") + 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") + ctk.CTkLabel(info_frame, text=f"Rating: {shop['rating']}").pack( + anchor="w" + ) # ------------- BOTTOM (Recommendations - Products) ------------- bottom_frame = ctk.CTkFrame(frame, fg_color="transparent") @@ -122,9 +124,7 @@ def dashboard_frame(parent, switch_func, API_URL, token): # Section Title recommend_label = ctk.CTkLabel( - bottom_frame, - text="TODAY'S RECOMMENDATIONS", - font=("Helvetica", 16, "bold") + bottom_frame, text="TODAY'S RECOMMENDATIONS", font=("Helvetica", 16, "bold") ) recommend_label.pack(pady=5) @@ -167,7 +167,11 @@ def dashboard_frame(parent, switch_func, API_URL, token): 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") + 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") diff --git a/app/frontend/components/shop/create_shop.py b/app/frontend/components/shop/create_shop.py index ed6ba4bbee4ba60f58f4a2ad08e6289a82caec60..e73fb5b2ab57b1e15f944f00fec33be8cfbc9644 100644 --- a/app/frontend/components/shop/create_shop.py +++ b/app/frontend/components/shop/create_shop.py @@ -3,6 +3,7 @@ from tkinter import messagebox, filedialog import os from utils.api_requests import create_shop_api # Import API function + def create_shop_frame(parent, switch_func, API_URL, token): frame = ctk.CTkFrame(parent) frame.access_token = token @@ -39,9 +40,13 @@ def create_shop_frame(parent, switch_func, API_URL, token): if status_code == 200: messagebox.showinfo("Shop Created", f"Shop '{name}' created successfully!") else: - messagebox.showerror("Error", response_data.get("detail", "An error occurred")) + messagebox.showerror( + "Error", response_data.get("detail", "An error occurred") + ) - ctk.CTkLabel(frame, text="Create Shop", font=("Helvetica", 18, "bold")).pack(pady=10) + ctk.CTkLabel(frame, text="Create Shop", font=("Helvetica", 18, "bold")).pack( + pady=10 + ) ctk.CTkLabel(frame, text="Shop Name:").pack(pady=5) entry_name = ctk.CTkEntry(frame, placeholder_text="Enter shop name") @@ -55,7 +60,9 @@ def create_shop_frame(parent, switch_func, API_URL, token): file_label = ctk.CTkLabel(frame, text="No file selected") file_label.pack(pady=5) - ctk.CTkButton(frame, text="Create Shop", fg_color="green", command=create_shop).pack(pady=15) + ctk.CTkButton( + frame, text="Create Shop", fg_color="green", command=create_shop + ).pack(pady=15) ctk.CTkButton( frame, text="Back", fg_color="transparent", command=lambda: switch_func("login") diff --git a/app/frontend/components/shop/view_shop.py b/app/frontend/components/shop/view_shop.py index 2c106654d7517b5a7d2942b279a80e0331456f33..3c4ae23dd957c0efca7bff9f95d27f4e84a68e22 100644 --- a/app/frontend/components/shop/view_shop.py +++ b/app/frontend/components/shop/view_shop.py @@ -1,7 +1,12 @@ import customtkinter as ctk from tkinter import messagebox from PIL import ImageTk -from utils.api_requests import fetch_shop_details, fetch_shop_products, load_image_from_url +from utils.api_requests import ( + fetch_shop_details, + fetch_shop_products, + load_image_from_url, +) + def view_shop_frame(parent, switch_func, API_URL, token): frame = ctk.CTkFrame(parent) @@ -11,10 +16,14 @@ def view_shop_frame(parent, switch_func, API_URL, token): title_label.pack(pady=10) # Shop Details - shop_name_label = ctk.CTkLabel(frame, text="Shop Name: ", font=("Helvetica", 14, "bold")) + shop_name_label = ctk.CTkLabel( + frame, text="Shop Name: ", font=("Helvetica", 14, "bold") + ) shop_name_label.pack(pady=5) - shop_description_label = ctk.CTkLabel(frame, text="Description: ", font=("Helvetica", 12)) + shop_description_label = ctk.CTkLabel( + frame, text="Description: ", font=("Helvetica", 12) + ) shop_description_label.pack(pady=5) shop_image_label = ctk.CTkLabel(frame, text="") # Placeholder for shop image @@ -33,7 +42,9 @@ def view_shop_frame(parent, switch_func, API_URL, token): return shop_name_label.configure(text=f"Shop Name: {shop_data['name']}") - shop_description_label.configure(text=f"Description: {shop_data.get('description', 'No description')}") + shop_description_label.configure( + text=f"Description: {shop_data.get('description', 'No description')}" + ) if "image_url" in shop_data and shop_data["image_url"]: image = load_image_from_url(shop_data["image_url"]) @@ -56,7 +67,9 @@ def view_shop_frame(parent, switch_func, API_URL, token): widget.destroy() if not products: - ctk.CTkLabel(product_list_frame, text="No products found.", font=("Helvetica", 12)).pack(pady=10) + ctk.CTkLabel( + product_list_frame, text="No products found.", font=("Helvetica", 12) + ).pack(pady=10) return for product in products: @@ -79,15 +92,25 @@ def view_shop_frame(parent, switch_func, API_URL, token): details_frame = ctk.CTkFrame(product_frame) details_frame.pack(side="left", fill="x", expand=True, padx=10) - ctk.CTkLabel(details_frame, text=product["name"], font=("Helvetica", 12, "bold")).pack(anchor="w") - ctk.CTkLabel(details_frame, text=f"Price: ${product['price']:.2f}", font=("Helvetica", 12)).pack(anchor="w") + ctk.CTkLabel( + details_frame, text=product["name"], font=("Helvetica", 12, "bold") + ).pack(anchor="w") + ctk.CTkLabel( + details_frame, + text=f"Price: ${product['price']:.2f}", + font=("Helvetica", 12), + ).pack(anchor="w") # Refresh Button - refresh_button = ctk.CTkButton(frame, text="Refresh", command=fetch_and_display_shop) + refresh_button = ctk.CTkButton( + frame, text="Refresh", command=fetch_and_display_shop + ) refresh_button.pack(pady=10) # Back Button - back_button = ctk.CTkButton(frame, text="Back", command=lambda: switch_func("login")) + back_button = ctk.CTkButton( + frame, text="Back", command=lambda: switch_func("login") + ) back_button.pack(pady=10) # Fetch shop data on load diff --git a/app/frontend/utils/api_requests.py b/app/frontend/utils/api_requests.py index 4f50771d7fd4a12c94986dd217f248d6083931fc..d17146c43f5b66338f8acb17aa47c28740b969c0 100644 --- a/app/frontend/utils/api_requests.py +++ b/app/frontend/utils/api_requests.py @@ -2,28 +2,32 @@ import requests from PIL import Image import io -#Login API + +# Login API def login_api(email, password, api_url): """ Sends login request to the API. - + :param email: User's email :param password: User's password :param api_url: Base API URL :return: Tuple (status_code, response_data) """ try: - response = requests.post(f"{api_url}/auth/login", json={"email": email, "password": password}) + response = requests.post( + f"{api_url}/auth/login", json={"email": email, "password": password} + ) response.raise_for_status() # Raise an error for 4xx and 5xx responses return response.status_code, response.json() # Return status and response data except requests.exceptions.RequestException as e: return None, {"error": str(e)} # Return error message if request fails -#Register API + +# Register API def register_api(username, email, phone_number, password, api_url): """ Sends a registration request to the API. - + :param username: User's username :param email: User's email :param phone_number: User's phone number @@ -46,7 +50,8 @@ def register_api(username, email, phone_number, password, api_url): except requests.exceptions.RequestException as e: return None, {"detail": str(e)} # Return error message if request fails -#Create Shop + +# Create Shop def create_shop_api(name, description, file_path, api_url, access_token): """ Sends a request to create a shop with optional image upload. @@ -81,7 +86,8 @@ def create_shop_api(name, description, file_path, api_url, access_token): except requests.exceptions.RequestException as e: return None, {"detail": str(e)} -#View Shop + +# View Shop def fetch_shop_details(api_url, token, shop_id=1): """ Fetches details of the shop owned by the logged-in user. @@ -101,7 +107,8 @@ def fetch_shop_details(api_url, token, shop_id=1): except requests.exceptions.RequestException as e: return {"error": str(e)} -#Fetch Products + +# Fetch Products def fetch_shop_products(api_url, token, shop_id): """ Fetches products for a given shop. @@ -113,7 +120,9 @@ def fetch_shop_products(api_url, token, shop_id): """ headers = {"Authorization": f"Bearer {token}"} try: - response = requests.get(f"{api_url}/products?shop_id={shop_id}", headers=headers) + response = requests.get( + f"{api_url}/products?shop_id={shop_id}", headers=headers + ) if response.status_code == 200: return response.json() else: @@ -121,7 +130,8 @@ def fetch_shop_products(api_url, token, shop_id): except requests.exceptions.RequestException as e: return {"error": str(e)} -#Load Image from URL + +# Load Image from URL def load_image_from_url(image_url, size=(150, 150)): """ Loads and resizes an image from a given URL. @@ -136,4 +146,4 @@ def load_image_from_url(image_url, size=(150, 150)): image = image.resize(size) return image except Exception: - return None \ No newline at end of file + return None