diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index 31d2560c45f5d7f2890cbcdc6d35b9c98eed19a4..ac8cbb3b8c31d7c6adc3b410d47b6c913ab46f4b 100644 --- a/app/frontend/components/auth/login.py +++ b/app/frontend/components/auth/login.py @@ -41,14 +41,16 @@ def login_frame(parent, switch_func, API_URL): messagebox.showinfo("Login Successful", f"Welcome back, {email}!") switch_func("dashboard", access_token) else: - messagebox.showerror("Login Failed", response_data.get("detail", "Invalid credentials")) + messagebox.showerror( + "Login Failed", response_data.get("detail", "Invalid credentials") + ) # Title ctk.CTkLabel( right_frame, text="Login", font=ctk.CTkFont("Helvetica", size=26, weight="bold"), - text_color="#111827" + text_color="#111827", ).pack(pady=(20, 10)) # Email @@ -65,7 +67,11 @@ def login_frame(parent, switch_func, API_URL): 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" + right_frame, + height=40, + show="*", + corner_radius=10, + placeholder_text="Enter your password", ) entry_password.pack(pady=5, padx=20, fill="x") @@ -78,7 +84,7 @@ def login_frame(parent, switch_func, API_URL): height=45, font=("Helvetica", 14), fg_color="#2563EB", - hover_color="#1E40AF" + hover_color="#1E40AF", ).pack(pady=20, padx=20, fill="x") # Register Redirect @@ -88,7 +94,7 @@ def login_frame(parent, switch_func, API_URL): command=lambda: switch_func("register"), fg_color="transparent", hover_color="#E5E7EB", - text_color="#2563EB" + 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 d2a635345377808e657efeeb110b660141a209e0..5f92659e2aa996ee57d89f343d2aa4d30cc76d69 100644 --- a/app/frontend/components/auth/register.py +++ b/app/frontend/components/auth/register.py @@ -7,10 +7,12 @@ def register_frame(parent, switch_func, API_URL): # 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 = 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(): @@ -20,7 +22,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 @@ -37,19 +45,25 @@ 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")) + 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" + 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 + register_panel, + placeholder_text="Username", + height=40, + corner_radius=10, + width=250, ) entry_username.pack(pady=10, padx=20, fill="x") @@ -61,19 +75,33 @@ def register_frame(parent, switch_func, API_URL): # Phone Number Entry entry_phone = ctk.CTkEntry( - register_panel, placeholder_text="Phone Number", height=40, corner_radius=10, width=250 + 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 + 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 + register_panel, + placeholder_text="Confirm Password", + show="*", + height=40, + corner_radius=10, + width=250, ) entry_confirm_password.pack(pady=10, padx=20, fill="x") @@ -86,7 +114,7 @@ def register_frame(parent, switch_func, API_URL): height=45, font=("Helvetica", 14), fg_color="#2563EB", - hover_color="#1E40AF" + hover_color="#1E40AF", ).pack(pady=20, padx=20, fill="x") # Link to Login Page @@ -96,7 +124,7 @@ def register_frame(parent, switch_func, API_URL): command=lambda: switch_func("login"), fg_color="transparent", hover_color="#E5E7EB", - text_color="#2563EB" + text_color="#2563EB", ).pack(pady=(5, 20)) return container diff --git a/app/frontend/components/dashboard.py b/app/frontend/components/dashboard.py index f5707c926cb655e12186dcfcccba5ebafc3a82b6..e2b0f96e17c145f1313d5208dd4aab75c5b679c6 100644 --- a/app/frontend/components/dashboard.py +++ b/app/frontend/components/dashboard.py @@ -7,6 +7,7 @@ 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. @@ -17,10 +18,11 @@ def fix_url(url): # If the URL starts with "app/static/", remove that part. prefix = "app/static/" if url.startswith(prefix): - url = url[len(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 dashboard UI: @@ -58,12 +60,16 @@ def dashboard_frame(parent, switch_func, API_URL, token): try: headers = {"Authorization": f"Bearer {token}"} # Adjust endpoint/params as needed - resp = requests.get(f"{API_URL}/search?name={keyword}&search_type=products", headers=headers) + 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", f"Search failed. Status code: {resp.status_code}") + messagebox.showerror( + "Error", f"Search failed. Status code: {resp.status_code}" + ) except Exception as e: messagebox.showerror("Error", f"Request error: {e}") @@ -119,7 +125,9 @@ def dashboard_frame(parent, switch_func, API_URL, token): """ 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 = ctk.CTkFrame( + parent, corner_radius=5, fg_color="#2b2b2b", width=100, height=130 + ) card.pack_propagate(False) image_label = ctk.CTkLabel(card, text="") @@ -139,14 +147,18 @@ def dashboard_frame(parent, switch_func, API_URL, token): 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}") + 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 = ctk.CTkLabel( + card, text=name_text, font=("Helvetica", 11, "bold"), wraplength=90 + ) name_label.pack() return card @@ -183,7 +195,13 @@ def dashboard_frame(parent, switch_func, API_URL, token): 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 = 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="") @@ -204,18 +222,32 @@ def dashboard_frame(parent, switch_func, API_URL, token): 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}") + 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 = 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 = ctk.CTkLabel( + card, + text=f"₫ {price_val:,.1f}", + font=("Helvetica", 10, "bold"), + text_color="#ff4242", + ) price_label.pack(side="bottom", pady=5) return card @@ -246,7 +278,10 @@ def dashboard_frame(parent, switch_func, API_URL, token): shops = resp.json() display_shops(shops[:5], top_shops_frame) else: - messagebox.showerror("Error", f"Failed to fetch featured shops. Status: {resp.status_code}") + messagebox.showerror( + "Error", + f"Failed to fetch featured shops. Status: {resp.status_code}", + ) except Exception as e: messagebox.showerror("Error", f"Request error: {e}") @@ -258,7 +293,10 @@ def dashboard_frame(parent, switch_func, API_URL, token): products = resp.json() display_products(products[:20], bottom_products_frame) else: - messagebox.showerror("Error", f"Failed to fetch recommended products. Status: {resp.status_code}") + messagebox.showerror( + "Error", + f"Failed to fetch recommended products. Status: {resp.status_code}", + ) except Exception as e: messagebox.showerror("Error", f"Request error: {e}") diff --git a/app/frontend/components/product/create_product.py b/app/frontend/components/product/create_product.py index 96f18b7b303d766745aa628252a7fb306e742841..97e6b2c6aceb1c84d893cbac8bd11955cf2d174c 100644 --- a/app/frontend/components/product/create_product.py +++ b/app/frontend/components/product/create_product.py @@ -3,6 +3,7 @@ from tkinter import filedialog, messagebox import requests import os + def create_product_frame(parent, switch_func, API_URL, token): """ Frame to create a new product. @@ -10,7 +11,9 @@ def create_product_frame(parent, switch_func, API_URL, token): On success, navigates back to the shop view. """ frame = ctk.CTkFrame(parent) - title = ctk.CTkLabel(frame, text="Create New Product", font=("Helvetica", 18, "bold")) + title = ctk.CTkLabel( + frame, text="Create New Product", font=("Helvetica", 18, "bold") + ) title.pack(pady=10) # Product Title @@ -39,13 +42,15 @@ def create_product_frame(parent, switch_func, API_URL, token): # Product Image Upload image_path = ctk.StringVar(value="No file selected") + def browse_image(): filename = filedialog.askopenfilename( - title="Select Product Image", - filetypes=[("Image Files", "*.png;*.jpg;*.jpeg;*.gif")] + title="Select Product Image", + filetypes=[("Image Files", "*.png;*.jpg;*.jpeg;*.gif")], ) if filename: image_path.set(filename) + browse_button = ctk.CTkButton(frame, text="Browse Image", command=browse_image) browse_button.pack(pady=5) image_label = ctk.CTkLabel(frame, textvariable=image_path) @@ -56,7 +61,6 @@ def create_product_frame(parent, switch_func, API_URL, token): price_val = price_entry.get().strip() desc_val = desc_entry.get().strip() stock_val = stock_entry.get().strip() - # shop_val = shop_id_entry.get().strip() if not title_val or not price_val or not stock_val: messagebox.showerror("Error", "Title, Price, and Stock are required") return @@ -66,19 +70,23 @@ def create_product_frame(parent, switch_func, API_URL, token): "price": price_val, "description": desc_val, "stock": stock_val, - # "shop_id": shop_val, } files = {} if os.path.isfile(image_path.get()): files["images"] = open(image_path.get(), "rb") headers = {"Authorization": f"Bearer {token}"} try: - resp = requests.post(f"{API_URL}/product/create", data=data, files=files, headers=headers) + resp = requests.post( + f"{API_URL}/product/create", data=data, files=files, headers=headers + ) if resp.status_code == 200: messagebox.showinfo("Success", "Product created successfully!") - switch_func("view_shop") # You might pass shop data if needed + switch_func("view_shop") else: - messagebox.showerror("Error", f"Failed to create product. Status: {resp.status_code}\n{resp.text}") + messagebox.showerror( + "Error", + f"Failed to create product. Status: {resp.status_code}\n{resp.text}", + ) except Exception as e: messagebox.showerror("Error", f"Request error: {e}") finally: diff --git a/app/frontend/components/user_details.py b/app/frontend/components/user_details.py index efa7fd24f80db45a54dc372684b53b4943706152..c22431a47b90c7d4435a97e42a29da7c4f35d747 100644 --- a/app/frontend/components/user_details.py +++ b/app/frontend/components/user_details.py @@ -6,6 +6,7 @@ import io SHOPPING = "#00c1ff" + def user_details_frame(parent, switch_func, API_URL, token): """ User details page with a "Become Shop Owner" button. @@ -18,13 +19,23 @@ def user_details_frame(parent, switch_func, API_URL, token): 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 = ctk.CTkLabel( + top_bar, text="My Profile", text_color="white", font=("Helvetica", 16, "bold") + ) top_label.pack(side="left", padx=21) 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 = 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 --- @@ -34,74 +45,136 @@ def user_details_frame(parent, switch_func, API_URL, token): # 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 = 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 = 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", - text_color="white", hover_color="#3b3b3b", state="disabled") + nav_profile = ctk.CTkButton( + sidebar_frame, + text="My Profile", + fg_color="#3b3b3b", + 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 = 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) # NEW: Become Shop Owner button - become_owner = ctk.CTkButton(sidebar_frame, text="Become Shop Owner", fg_color="#2b2b2b", - text_color="white", hover_color="#3b3b3b", command=lambda: switch_func("create_shop")) + become_owner = ctk.CTkButton( + sidebar_frame, + text="Become Shop Owner", + fg_color="#2b2b2b", + text_color="white", + hover_color="#3b3b3b", + command=lambda: switch_func("create_shop"), + ) become_owner.pack(fill="x", padx=10, pady=5) # RIGHT CONTENT (User Details Form) content_frame = ctk.CTkFrame(main_section, fg_color="transparent") content_frame.pack(side="left", fill="both", expand=True, padx=20, pady=20) - 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)) + 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)) 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)) + 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 = 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)) + 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 = 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)) + 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 = 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)) + 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 = 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)) + 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_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") 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 = 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_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)) + birthday_entry.pack(anchor="w", pady=(0, 10)) + # Save Button def save_profile(): headers = {"Authorization": f"Bearer {token}"} @@ -111,7 +184,7 @@ def user_details_frame(parent, switch_func, API_URL, token): "email": email_entry.get().strip(), "phone": phone_entry.get().strip(), "gender": gender_var.get(), - "birthday": birthday_entry.get().strip() + "birthday": birthday_entry.get().strip(), } try: resp = requests.put(f"{API_URL}/user/update", headers=headers, json=payload) @@ -121,21 +194,34 @@ def user_details_frame(parent, switch_func, API_URL, token): 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, text_color="white", - command=save_profile, width=80) - save_button.pack(anchor="w", pady=(20,10)) + + save_button = ctk.CTkButton( + form_frame, + text="Save", + fg_color=SHOPPING, + 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)) + 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)) + photo_label.pack(anchor="w", pady=(0, 10)) + def choose_photo(): file_path = filedialog.askopenfilename( title="Choose a Profile Picture", - filetypes=[("Image Files", "*.png *.jpg *.jpeg *.gif")] + filetypes=[("Image Files", "*.png *.jpg *.jpeg *.gif")], ) if file_path: try: @@ -146,9 +232,17 @@ def user_details_frame(parent, switch_func, API_URL, token): 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 = ctk.CTkButton( + pic_frame, + text="Choose Image", + fg_color="white", + text_color="black", + command=choose_photo, + width=80, + ) change_photo_button.pack(anchor="w") + def fetch_user_info(): headers = {"Authorization": f"Bearer {token}"} try: @@ -171,7 +265,9 @@ def user_details_frame(parent, switch_func, API_URL, token): 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)) + 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 @@ -181,6 +277,7 @@ def user_details_frame(parent, switch_func, API_URL, token): 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 index b71b34fc7f27dd6ebfcf97eb4b3b9ad1210b57a2..6d72df851bcb366e6612de84d69bd8ae43c48609 100644 --- a/app/frontend/components/user_orders.py +++ b/app/frontend/components/user_orders.py @@ -6,9 +6,10 @@ 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 + 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. """ @@ -20,10 +21,7 @@ def user_orders_frame(parent, switch_func, API_URL, token): top_bar.pack(fill="x", side="top") top_label = ctk.CTkLabel( - top_bar, - text="My Orders", - text_color="white", - font=("Helvetica", 16, "bold") + top_bar, text="My Orders", text_color="white", font=("Helvetica", 16, "bold") ) top_label.pack(side="left", padx=20) @@ -37,7 +35,7 @@ def user_orders_frame(parent, switch_func, API_URL, token): text_color="black", command=go_back, width=60, - height=30 + height=30, ) back_button.pack(side="right", padx=20, pady=5) @@ -50,10 +48,7 @@ def user_orders_frame(parent, switch_func, API_URL, token): sidebar_frame.pack(side="left", fill="y") sidebar_title = ctk.CTkLabel( - sidebar_frame, - text="Menu", - font=("Helvetica", 14, "bold"), - text_color="white" + sidebar_frame, text="Menu", font=("Helvetica", 14, "bold"), text_color="white" ) sidebar_title.pack(pady=(10, 5)) @@ -66,7 +61,7 @@ def user_orders_frame(parent, switch_func, API_URL, token): fg_color="#2b2b2b", text_color="white", hover_color="#3b3b3b", - command=go_back + command=go_back, ) nav_dashboard.pack(fill="x", padx=10, pady=5) @@ -76,7 +71,7 @@ def user_orders_frame(parent, switch_func, API_URL, token): fg_color="#2b2b2b", text_color="white", hover_color="#3b3b3b", - command=open_profile + command=open_profile, ) nav_profile.pack(fill="x", padx=10, pady=5) @@ -86,7 +81,7 @@ def user_orders_frame(parent, switch_func, API_URL, token): fg_color="#3b3b3b", # Active/selected state text_color="white", hover_color="#3b3b3b", - state="disabled" + state="disabled", ) nav_orders.pack(fill="x", padx=10, pady=5) @@ -98,7 +93,7 @@ def user_orders_frame(parent, switch_func, API_URL, token): content_frame, text="Your Orders", font=("Helvetica", 18, "bold"), - text_color="white" + text_color="white", ) main_title_label.pack(anchor="w", pady=(0, 5)) @@ -106,7 +101,7 @@ def user_orders_frame(parent, switch_func, API_URL, token): content_frame, text="Review the products you have purchased.", font=("Helvetica", 12), - text_color="#cccccc" + text_color="#cccccc", ) subtitle_label.pack(anchor="w", pady=(0, 15)) @@ -139,7 +134,9 @@ def user_orders_frame(parent, switch_func, API_URL, token): widget.destroy() if not orders: - ctk.CTkLabel(container, text="No orders found.", text_color="white").pack(pady=10) + ctk.CTkLabel(container, text="No orders found.", text_color="white").pack( + pady=10 + ) return for order in orders: @@ -158,7 +155,9 @@ def user_orders_frame(parent, switch_func, API_URL, token): 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)) + 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 @@ -174,25 +173,23 @@ def user_orders_frame(parent, switch_func, API_URL, token): info_frame, text=product.get("name", "No Name"), font=("Helvetica", 13, "bold"), - text_color="white" + 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" + 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" + 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 = 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): @@ -201,7 +198,9 @@ def user_orders_frame(parent, switch_func, API_URL, token): 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')}") + label_widget.configure( + text=f"Shop: {shop_data.get('name', 'No Shop Name')}" + ) else: label_widget.configure(text="Shop: Not found") except Exception: @@ -211,14 +210,16 @@ def user_orders_frame(parent, switch_func, API_URL, token): # "View Order" button (placeholder action) def view_order(): - messagebox.showinfo("Order Details", f"View details for order of {product.get('name')}") + 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 + command=view_order, ) view_button.pack(anchor="e", pady=(5, 0)) diff --git a/app/frontend/main.py b/app/frontend/main.py index 6a77c154fb29289f068ce83d1ee40b72d01665f5..145c15102b577eba765ce62f922e75b769dff6be 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -12,6 +12,7 @@ from components.user_orders import user_orders_frame API_URL = "http://127.0.0.1:8000" access_token = None # Global token + def switch_frame(frame_name, *args): """ Switch between frames. @@ -33,6 +34,7 @@ def switch_frame(frame_name, *args): frame.tkraise() + ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") @@ -43,7 +45,9 @@ root.geometry("1000x800") # Create Frames with the token parameter where needed. login = login_frame(root, switch_frame, API_URL) register = register_frame(root, switch_frame, API_URL) -create_shop = create_shop_frame(root, switch_frame, API_URL, access_token) # Accepts token +create_shop = create_shop_frame( + root, switch_frame, API_URL, access_token +) # Accepts token # Pass a placeholder (None) for shop_data in view_shop_frame view_shop = view_shop_frame(root, switch_frame, None, access_token, API_URL) product = create_product_frame(root, switch_frame, API_URL, access_token) diff --git a/app/frontend/utils/api_requests.py b/app/frontend/utils/api_requests.py index d17146c43f5b66338f8acb17aa47c28740b969c0..59aa0806c59531d6ce2b760712be7a3adec561b5 100644 --- a/app/frontend/utils/api_requests.py +++ b/app/frontend/utils/api_requests.py @@ -88,13 +88,13 @@ def create_shop_api(name, description, file_path, api_url, access_token): # View Shop -def fetch_shop_details(api_url, token, shop_id=1): +def fetch_shop_details(api_url, token, shop_id): """ Fetches details of the shop owned by the logged-in user. :param api_url: Base API URL :param token: Authorization token - :param shop_id: ID of the shop (default = 1) + :param shop_id: ID of the shop :return: Dictionary containing shop details or error message """ headers = {"Authorization": f"Bearer {token}"}