From 6bf1f29843abd2c173e3cca87740db23b3f68225 Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Mon, 17 Mar 2025 22:07:12 +0700 Subject: [PATCH 1/7] Using Ruff for frontend --- app/frontend/components/auth/login.py | 5 ++- app/frontend/components/auth/register.py | 17 +++++++-- app/frontend/components/dashboard.py | 40 +++++++++++--------- app/frontend/components/shop/create_shop.py | 13 +++++-- app/frontend/components/shop/view_shop.py | 41 ++++++++++++++++----- app/frontend/utils/api_requests.py | 32 ++++++++++------ 6 files changed, 103 insertions(+), 45 deletions(-) diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index 87b5082..79570d5 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 01b1b75..1be5aab 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 32a128a..934e685 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 ed6ba4b..e73fb5b 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 2c10665..3c4ae23 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 4f50771..d17146c 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 -- GitLab From b31e9f3dd1a9b02eb53c82ad03741b1c8821bc45 Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Mon, 17 Mar 2025 22:59:50 +0700 Subject: [PATCH 2/7] Fix color for main.py in the frontend --- app/frontend/main.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/app/frontend/main.py b/app/frontend/main.py index 62b98c4..7deb904 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -1,4 +1,3 @@ -# main.py import customtkinter as ctk from components.auth.login import login_frame from components.auth.register import register_frame @@ -28,7 +27,7 @@ ctk.set_default_color_theme("blue") root = ctk.CTk() root.title("Shopping App") -root.geometry("900x800") +root.geometry("1000x800") # Create Frames login = login_frame(root, switch_frame, API_URL) @@ -37,9 +36,7 @@ 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 +dashboard = dashboard_frame(root, switch_frame, API_URL, access_token) frames = { "login": login, @@ -48,13 +45,13 @@ frames = { "create_product": product, "category": category, "view_shop": view_shop, - "dashboard": dashboard, # add dashboard here + "dashboard": dashboard, } for frame in frames.values(): - frame.place(relx=0, rely=0, relwidth=1, relheight=0.8) + frame.place(relx=0, rely=0, relwidth=1, relheight=1) # Adjusted height for full scaling -# Show the login frame first (or switch to dashboard as needed) -switch_frame("login") # switch to dashboard +# Show the login frame first +switch_frame("login") root.mainloop() -- GitLab From aba7ea1ec0363ea9f6490c997bdec1a531320c35 Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Mon, 17 Mar 2025 23:58:22 +0700 Subject: [PATCH 3/7] Modified .gitignore and change frontend components --- .gitignore | 3 +- app/frontend/components/auth/login.py | 38 +++++++++++++++++++------- app/frontend/main.py | 4 ++- app/static/front_end_img/login.jpg | Bin 0 -> 70252 bytes 4 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 app/static/front_end_img/login.jpg diff --git a/.gitignore b/.gitignore index cee315c..5085a4c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ __pycache__ # ignore all files in the static folder except the default folder app/static/* -!app/static/default/ \ No newline at end of file +!app/static/default/ +!app/static/front_end_img/ \ No newline at end of file diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index 79570d5..38f9228 100644 --- a/app/frontend/components/auth/login.py +++ b/app/frontend/components/auth/login.py @@ -1,10 +1,28 @@ import customtkinter as ctk from tkinter import messagebox +from PIL import Image 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) + # Create a container frame to hold both left (empty) and right (login) frames + container = ctk.CTkFrame(parent) + container.grid_columnconfigure(0, weight=1) # Left empty frame + container.grid_columnconfigure(1, weight=1) # Right login frame + container.grid_rowconfigure(0, weight=1) + + # Left empty frame (Just an empty frame to balance the layout) + left_frame = ctk.CTkFrame(container) + left_frame.grid(row=0, column=0, sticky="nsew") + + # Load and display the image + image_path = "app\static\login.jpg" # Change this to your image path + img = ctk.CTkImage(light_image=Image.open(image_path), size=(1000, 1000)) # Resize as needed + image_label = ctk.CTkLabel(left_frame, image=img, text="") # No text, only image + image_label.place(relwidth=1, relheight=1) + + # Right login frame + right_frame = ctk.CTkFrame(container) + right_frame.grid(row=0, column=1, sticky="nsew") def login(): email = entry_email.get() @@ -27,22 +45,22 @@ def login_frame(parent, switch_func, API_URL): "Login Failed", response_data.get("detail", "Invalid credentials") ) - ctk.CTkLabel(frame, text="Login", font=("Helvetica", 18, "bold")).pack(pady=10) + ctk.CTkLabel(right_frame, text="Login", font=("Helvetica", 18, "bold")).pack(pady=10) - ctk.CTkLabel(frame, text="Email:").pack(pady=5) - entry_email = ctk.CTkEntry(frame) + ctk.CTkLabel(right_frame, text="Email:").pack(pady=5) + entry_email = ctk.CTkEntry(right_frame) entry_email.pack(pady=5) - ctk.CTkLabel(frame, text="Password:").pack(pady=5) - entry_password = ctk.CTkEntry(frame, show="*") + ctk.CTkLabel(right_frame, text="Password:").pack(pady=5) + entry_password = ctk.CTkEntry(right_frame, show="*") entry_password.pack(pady=5) - ctk.CTkButton(frame, text="Login", command=login).pack(pady=15) + ctk.CTkButton(right_frame, text="Login", command=login).pack(pady=15) ctk.CTkButton( - frame, + right_frame, text="Don't have an account? Register", command=lambda: switch_func("register"), ).pack() - return frame + return container diff --git a/app/frontend/main.py b/app/frontend/main.py index 7deb904..6773394 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -49,7 +49,9 @@ frames = { } for frame in frames.values(): - frame.place(relx=0, rely=0, relwidth=1, relheight=1) # Adjusted height for full scaling + frame.place( + relx=0, rely=0, relwidth=1, relheight=1 + ) # Adjusted height for full scaling # Show the login frame first switch_frame("login") diff --git a/app/static/front_end_img/login.jpg b/app/static/front_end_img/login.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b8235c6d4067b453380179dda20c7c72e6da8ab5 GIT binary patch literal 70252 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 literal 0 HcmV?d00001 -- GitLab From b08671256f4f8d3a623beebca597b39c9f4a7444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Minh?= <w11220031@st.phenikaa-uni.edu.vn> Date: Tue, 18 Mar 2025 09:42:42 +0700 Subject: [PATCH 4/7] Modified login frontend --- app/frontend/components/auth/login.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index 38f9228..f2330a5 100644 --- a/app/frontend/components/auth/login.py +++ b/app/frontend/components/auth/login.py @@ -15,7 +15,7 @@ def login_frame(parent, switch_func, API_URL): left_frame.grid(row=0, column=0, sticky="nsew") # Load and display the image - image_path = "app\static\login.jpg" # Change this to your image path + image_path = "app/static/front_end_img/login.jpg" # Change this to your image path img = ctk.CTkImage(light_image=Image.open(image_path), size=(1000, 1000)) # Resize as needed image_label = ctk.CTkLabel(left_frame, image=img, text="") # No text, only image image_label.place(relwidth=1, relheight=1) -- GitLab From f2245f5f07e10e2aab12b0ee1291af4c09d2bc7d Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Tue, 25 Mar 2025 21:55:18 +0700 Subject: [PATCH 5/7] Add user_details and orders --- app/frontend/components/auth/login.py | 13 +- app/frontend/components/dashboard.py | 67 ++++++ app/frontend/components/user_details.py | 297 ++++++++++++++++++++++++ app/frontend/components/user_orders.py | 228 ++++++++++++++++++ app/frontend/main.py | 8 +- requirements.txt | Bin 1045 -> 2206 bytes 6 files changed, 608 insertions(+), 5 deletions(-) create mode 100644 app/frontend/components/user_details.py create mode 100644 app/frontend/components/user_orders.py diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index f2330a5..79f360b 100644 --- a/app/frontend/components/auth/login.py +++ b/app/frontend/components/auth/login.py @@ -3,6 +3,7 @@ from tkinter import messagebox from PIL import Image from utils.api_requests import login_api # Import the login function from login_api.py + def login_frame(parent, switch_func, API_URL): # Create a container frame to hold both left (empty) and right (login) frames container = ctk.CTkFrame(parent) @@ -16,10 +17,12 @@ def login_frame(parent, switch_func, API_URL): # Load and display the image image_path = "app/static/front_end_img/login.jpg" # Change this to your image path - img = ctk.CTkImage(light_image=Image.open(image_path), size=(1000, 1000)) # Resize as needed + img = ctk.CTkImage( + light_image=Image.open(image_path), size=(1000, 1000) + ) # Resize as needed image_label = ctk.CTkLabel(left_frame, image=img, text="") # No text, only image image_label.place(relwidth=1, relheight=1) - + # Right login frame right_frame = ctk.CTkFrame(container) right_frame.grid(row=0, column=1, sticky="nsew") @@ -38,14 +41,16 @@ def login_frame(parent, switch_func, API_URL): if status_code == 200: access_token = response_data.get("access_token") messagebox.showinfo("Login Successful", f"Welcome back, {email}!") - switch_func("view_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") ) - ctk.CTkLabel(right_frame, text="Login", font=("Helvetica", 18, "bold")).pack(pady=10) + ctk.CTkLabel(right_frame, text="Login", font=("Helvetica", 18, "bold")).pack( + pady=10 + ) ctk.CTkLabel(right_frame, text="Email:").pack(pady=5) entry_email = ctk.CTkEntry(right_frame) diff --git a/app/frontend/components/dashboard.py b/app/frontend/components/dashboard.py index 934e685..75622c9 100644 --- a/app/frontend/components/dashboard.py +++ b/app/frontend/components/dashboard.py @@ -57,6 +57,73 @@ def dashboard_frame(parent, switch_func, API_URL, token): ) search_button.place(relx=0.71, rely=0.25, relwidth=0.08, relheight=0.5) + # ------------- USER & CART ICONS (Top-Right) ------------- + def open_user_details(): + # Switch to user_details.py screen or run it + switch_func("user_details") + # Alternatively, use os.system("python user_details.py") + + def open_cart_details(): + # Switch to cart_shopping.py screen or run it + switch_func("cart_shopping") + # Alternatively, use os.system("python cart_shopping.py") + + # Try loading icon images; update paths as needed. + try: + user_image = Image.open("path/to/user_icon.png").resize((30, 30)) + user_icon = ImageTk.PhotoImage(user_image) + except Exception as e: + print(f"User icon load error: {e}") + user_icon = None + + try: + cart_image = Image.open("path/to/cart_icon.png").resize((30, 30)) + cart_icon = ImageTk.PhotoImage(cart_image) + except Exception as e: + print(f"Cart icon load error: {e}") + cart_icon = None + + if user_icon: + user_button = ctk.CTkButton( + header_frame, + image=user_icon, + text="", + fg_color="transparent", + command=open_user_details, + ) + switch_func("user_details") + user_button.image = user_icon + user_button.place(relx=0.82, rely=0.25, relwidth=0.08, relheight=0.5) + else: + user_button = ctk.CTkButton( + header_frame, + text="User", + fg_color="white", + text_color="black", + command=open_user_details, + ) + user_button.place(relx=0.82, rely=0.25, relwidth=0.08, relheight=0.5) + + if cart_icon: + cart_button = ctk.CTkButton( + header_frame, + image=cart_icon, + text="", + fg_color="transparent", + command=open_cart_details, + ) + cart_button.image = cart_icon + cart_button.place(relx=0.91, rely=0.25, relwidth=0.08, relheight=0.5) + else: + cart_button = ctk.CTkButton( + header_frame, + text="Cart", + fg_color="white", + text_color="black", + command=open_cart_details, + ) + cart_button.place(relx=0.91, 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) diff --git a/app/frontend/components/user_details.py b/app/frontend/components/user_details.py new file mode 100644 index 0000000..7a49032 --- /dev/null +++ b/app/frontend/components/user_details.py @@ -0,0 +1,297 @@ +import customtkinter as ctk +import requests +from PIL import Image, ImageTk +from tkinter import messagebox, filedialog +import io + +SHOPPING = "#00c1ff" + +def user_details_frame(parent, switch_func, API_URL, token): + """ + A two-column user details page that matches the dashboard color scheme: + - Top bar with SHOPPING color (#00c1ff) + - Left sidebar with a dark background (#2b2b2b) + - Right content area (profile form, profile picture) + """ + + # Main container frame (transparent background, like dashboard) + frame = ctk.CTkFrame(parent, fg_color="transparent") + + # ----------------- TOP BAR ----------------- + top_bar = ctk.CTkFrame(frame, fg_color=SHOPPING, height=40) + top_bar.pack(fill="x", side="top") + + top_label = ctk.CTkLabel( + top_bar, + text="My Profile", + text_color="white", + font=("Helvetica", 16, "bold"), + ) + top_label.pack(side="left", padx=20) + + def go_back(): + switch_func("dashboard") + + back_button = ctk.CTkButton( + top_bar, + text="Back", + fg_color="white", + text_color="black", + command=go_back, + width=60, + height=30, + ) + back_button.pack(side="right", padx=20, pady=5) + + # ----------------- MAIN SECTION (Sidebar + Content) ----------------- + main_section = ctk.CTkFrame(frame, fg_color="transparent") + main_section.pack(fill="both", expand=True) + + # ----------------- LEFT SIDEBAR ----------------- + + sidebar_frame = ctk.CTkFrame(main_section, width=200, fg_color="#2b2b2b") + sidebar_frame.pack(side="left", fill="y") + + sidebar_title = ctk.CTkLabel( + sidebar_frame, + text="Menu", + font=("Helvetica", 14, "bold"), + text_color="white" + ) + sidebar_title.pack(pady=(10, 5)) + + nav_dashboard = ctk.CTkButton( + sidebar_frame, + text="Dashboard", + fg_color="#2b2b2b", + text_color="white", + hover_color="#3b3b3b", + command=go_back + ) + nav_dashboard.pack(fill="x", padx=10, pady=5) + + nav_profile = ctk.CTkButton( + sidebar_frame, + text="My Profile", + fg_color="#3b3b3b", # Active/selected state + text_color="white", + hover_color="#3b3b3b", + state="disabled" + ) + nav_profile.pack(fill="x", padx=10, pady=5) + + nav_orders = ctk.CTkButton( + sidebar_frame, + text="My Orders", + fg_color="#2b2b2b", + text_color="white", + hover_color="#3b3b3b", + command=lambda: switch_func("user_orders") + ) + nav_orders.pack(fill="x", padx=10, pady=5) + + nav_address = ctk.CTkButton( + sidebar_frame, + text="Address Book", + fg_color="#2b2b2b", + text_color="white", + hover_color="#3b3b3b", + command=lambda: messagebox.showinfo("Info", "Address Book clicked!") + ) + nav_address.pack(fill="x", padx=10, pady=5) + + # ----------------- RIGHT CONTENT (User Details) ----------------- + content_frame = ctk.CTkFrame(main_section, fg_color="transparent") + content_frame.pack(side="left", fill="both", expand=True, padx=20, pady=20) + + # Title / Subtitle + title_label = ctk.CTkLabel( + content_frame, + text="My Profile", + font=("Helvetica", 18, "bold"), + text_color="white" + ) + title_label.pack(anchor="w", pady=(0, 5)) + + subtitle_label = ctk.CTkLabel( + content_frame, + text="Manage your profile information to keep your account secure", + font=("Helvetica", 12), + text_color="#cccccc" + ) + subtitle_label.pack(anchor="w", pady=(0, 15)) + + # -- Split the right content into two frames: Left form & Right picture -- + right_main = ctk.CTkFrame(content_frame, fg_color="transparent") + right_main.pack(fill="both", expand=True) + + # LEFT FORM + form_frame = ctk.CTkFrame(right_main, fg_color="transparent") + form_frame.pack(side="left", fill="both", expand=True, padx=(0, 20)) + + # Username + username_label = ctk.CTkLabel(form_frame, text="Username", font=("Helvetica", 12), text_color="white") + username_label.pack(anchor="w") + username_entry = ctk.CTkEntry(form_frame, placeholder_text="Enter your username...") + username_entry.pack(anchor="w", pady=(0, 10)) + + # Name + name_label = ctk.CTkLabel(form_frame, text="Name", font=("Helvetica", 12), text_color="white") + name_label.pack(anchor="w") + name_entry = ctk.CTkEntry(form_frame, placeholder_text="Enter your name...") + name_entry.pack(anchor="w", pady=(0, 10)) + + # Email + email_label = ctk.CTkLabel(form_frame, text="Email", font=("Helvetica", 12), text_color="white") + email_label.pack(anchor="w") + email_entry = ctk.CTkEntry(form_frame, placeholder_text="Enter your email...") + email_entry.pack(anchor="w", pady=(0, 10)) + + # Phone + phone_label = ctk.CTkLabel(form_frame, text="Phone", font=("Helvetica", 12), text_color="white") + phone_label.pack(anchor="w") + phone_entry = ctk.CTkEntry(form_frame, placeholder_text="Enter your phone...") + phone_entry.pack(anchor="w", pady=(0, 10)) + + # Gender + gender_label = ctk.CTkLabel(form_frame, text="Gender", font=("Helvetica", 12), text_color="white") + gender_label.pack(anchor="w", pady=(10, 0)) + + gender_var = ctk.StringVar(value="Male") # Default + gender_frame = ctk.CTkFrame(form_frame, fg_color="transparent") + gender_frame.pack(anchor="w", pady=5) + + male_radio = ctk.CTkRadioButton(gender_frame, text="Male", variable=gender_var, value="Male", text_color="white") + female_radio = ctk.CTkRadioButton(gender_frame, text="Female", variable=gender_var, value="Female", text_color="white") + other_radio = ctk.CTkRadioButton(gender_frame, text="Other", variable=gender_var, value="Other", text_color="white") + male_radio.pack(side="left", padx=5) + female_radio.pack(side="left", padx=5) + other_radio.pack(side="left", padx=5) + + # Birthday + birthday_label = ctk.CTkLabel(form_frame, text="Date of Birth", font=("Helvetica", 12), text_color="white") + birthday_label.pack(anchor="w", pady=(10, 0)) + birthday_entry = ctk.CTkEntry(form_frame, placeholder_text="dd/mm/yyyy") + birthday_entry.pack(anchor="w", pady=(0, 10)) + + # Save Button + def save_profile(): + """ + Example: Send a request to update user details. + Adjust the endpoint and payload for your backend. + """ + headers = {"Authorization": f"Bearer {token}"} + payload = { + "username": username_entry.get().strip(), + "name": name_entry.get().strip(), + "email": email_entry.get().strip(), + "phone": phone_entry.get().strip(), + "gender": gender_var.get(), + "birthday": birthday_entry.get().strip() + } + try: + resp = requests.put(f"{API_URL}/user/update", headers=headers, json=payload) + if resp.status_code == 200: + messagebox.showinfo("Success", "Profile updated successfully!") + else: + messagebox.showerror("Error", "Unable to update profile.") + except Exception as e: + messagebox.showerror("Error", f"Request error: {e}") + + save_button = ctk.CTkButton( + form_frame, + text="Save", + fg_color=SHOPPING, # #00c1ff + text_color="white", + command=save_profile, + width=80 + ) + save_button.pack(anchor="w", pady=(20, 10)) + + # RIGHT PICTURE SECTION + pic_frame = ctk.CTkFrame(right_main, fg_color="transparent") + pic_frame.pack(side="left", fill="both", expand=True) + + pic_label = ctk.CTkLabel(pic_frame, text="Profile Picture", font=("Helvetica", 12, "bold"), text_color="white") + pic_label.pack(anchor="w", pady=(0, 10)) + + photo_label = ctk.CTkLabel(pic_frame, text="No image", text_color="white") + photo_label.pack(anchor="w", pady=(0, 10)) + + def choose_photo(): + """ + Let the user pick a photo from their files. + Then optionally upload it or just display it. + """ + file_path = filedialog.askopenfilename( + title="Choose a Profile Picture", + filetypes=[("Image Files", "*.png *.jpg *.jpeg *.gif")] + ) + if file_path: + # Display chosen image + try: + pil_img = Image.open(file_path).resize((100, 100)) + tk_img = ImageTk.PhotoImage(pil_img) + photo_label.configure(image=tk_img, text="") + photo_label.image = tk_img + + # Optionally, upload the file to your server: + # headers = {"Authorization": f"Bearer {token}"} + # with open(file_path, 'rb') as f: + # requests.post(f'{API_URL}/user/upload_photo', files={'file': f}, headers=headers) + except Exception as e: + print(f"Image load error: {e}") + messagebox.showerror("Error", "Cannot load the image.") + + change_photo_button = ctk.CTkButton( + pic_frame, + text="Choose Image", + fg_color="white", + text_color="black", + command=choose_photo, + width=80 + ) + change_photo_button.pack(anchor="w") + + # Fetch existing user info (if needed) + def fetch_user_info(): + """ + Fetch the user's existing data to populate the form fields. + """ + headers = {"Authorization": f"Bearer {token}"} + try: + resp = requests.get(f"{API_URL}/user/profile", headers=headers) + if resp.status_code == 200: + data = resp.json() + username_entry.delete(0, "end") + username_entry.insert(0, data.get("username", "")) + name_entry.delete(0, "end") + name_entry.insert(0, data.get("name", "")) + email_entry.delete(0, "end") + email_entry.insert(0, data.get("email", "")) + phone_entry.delete(0, "end") + phone_entry.insert(0, data.get("phone", "")) + gender_var.set(data.get("gender", "Male")) + birthday_entry.delete(0, "end") + birthday_entry.insert(0, data.get("birthday", "")) + + # If there's a profile picture URL, load it + pic_url = data.get("photo_url") + if pic_url: + try: + resp_pic = requests.get(pic_url) + if resp_pic.status_code == 200: + pil_img = Image.open(io.BytesIO(resp_pic.content)).resize((100, 100)) + tk_img = ImageTk.PhotoImage(pil_img) + photo_label.configure(image=tk_img, text="") + photo_label.image = tk_img + except Exception as e: + print(f"Profile picture load error: {e}") + else: + messagebox.showerror("Error", "Unable to retrieve user information.") + except Exception as e: + messagebox.showerror("Error", f"Request error: {e}") + + fetch_user_info() + + return frame diff --git a/app/frontend/components/user_orders.py b/app/frontend/components/user_orders.py new file mode 100644 index 0000000..b71b34f --- /dev/null +++ b/app/frontend/components/user_orders.py @@ -0,0 +1,228 @@ +import customtkinter as ctk +import requests +from PIL import Image, ImageTk +from tkinter import messagebox +import io + +SHOPPING = "#00c1ff" + +def user_orders_frame(parent, switch_func, API_URL, token): + """ + A two-column user orders page that displays the products the user has purchased + along with associated shop details. The layout and color scheme match your dashboard. + """ + + # Main container frame with transparent background + frame = ctk.CTkFrame(parent, fg_color="transparent") + + # ----------------- TOP BAR ----------------- + top_bar = ctk.CTkFrame(frame, fg_color=SHOPPING, height=40) + top_bar.pack(fill="x", side="top") + + top_label = ctk.CTkLabel( + top_bar, + text="My Orders", + text_color="white", + font=("Helvetica", 16, "bold") + ) + top_label.pack(side="left", padx=20) + + def go_back(): + switch_func("dashboard") + + back_button = ctk.CTkButton( + top_bar, + text="Back", + fg_color="white", + text_color="black", + command=go_back, + width=60, + height=30 + ) + back_button.pack(side="right", padx=20, pady=5) + + # ----------------- MAIN SECTION (Sidebar + Content) ----------------- + main_section = ctk.CTkFrame(frame, fg_color="transparent") + main_section.pack(fill="both", expand=True) + + # ----------------- LEFT SIDEBAR ----------------- + sidebar_frame = ctk.CTkFrame(main_section, width=200, fg_color="#2b2b2b") + sidebar_frame.pack(side="left", fill="y") + + sidebar_title = ctk.CTkLabel( + sidebar_frame, + text="Menu", + font=("Helvetica", 14, "bold"), + text_color="white" + ) + sidebar_title.pack(pady=(10, 5)) + + def open_profile(): + switch_func("user_details") + + nav_dashboard = ctk.CTkButton( + sidebar_frame, + text="Dashboard", + fg_color="#2b2b2b", + text_color="white", + hover_color="#3b3b3b", + command=go_back + ) + nav_dashboard.pack(fill="x", padx=10, pady=5) + + nav_profile = ctk.CTkButton( + sidebar_frame, + text="My Profile", + fg_color="#2b2b2b", + text_color="white", + hover_color="#3b3b3b", + command=open_profile + ) + nav_profile.pack(fill="x", padx=10, pady=5) + + nav_orders = ctk.CTkButton( + sidebar_frame, + text="My Orders", + fg_color="#3b3b3b", # Active/selected state + text_color="white", + hover_color="#3b3b3b", + state="disabled" + ) + nav_orders.pack(fill="x", padx=10, pady=5) + + # ----------------- RIGHT CONTENT (Orders List) ----------------- + content_frame = ctk.CTkFrame(main_section, fg_color="transparent") + content_frame.pack(side="left", fill="both", expand=True, padx=20, pady=20) + + main_title_label = ctk.CTkLabel( + content_frame, + text="Your Orders", + font=("Helvetica", 18, "bold"), + text_color="white" + ) + main_title_label.pack(anchor="w", pady=(0, 5)) + + subtitle_label = ctk.CTkLabel( + content_frame, + text="Review the products you have purchased.", + font=("Helvetica", 12), + text_color="#cccccc" + ) + subtitle_label.pack(anchor="w", pady=(0, 15)) + + # A frame to hold the list of orders + orders_list_frame = ctk.CTkFrame(content_frame, fg_color="transparent") + orders_list_frame.pack(fill="both", expand=True) + + # ----------- Functions to fetch and display orders ----------- + def fetch_orders(): + """ + Fetch the list of user orders from the API. + """ + headers = {"Authorization": f"Bearer {token}"} + try: + resp = requests.get(f"{API_URL}/orders/list", headers=headers) + if resp.status_code == 200: + orders = resp.json() # Expect a list of order dicts + display_orders(orders, orders_list_frame) + else: + messagebox.showerror("Error", "Failed to fetch orders.") + except Exception as ex: + messagebox.showerror("Error", f"Request error: {ex}") + + def display_orders(orders, container): + """ + Display each order with product and shop details. + """ + # Clear previous content + for widget in container.winfo_children(): + widget.destroy() + + if not orders: + ctk.CTkLabel(container, text="No orders found.", text_color="white").pack(pady=10) + return + + for order in orders: + # Assume each order dict includes a 'product' dict and an 'order_date' + product = order.get("product", {}) + order_date = order.get("order_date", "Unknown Date") + + order_frame = ctk.CTkFrame(container, corner_radius=5, fg_color="#2b2b2b") + order_frame.pack(fill="x", padx=5, pady=5) + + # Left: Product image + image_label = ctk.CTkLabel(order_frame, text="") + image_label.pack(side="left", padx=5, pady=5) + if product.get("images"): + try: + img_url = product["images"][0]["image_url"] + img_resp = requests.get(img_url) + if img_resp.status_code == 200: + pil_img = Image.open(io.BytesIO(img_resp.content)).resize((60, 60)) + tk_img = ImageTk.PhotoImage(pil_img) + image_label.configure(image=tk_img, text="") + image_label.image = tk_img + except Exception as ex: + print(f"Product image error: {ex}") + + # Right: Order details and shop info + info_frame = ctk.CTkFrame(order_frame, fg_color="transparent") + info_frame.pack(side="left", fill="both", expand=True, padx=10) + + # Product Name + ctk.CTkLabel( + info_frame, + text=product.get("name", "No Name"), + font=("Helvetica", 13, "bold"), + text_color="white" + ).pack(anchor="w") + + # Price (and order date) + price = product.get("price", 0.0) + ctk.CTkLabel( + info_frame, + text=f"Price: {price:.2f}", + text_color="#cccccc" + ).pack(anchor="w") + ctk.CTkLabel( + info_frame, + text=f"Ordered on: {order_date}", + text_color="#cccccc" + ).pack(anchor="w") + + # Shop Name + shop_id = product.get("shop_id") + shop_name_label = ctk.CTkLabel(info_frame, text="Shop: Loading...", text_color="#cccccc") + shop_name_label.pack(anchor="w") + + def fetch_shop_and_update_label(sid, label_widget): + headers = {"Authorization": f"Bearer {token}"} + try: + sresp = requests.get(f"{API_URL}/shop/get/{sid}", headers=headers) + if sresp.status_code == 200: + shop_data = sresp.json() + label_widget.configure(text=f"Shop: {shop_data.get('name', 'No Shop Name')}") + else: + label_widget.configure(text="Shop: Not found") + except Exception: + label_widget.configure(text="Shop: Error fetching") + + fetch_shop_and_update_label(shop_id, shop_name_label) + + # "View Order" button (placeholder action) + def view_order(): + messagebox.showinfo("Order Details", f"View details for order of {product.get('name')}") + + view_button = ctk.CTkButton( + info_frame, + text="View Order", + fg_color=SHOPPING, + text_color="white", + command=view_order + ) + view_button.pack(anchor="e", pady=(5, 0)) + + # Fetch orders when the frame loads + fetch_orders() + + return frame diff --git a/app/frontend/main.py b/app/frontend/main.py index 6773394..65b8f0a 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -5,7 +5,9 @@ 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 +from components.dashboard import dashboard_frame +from components.user_details import user_details_frame +from components.user_orders import user_orders_frame API_URL = "http://127.0.0.1:8000" access_token = None @@ -37,6 +39,8 @@ 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) +user_details = user_details_frame(root, switch_frame, API_URL, access_token) +user_orders = user_orders_frame(root, switch_frame, API_URL, access_token) frames = { "login": login, @@ -46,6 +50,8 @@ frames = { "category": category, "view_shop": view_shop, "dashboard": dashboard, + "user_details": user_details, + "user_orders": user_orders, } for frame in frames.values(): diff --git a/requirements.txt b/requirements.txt index 2383f12bc661c32a3b8eac0fcf9146bd60a0f3fe..1c5bb150f1acbbb5b29d84ddfede66a9ed8b7204 100644 GIT binary patch literal 2206 zcmZvd%}yIZ5QOI(DUY%U+k}uq4qQ=?5)moN2^s&bF<!&2!H|bfQeW4O#~`#?+j4hT zcURZ+{QEmjjh;=~rm@~xnx;zMFX>*tyYyAhaXLz$^&Y2>>4Ur)S(l2ycB22!_O_ce zNo~Y#!#a{>7%!0%A-2L^rnz3Chr%4DPx2V(9qH44^SuZE4<dWE)vBOXWiugFQF)uz z`tMGde<B0OWTv(pg>{lvk(GMEJ?pb=6uS}TIB;^uM4$E^xu;E9gx`It)usE#4~B-l z@1ttl3U95?m9jSarf<CIwD2*FPF7-^US_J#tbtZIoeH7TG+tz_h1&!YC}k$IsC=nD zuG3S`1?be_%+8u;IG2qo$HMg_$T`sCS(vrgUX|*D#mtXY!Mql#{g4@$#`9)hsg+|t zIst=<%7ox`rl(OPY~aBRJ%6&)QD4qJh*;y+^53eHNpw4lSmH{B*#kb9cG2}|RAGFh zJV!jS!<d4(SZYd*_$qZd_fnZV**F#S*9sp6<PPc5o)@8oTX_x-P>#en*->ySy-XFq z6ILZ}^qtv)3u-dfW$*nc`;~srl{r`D%8fZyEv@=3tYw|9HH9uc$i`ezRU-@(h+>@g zP;Xy?kDMQ$Tg7KJdQxOYLFB$yRh+l6Sm6vTdPbPIPYNELo}v_HhNe$?1oX_w+(j(v zv>wa>%uFJWx51&#bB33GGVGj5UjZ0Y>az`sr)Qr^EIf+^w!zyy$SfWEK7OTc&@M=H zTvXtVV_sh8yH;JW>G}DT!hth<S9J?%gL8q-vZ@A&d03hFT%=#==YiFeARpFCy;tc+ z`quMk79CChdCpa+k4@BQi;ydyqW65)($-z{$|TwAS!GtBr$qkW>0C89JLk=Is-y1* z;c(L&_BT@h+z0kyc6DD5N~3pcq>Z`OF)5#HHg2h85Ie+NcEXs2qC6e$k@dby_MS02 zb<YjO9pyU?7OZgUF_Y&)qAE6T*=KY%>hBewuC*}O@8t}-d-vuY%n7w_tO7hvq|bcX z@@;H|WA&bPU$(lZYvG_?CfG&&d@~nsTI-809$%ebZ+(zGHE#`{%u&R99>xA7bSu~Q m5wWAlVuYR8-3CMONPn&)E6QB&^#6@48+oquJ8QuG9jE{G_(BZ; literal 1045 zcmYLIOK;mS48H5XSnw-pdhnswVgWjIz_3%H%8IQ<mQ;yu>iznCl-KPjisbhosk{%Q z$cz<j(d1#M<(;gB@|z9A(907ZuR3j!hM|+KY=t(7?8Od4Tek<<$gUCk!tX{lcM^|T zrEIA3AuY<;-zITo&p*!U>oA;9bP_takkq0Wv6n|tFv65noHqdXvSzF`Q&{L~J#m{u zAuy_R9Ss=`N_8PP`W!c!g9oWX-z>_ymk+>?iUKM@96~NClZv2P%ccSM<||Tx8b={s zOlD_c5qssznnMw!+M;4g#&~CnACN5OrYQ}<Sj<FGhh*3c?!|8k!4+SRvM+&8PH*9Y zu%n0mPByi$lt<+!lfsJRo$jOcZ>lW`@*ozL9d5p|R56<l2(>sPy2fX90VYRUv5aeS zg%Llt{O&3pyoX&Op>Vh9zW5Q+TV~Ukfou*UDt%Rx^%EHMsE#V<VnPE~dl9X3;RbDx z4d}S($fMrvMfL^-WHr-AzvI6^Wj-mXuLUcg|FbLX3IVdHW>db}yzEB8iaStw+NQ;L zDp}1v(w?@*pFjRW@$0-jfB#mNu=HS=)fmY5k9`>2xCEEgkp=OxJus|B@vnz7YLCUg zWZqYkcciYBok&*C=+Vi0c)*W}M|!oT0m7}wvvCd@X0<Tb0eRV590g7Y2LeLi_Tcnv zma;~*>`nrMS;A<_s6e9=*v*8!B1{q|0WyM567KSBfPxn=USOv2*#-|Ua8-Pg>pn(o uG_M54kD+z!4K_lo9~Mqx%0T5Ey(!Z3#X3HA-qK#&%xez%YFIOnYVil?XFVtY -- GitLab From 1c39532ed5079c32e3026ae24ab48b00ebd91acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Minh?= <w11220031@st.phenikaa-uni.edu.vn> Date: Fri, 28 Mar 2025 21:04:26 +0700 Subject: [PATCH 6/7] update frontend for login, register, dashboard and user details --- app/frontend/components/auth/login.py | 85 +++++++++++------- app/frontend/components/auth/register.py | 107 ++++++++++++++--------- app/frontend/components/dashboard.py | 3 +- app/frontend/components/user_details.py | 10 --- 4 files changed, 124 insertions(+), 81 deletions(-) diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index 79f360b..dc2d30d 100644 --- a/app/frontend/components/auth/login.py +++ b/app/frontend/components/auth/login.py @@ -1,32 +1,31 @@ import customtkinter as ctk from tkinter import messagebox from PIL import Image -from utils.api_requests import login_api # Import the login function from login_api.py +from utils.api_requests import login_api def login_frame(parent, switch_func, API_URL): - # Create a container frame to hold both left (empty) and right (login) frames - container = ctk.CTkFrame(parent) - container.grid_columnconfigure(0, weight=1) # Left empty frame - container.grid_columnconfigure(1, weight=1) # Right login frame + # Overall container + container = ctk.CTkFrame(parent, fg_color="#F3F4F6") # Light gray background + container.grid_columnconfigure(0, weight=1) + container.grid_columnconfigure(1, weight=1) container.grid_rowconfigure(0, weight=1) - # Left empty frame (Just an empty frame to balance the layout) - left_frame = ctk.CTkFrame(container) + # Left image frame + left_frame = ctk.CTkFrame(container, fg_color="transparent") left_frame.grid(row=0, column=0, sticky="nsew") - # Load and display the image - image_path = "app/static/front_end_img/login.jpg" # Change this to your image path - img = ctk.CTkImage( - light_image=Image.open(image_path), size=(1000, 1000) - ) # Resize as needed - image_label = ctk.CTkLabel(left_frame, image=img, text="") # No text, only image + image_path = "app/static/front_end_img/login.jpg" + img = ctk.CTkImage(light_image=Image.open(image_path), size=(800, 800)) + image_label = ctk.CTkLabel(left_frame, image=img, text="") image_label.place(relwidth=1, relheight=1) # Right login frame - right_frame = ctk.CTkFrame(container) - right_frame.grid(row=0, column=1, sticky="nsew") + right_frame = ctk.CTkFrame(container, fg_color="white", corner_radius=20) + right_frame.grid(row=0, column=1, sticky="nsew", padx=50, pady=100) + right_frame.grid_columnconfigure(0, weight=1) + # Login Function def login(): email = entry_email.get() password = entry_password.get() @@ -35,37 +34,61 @@ def login_frame(parent, switch_func, API_URL): messagebox.showwarning("Input Error", "Both fields are required!") return - # Call the API function from login_api.py status_code, response_data = login_api(email, password, API_URL) if status_code == 200: access_token = response_data.get("access_token") messagebox.showinfo("Login Successful", f"Welcome back, {email}!") 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") - ) + messagebox.showerror("Login Failed", response_data.get("detail", "Invalid credentials")) - ctk.CTkLabel(right_frame, text="Login", font=("Helvetica", 18, "bold")).pack( - pady=10 - ) + # Title + ctk.CTkLabel( + right_frame, + text="Login", + font=ctk.CTkFont("Helvetica", size=26, weight="bold"), + text_color="#111827" + ).pack(pady=(20, 10)) - ctk.CTkLabel(right_frame, text="Email:").pack(pady=5) - entry_email = ctk.CTkEntry(right_frame) - entry_email.pack(pady=5) + # Email + ctk.CTkLabel( + right_frame, text="Email", font=("Helvetica", 14), text_color="#374151" + ).pack(pady=(10, 5)) + entry_email = ctk.CTkEntry( + right_frame, height=40, corner_radius=10, placeholder_text="Enter your email" + ) + entry_email.pack(pady=5, padx=20, fill="x") - ctk.CTkLabel(right_frame, text="Password:").pack(pady=5) - entry_password = ctk.CTkEntry(right_frame, show="*") - entry_password.pack(pady=5) + # Password + ctk.CTkLabel( + right_frame, text="Password", font=("Helvetica", 14), text_color="#374151" + ).pack(pady=(10, 5)) + entry_password = ctk.CTkEntry( + right_frame, height=40, show="*", corner_radius=10, placeholder_text="Enter your password" + ) + entry_password.pack(pady=5, padx=20, fill="x") - ctk.CTkButton(right_frame, text="Login", command=login).pack(pady=15) + # Login Button + ctk.CTkButton( + right_frame, + text="Login", + command=login, + corner_radius=10, + height=45, + font=("Helvetica", 14), + fg_color="#2563EB", + hover_color="#1E40AF" + ).pack(pady=20, padx=20, fill="x") + # Register Redirect ctk.CTkButton( right_frame, text="Don't have an account? Register", command=lambda: switch_func("register"), - ).pack() + fg_color="transparent", + hover_color="#E5E7EB", + text_color="#2563EB" + ).pack(pady=(5, 20)) return container diff --git a/app/frontend/components/auth/register.py b/app/frontend/components/auth/register.py index 1be5aab..d2a6353 100644 --- a/app/frontend/components/auth/register.py +++ b/app/frontend/components/auth/register.py @@ -4,7 +4,14 @@ from utils.api_requests import register_api # Import the API function def register_frame(parent, switch_func, API_URL): - frame = ctk.CTkFrame(parent) + # Create a full-window container with a light gray background + container = ctk.CTkFrame(parent, fg_color="#F3F4F6") + container.pack(expand=True, fill="both") + + # Center the register panel (the white card) in the middle of the container. + # Width and height are set to ensure a consistent size. + register_panel = ctk.CTkFrame(container, fg_color="white", corner_radius=20, width=400, height=500) + register_panel.place(relx=0.5, rely=0.5, anchor="center") def register(): username = entry_username.get() @@ -13,13 +20,7 @@ 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 @@ -36,38 +37,66 @@ def register_frame(parent, switch_func, API_URL): messagebox.showinfo("Registration Successful", f"Welcome, {username}!") switch_func("login") else: - messagebox.showerror( - "Registration Failed", response_data.get("detail", "Unknown error") - ) - - ctk.CTkLabel(frame, text="Register", font=("Helvetica", 18, "bold")).pack(pady=10) - - ctk.CTkLabel(frame, text="Username:").pack(pady=5) - entry_username = ctk.CTkEntry(frame) - entry_username.pack(pady=5) - - ctk.CTkLabel(frame, text="Email:").pack(pady=5) - entry_email = ctk.CTkEntry(frame) - entry_email.pack(pady=5) - - ctk.CTkLabel(frame, text="Phone Number:").pack(pady=5) - entry_phone = ctk.CTkEntry(frame) - entry_phone.pack(pady=5) - - ctk.CTkLabel(frame, text="Password:").pack(pady=5) - entry_password = ctk.CTkEntry(frame, show="*") - entry_password.pack(pady=5) - - ctk.CTkLabel(frame, text="Confirm Password:").pack(pady=5) - entry_confirm_password = ctk.CTkEntry(frame, show="*") - entry_confirm_password.pack(pady=5) - - ctk.CTkButton(frame, text="Register", command=register).pack(pady=15) - + messagebox.showerror("Registration Failed", response_data.get("detail", "Unknown error")) + + # Title for the registration form + ctk.CTkLabel( + register_panel, + text="Create Account", + font=ctk.CTkFont("Helvetica", size=26, weight="bold"), + text_color="#111827" + ).pack(pady=(20, 10)) + + # Username Entry + entry_username = ctk.CTkEntry( + register_panel, placeholder_text="Username", height=40, corner_radius=10, width=250 + ) + entry_username.pack(pady=10, padx=20, fill="x") + + # Email Entry + entry_email = ctk.CTkEntry( + register_panel, placeholder_text="Email", height=40, corner_radius=10, width=250 + ) + entry_email.pack(pady=10, padx=20, fill="x") + + # Phone Number Entry + entry_phone = ctk.CTkEntry( + register_panel, placeholder_text="Phone Number", height=40, corner_radius=10, width=250 + ) + entry_phone.pack(pady=10, padx=20, fill="x") + + # Password Entry + entry_password = ctk.CTkEntry( + register_panel, placeholder_text="Password", show="*", height=40, corner_radius=10, width=250 + ) + entry_password.pack(pady=10, padx=20, fill="x") + + # Confirm Password Entry + entry_confirm_password = ctk.CTkEntry( + register_panel, placeholder_text="Confirm Password", show="*", height=40, corner_radius=10, width=250 + ) + entry_confirm_password.pack(pady=10, padx=20, fill="x") + + # Register Button + ctk.CTkButton( + register_panel, + text="Register", + command=register, + corner_radius=10, + height=45, + font=("Helvetica", 14), + fg_color="#2563EB", + hover_color="#1E40AF" + ).pack(pady=20, padx=20, fill="x") + + # Link to Login Page ctk.CTkButton( - frame, + register_panel, text="Already have an account? Login", command=lambda: switch_func("login"), - ).pack() + fg_color="transparent", + hover_color="#E5E7EB", + text_color="#2563EB" + ).pack(pady=(5, 20)) - return frame + return container diff --git a/app/frontend/components/dashboard.py b/app/frontend/components/dashboard.py index 75622c9..f6c1af4 100644 --- a/app/frontend/components/dashboard.py +++ b/app/frontend/components/dashboard.py @@ -65,7 +65,7 @@ def dashboard_frame(parent, switch_func, API_URL, token): def open_cart_details(): # Switch to cart_shopping.py screen or run it - switch_func("cart_shopping") + switch_func("user_orders") # Alternatively, use os.system("python cart_shopping.py") # Try loading icon images; update paths as needed. @@ -112,6 +112,7 @@ def dashboard_frame(parent, switch_func, API_URL, token): fg_color="transparent", command=open_cart_details, ) + switch_func("user_orders") cart_button.image = cart_icon cart_button.place(relx=0.91, rely=0.25, relwidth=0.08, relheight=0.5) else: diff --git a/app/frontend/components/user_details.py b/app/frontend/components/user_details.py index 7a49032..a6b1a8f 100644 --- a/app/frontend/components/user_details.py +++ b/app/frontend/components/user_details.py @@ -90,16 +90,6 @@ def user_details_frame(parent, switch_func, API_URL, token): ) nav_orders.pack(fill="x", padx=10, pady=5) - nav_address = ctk.CTkButton( - sidebar_frame, - text="Address Book", - fg_color="#2b2b2b", - text_color="white", - hover_color="#3b3b3b", - command=lambda: messagebox.showinfo("Info", "Address Book clicked!") - ) - nav_address.pack(fill="x", padx=10, pady=5) - # ----------------- RIGHT CONTENT (User Details) ----------------- content_frame = ctk.CTkFrame(main_section, fg_color="transparent") content_frame.pack(side="left", fill="both", expand=True, padx=20, pady=20) -- GitLab From b02b661896dc99282382b7e6e49a22be969f902f Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Fri, 28 Mar 2025 22:30:40 +0700 Subject: [PATCH 7/7] Make change for product and shop also main to show img for frontend and make change for dashboard for prettier appearance --- app/backend/main.py | 9 +- app/backend/routes/product.py | 69 +++--- app/backend/routes/shop.py | 36 ++-- app/frontend/components/auth/login.py | 2 +- app/frontend/components/dashboard.py | 290 +++++++++++++------------- 5 files changed, 201 insertions(+), 205 deletions(-) diff --git a/app/backend/main.py b/app/backend/main.py index 658f0ca..9606ccc 100644 --- a/app/backend/main.py +++ b/app/backend/main.py @@ -4,13 +4,20 @@ import os 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 from backend.database import init_db from core.config import settings app = FastAPI(title="Shopping App", version="1.0.0", debug=settings.debug) -# initialize database +# ------------------- NEW: MOUNT STATIC FILES ------------------- +# Suppose your static files are located in "app/static" +# Adjust the path if needed. +static_dir_path = os.path.join(os.path.dirname(__file__), "..", "static") +app.mount("/static", StaticFiles(directory=static_dir_path), name="static") + +# Initialize database init_db() # Include API routes diff --git a/app/backend/routes/product.py b/app/backend/routes/product.py index fcad951..30ca0e9 100644 --- a/app/backend/routes/product.py +++ b/app/backend/routes/product.py @@ -11,9 +11,7 @@ import os router = APIRouter() -static_dir = os.path.join("app", "static") -os.makedirs(static_dir, exist_ok=True) - +BACKEND_HOST = "http://127.0.0.1:8000" # Or use settings.backend_url if available @router.post("/create", response_model=ProductRead) def create_product( @@ -27,6 +25,12 @@ def create_product( session: Session = Depends(get_session), current_user: User = Depends(get_current_user), ): + shop = session.get(Shop, shop_id) + if not shop or shop.owner_id != current_user.id: + raise HTTPException( + status_code=403, detail="Unauthorized to create product for this shop" + ) + product = Product( name=name, description=description, @@ -40,35 +44,31 @@ def create_product( session.commit() session.refresh(product) - shop = session.get(Shop, shop_id) - if not shop or shop.owner_id != current_user.id: - raise HTTPException( - status_code=403, detail="Unauthorized to create product for this shop" - ) - - # Directory structure: static/shop_{shop_name}/product_{product_name}/ + # Directory: static/shop_{shop.name}/product_{product.name}/ 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 - ) + relative_path = os.path.relpath(file_location, settings.static_dir) + relative_path = relative_path.replace("\\", "/") + public_url = f"{BACKEND_HOST}/static/{relative_path}" + product_image = ProductImage(product_id=product.id, image_url=public_url) 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) + else: + # Use default product image from static/default folder + default_path = os.path.join(settings.static_dir, "default", "default_product.png") + relative_path = os.path.relpath(default_path, settings.static_dir) + relative_path = relative_path.replace("\\", "/") + public_url = f"{BACKEND_HOST}/static/{relative_path}" + product_image = ProductImage(product_id=product.id, image_url=public_url) session.add(product_image) session.commit() @@ -82,7 +82,6 @@ def read_all_products(order: str = "desc", session: Session = Depends(get_sessio if order == "desc" else func.count(OrderItem.id).asc() ) - products = ( session.query(Product) .outerjoin(OrderItem, Product.id == OrderItem.product_id) @@ -90,7 +89,6 @@ def read_all_products(order: str = "desc", session: Session = Depends(get_sessio .order_by(order_by) .all() ) - return products @@ -120,28 +118,16 @@ def update_product( status_code=403, detail="Unauthorized to update this product" ) - 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 + product.name = name + product.description = description + product.price = price + product.stock = stock + product.category_id = category_id session.add(product) session.commit() session.refresh(product) - product = session.get(Product, product_id) - if not product or product.shop.owner_id != current_user.id: - raise HTTPException( - status_code=403, detail="Unauthorized to update this product" - ) - - # Directory structure: static/shop_{shop_name}/product_{product_name}/ shop = session.get(Shop, product.shop_id) shop_dir = os.path.join(settings.static_dir, f"shop_{shop.name}") os.makedirs(shop_dir, exist_ok=True) @@ -154,8 +140,10 @@ def update_product( 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) + relative_path = os.path.relpath(file_location, settings.static_dir) + relative_path = relative_path.replace("\\", "/") + public_url = f"{BACKEND_HOST}/static/{relative_path}" + image = ProductImage(product_id=product.id, image_url=public_url) session.add(image) session.commit() @@ -173,7 +161,6 @@ def delete_product( raise HTTPException( status_code=403, detail="Unauthorized to delete this product" ) - session.delete(product) session.commit() return {"message": "Product deleted successfully"} diff --git a/app/backend/routes/shop.py b/app/backend/routes/shop.py index a240d1b..d5a651a 100644 --- a/app/backend/routes/shop.py +++ b/app/backend/routes/shop.py @@ -11,6 +11,8 @@ import os router = APIRouter() +# Define your backend host URL. +BACKEND_HOST = "http://127.0.0.1:8000" # Or use settings.backend_url if available @router.post("/create", response_model=ShopRead) def create_shop( @@ -39,6 +41,7 @@ def create_shop( session.commit() session.refresh(shop) + # Create a folder in the static directory for the shop shop_dir = os.path.join(settings.static_dir, f"shop_{shop.name}") os.makedirs(shop_dir, exist_ok=True) @@ -46,13 +49,20 @@ def create_shop( file_location = os.path.join(shop_dir, file.filename) with open(file_location, "wb") as buffer: shutil.copyfileobj(file.file, buffer) - shop.image_url = file_location + relative_path = os.path.relpath(file_location, settings.static_dir) + relative_path = relative_path.replace("\\", "/") + public_url = f"{BACKEND_HOST}/static/{relative_path}" + shop.image_url = public_url else: - shop.image_url = os.path.join(settings.static_dir, "default/default_shop.png") + # Use default shop image from static/default folder + default_path = os.path.join(settings.static_dir, "default", "default_shop.png") + relative_path = os.path.relpath(default_path, settings.static_dir) + relative_path = relative_path.replace("\\", "/") + public_url = f"{BACKEND_HOST}/static/{relative_path}" + shop.image_url = public_url session.commit() session.refresh(shop) - return shop @@ -61,7 +71,6 @@ def get_all_shops(order: str = "desc", session: Session = Depends(get_session)): order_by = ( func.count(Order.id).desc() if order == "desc" else func.count(Order.id).asc() ) - shops = ( session.query(Shop) .outerjoin(Order, Shop.id == Order.shop_id) @@ -69,7 +78,6 @@ def get_all_shops(order: str = "desc", session: Session = Depends(get_session)): .order_by(order_by) .all() ) - return shops @@ -94,10 +102,9 @@ def update_shop( shop = session.get(Shop, shop_id) if not shop: raise HTTPException(status_code=404, detail="Shop not found") - - # Ensure the current user is the shop owner if shop.owner_id != current_user.id: raise HTTPException(status_code=403, detail="Unauthorized to update this shop") + if name: shop.name = name if description: @@ -118,14 +125,20 @@ def update_shop( file_location = os.path.join(shop_dir, file.filename) with open(file_location, "wb") as buffer: shutil.copyfileobj(file.file, buffer) - shop.image_url = file_location + relative_path = os.path.relpath(file_location, settings.static_dir) + relative_path = relative_path.replace("\\", "/") + public_url = f"{BACKEND_HOST}/static/{relative_path}" + shop.image_url = public_url else: - shop.image_url = os.path.join(settings.static_dir, "default/default_shop.png") + default_path = os.path.join(settings.static_dir, "default", "default_shop.png") + relative_path = os.path.relpath(default_path, settings.static_dir) + relative_path = relative_path.replace("\\", "/") + public_url = f"{BACKEND_HOST}/static/{relative_path}" + shop.image_url = public_url session.add(shop) session.commit() session.refresh(shop) - return shop @@ -138,11 +151,8 @@ def delete_shop( shop = session.get(Shop, shop_id) if not shop: raise HTTPException(status_code=404, detail="Shop not found") - - # Ensure the current user is the shop owner if shop.owner_id != current_user.id: raise HTTPException(status_code=403, detail="Unauthorized to delete this shop") - session.delete(shop) session.commit() return {"message": "Shop deleted successfully"} diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index dc2d30d..31d2560 100644 --- a/app/frontend/components/auth/login.py +++ b/app/frontend/components/auth/login.py @@ -15,7 +15,7 @@ def login_frame(parent, switch_func, API_URL): left_frame = ctk.CTkFrame(container, fg_color="transparent") left_frame.grid(row=0, column=0, sticky="nsew") - image_path = "app/static/front_end_img/login.jpg" + image_path = "../../app/static/front_end_img/login.jpg" img = ctk.CTkImage(light_image=Image.open(image_path), size=(800, 800)) image_label = ctk.CTkLabel(left_frame, image=img, text="") image_label.place(relwidth=1, relheight=1) diff --git a/app/frontend/components/dashboard.py b/app/frontend/components/dashboard.py index f6c1af4..f5707c9 100644 --- a/app/frontend/components/dashboard.py +++ b/app/frontend/components/dashboard.py @@ -5,10 +5,29 @@ from tkinter import messagebox import io SHOPPING = "#00c1ff" - +BACKEND_HOST = "http://127.0.0.1:8000" # Adjust if needed + +def fix_url(url): + """ + If the provided URL does not start with http, assume it's a relative path. + Remove any unwanted prefix (e.g., "app/static/") and prepend the public URL. + """ + if url.startswith("http"): + return url + # If the URL starts with "app/static/", remove that part. + prefix = "app/static/" + if url.startswith(prefix): + url = url[len(prefix):] + # Prepend the public URL + return f"{BACKEND_HOST}/static/{url}" def dashboard_frame(parent, switch_func, API_URL, token): - # Main container frame + """ + Main dashboard UI: + - Header (top bar) with logo, search, user, cart + - Middle area with horizontal scroll of top shops + - Bottom area with grid layout of recommended products + """ frame = ctk.CTkFrame(parent, fg_color="transparent") # ------------- HEADER (Top Bar) ------------- @@ -38,16 +57,16 @@ def dashboard_frame(parent, switch_func, API_URL, token): return try: headers = {"Authorization": f"Bearer {token}"} - resp = requests.get(f"{API_URL}/product?search={keyword}", headers=headers) + # Adjust endpoint/params as needed + resp = requests.get(f"{API_URL}/search?name={keyword}&search_type=products", 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.") + messagebox.showerror("Error", f"Search failed. Status code: {resp.status_code}") except Exception as e: messagebox.showerror("Error", f"Request error: {e}") - # Search button search_button = ctk.CTkButton( header_frame, text="Search", @@ -59,89 +78,80 @@ def dashboard_frame(parent, switch_func, API_URL, token): # ------------- USER & CART ICONS (Top-Right) ------------- def open_user_details(): - # Switch to user_details.py screen or run it switch_func("user_details") - # Alternatively, use os.system("python user_details.py") def open_cart_details(): - # Switch to cart_shopping.py screen or run it - switch_func("user_orders") - # Alternatively, use os.system("python cart_shopping.py") - - # Try loading icon images; update paths as needed. - try: - user_image = Image.open("path/to/user_icon.png").resize((30, 30)) - user_icon = ImageTk.PhotoImage(user_image) - except Exception as e: - print(f"User icon load error: {e}") - user_icon = None - - try: - cart_image = Image.open("path/to/cart_icon.png").resize((30, 30)) - cart_icon = ImageTk.PhotoImage(cart_image) - except Exception as e: - print(f"Cart icon load error: {e}") - cart_icon = None - - if user_icon: - user_button = ctk.CTkButton( - header_frame, - image=user_icon, - text="", - fg_color="transparent", - command=open_user_details, - ) - switch_func("user_details") - user_button.image = user_icon - user_button.place(relx=0.82, rely=0.25, relwidth=0.08, relheight=0.5) - else: - user_button = ctk.CTkButton( - header_frame, - text="User", - fg_color="white", - text_color="black", - command=open_user_details, - ) - user_button.place(relx=0.82, rely=0.25, relwidth=0.08, relheight=0.5) - - if cart_icon: - cart_button = ctk.CTkButton( - header_frame, - image=cart_icon, - text="", - fg_color="transparent", - command=open_cart_details, - ) switch_func("user_orders") - cart_button.image = cart_icon - cart_button.place(relx=0.91, rely=0.25, relwidth=0.08, relheight=0.5) - else: - cart_button = ctk.CTkButton( - header_frame, - text="Cart", - fg_color="white", - text_color="black", - command=open_cart_details, - ) - cart_button.place(relx=0.91, rely=0.25, relwidth=0.08, relheight=0.5) + + user_button = ctk.CTkButton( + header_frame, + text="User", + fg_color="white", + text_color="black", + command=open_user_details, + ) + user_button.place(relx=0.82, rely=0.25, relwidth=0.08, relheight=0.5) + + cart_button = ctk.CTkButton( + header_frame, + text="Cart", + fg_color="white", + text_color="black", + command=open_cart_details, + ) + cart_button.place(relx=0.91, 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 = ctk.CTkScrollableFrame( + middle_frame, fg_color="transparent", width=750, height=150 + ) top_shops_frame.pack(fill="both", expand=True, padx=10, pady=5) + def create_shop_card(parent, shop_data): + """ + Create a card for a shop with its image on top and name below. + """ + card = ctk.CTkFrame(parent, corner_radius=5, fg_color="#2b2b2b", width=100, height=130) + card.pack_propagate(False) + + image_label = ctk.CTkLabel(card, text="") + image_label.pack(pady=5) + + # Use "image_url" as returned by the backend, then fix it if needed. + image_url = shop_data.get("image_url") + if image_url: + fixed_url = fix_url(image_url) + print(f"[DEBUG] Fetching shop image from: {fixed_url}") + try: + resp = requests.get(fixed_url) + print(f"[DEBUG] Status code for shop image: {resp.status_code}") + if resp.status_code == 200: + pil_img = Image.open(io.BytesIO(resp.content)).resize((60, 60)) + tk_img = ImageTk.PhotoImage(pil_img) + image_label.configure(image=tk_img, text="") + image_label.image = tk_img + else: + print(f"[DEBUG] Failed to fetch shop image. Status: {resp.status_code}") + except Exception as e: + print(f"[DEBUG] Shop image error: {e}") + else: + print(f"[DEBUG] No 'image_url' found for shop: {shop_data.get('name')}") + + name_text = shop_data.get("name", "No Name") + name_label = ctk.CTkLabel(card, text=name_text, font=("Helvetica", 11, "bold"), wraplength=90) + name_label.pack() + + return card + 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() @@ -149,59 +159,68 @@ def dashboard_frame(parent, switch_func, API_URL, token): 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) + inner_frame = ctk.CTkFrame(container, fg_color="transparent") + inner_frame.pack(side="top", fill="both", expand=True) - # Shop image/logo on left - image_label = ctk.CTkLabel(sframe, text="") - image_label.pack(side="left", padx=5, pady=5) + for shop in shops: + shop_card = create_shop_card(inner_frame, shop) + shop_card.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 (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 = ctk.CTkScrollableFrame( + bottom_frame, fg_color="transparent", width=750, height=300 + ) bottom_products_frame.pack(fill="both", expand=True, padx=10, pady=5) + def create_product_card(parent, product_data): + card_width = 130 + card_height = 210 + card = ctk.CTkFrame(parent, corner_radius=5, fg_color="#2b2b2b", width=card_width, height=card_height) + card.pack_propagate(False) + + image_label = ctk.CTkLabel(card, text="") + image_label.pack(pady=5) + + product_images = product_data.get("images", []) + if product_images: + img_url = product_images[0].get("image_url") + if img_url: + fixed_url = fix_url(img_url) + print(f"[DEBUG] Fetching product image from: {fixed_url}") + try: + resp = requests.get(fixed_url) + print(f"[DEBUG] Status code for product image: {resp.status_code}") + if resp.status_code == 200: + pil_img = Image.open(io.BytesIO(resp.content)).resize((70, 70)) + tk_img = ImageTk.PhotoImage(pil_img) + image_label.configure(image=tk_img, text="") + image_label.image = tk_img + else: + print(f"[DEBUG] Failed to fetch product image. Status: {resp.status_code}") + except Exception as e: + print(f"[DEBUG] Product image error: {e}") + else: + print(f"[DEBUG] No images found for product: {product_data.get('name')}") + + name_text = product_data.get("name", "No Name") + name_label = ctk.CTkLabel(card, text=name_text, font=("Helvetica", 11, "bold"), wraplength=120, anchor="center", justify="center") + name_label.pack(padx=5, pady=3) + + price_val = product_data.get("price", 0.0) + price_label = ctk.CTkLabel(card, text=f"â‚« {price_val:,.1f}", font=("Helvetica", 10, "bold"), text_color="#ff4242") + price_label.pack(side="bottom", pady=5) + + return card + 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() @@ -209,64 +228,37 @@ def dashboard_frame(parent, switch_func, API_URL, token): 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) + grid_frame = ctk.CTkFrame(container, fg_color="transparent") + grid_frame.pack(fill="both", expand=True) - # 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") + columns = 7 # 7 products per row + for idx, product in enumerate(products): + row = idx // columns + col = idx % columns + product_card = create_product_card(grid_frame, product) + product_card.grid(row=row, column=col, padx=5, pady=5) 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.") + messagebox.showerror("Error", f"Failed to fetch featured shops. Status: {resp.status_code}") 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) + display_products(products[:20], bottom_products_frame) else: - messagebox.showerror("Error", "Failed to fetch recommended products.") + messagebox.showerror("Error", f"Failed to fetch recommended products. Status: {resp.status_code}") except Exception as e: messagebox.showerror("Error", f"Request error: {e}") -- GitLab