From 3c7a82827a40b106e355c088147d67f580d0a254 Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Mon, 31 Mar 2025 21:44:38 +0700 Subject: [PATCH] update front end for owner --- .../components/product/create_product.py | 125 +++++++----- app/frontend/components/shop/create_shop.py | 134 +++++++------ app/frontend/components/shop/view_shop.py | 182 +++++++---------- app/frontend/components/user_details.py | 187 ++++-------------- app/frontend/main.py | 40 ++-- 5 files changed, 286 insertions(+), 382 deletions(-) diff --git a/app/frontend/components/product/create_product.py b/app/frontend/components/product/create_product.py index b95400c..f34f6cf 100644 --- a/app/frontend/components/product/create_product.py +++ b/app/frontend/components/product/create_product.py @@ -1,69 +1,92 @@ import customtkinter as ctk +from tkinter import filedialog, messagebox import requests -from tkinter import messagebox +import os - -def product_frame(parent, switch_func, API_URL, token): +def create_product_frame(parent, switch_func, API_URL, token): + """ + Frame to create a new product. + Fields: Title, Price, Description, Stock, and Product Image. + 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.pack(pady=10) - ctk.CTkLabel(frame, text="Product Management", font=("Helvetica", 18, "bold")).pack( - pady=10 - ) - - ctk.CTkLabel(frame, text="Product Name:").pack(pady=5) - entry_name = ctk.CTkEntry(frame) - entry_name.pack(pady=5) + # Product Title + prod_title_label = ctk.CTkLabel(frame, text="Product Title:") + prod_title_label.pack(pady=5) + prod_title_entry = ctk.CTkEntry(frame, width=300) + prod_title_entry.pack(pady=5) - ctk.CTkLabel(frame, text="Price:").pack(pady=5) - entry_price = ctk.CTkEntry(frame) - entry_price.pack(pady=5) + # Price + price_label = ctk.CTkLabel(frame, text="Price:") + price_label.pack(pady=5) + price_entry = ctk.CTkEntry(frame, width=300) + price_entry.pack(pady=5) - ctk.CTkLabel(frame, text="Stock:").pack(pady=5) - entry_stock = ctk.CTkEntry(frame) - entry_stock.pack(pady=5) + # Description + desc_label = ctk.CTkLabel(frame, text="Description:") + desc_label.pack(pady=5) + desc_entry = ctk.CTkEntry(frame, width=300) + desc_entry.pack(pady=5) - def create_product(): - name = entry_name.get().strip() - price = entry_price.get().strip() - stock = entry_stock.get().strip() + # Stock Quantity + stock_label = ctk.CTkLabel(frame, text="Stock Quantity:") + stock_label.pack(pady=5) + stock_entry = ctk.CTkEntry(frame, width=300) + stock_entry.pack(pady=5) - if not name or not price or not stock: - messagebox.showwarning("Input Error", "All fields are required!") - return + # 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")] + ) + 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) + image_label.pack(pady=5) - try: - price = float(price) - stock = int(stock) - except ValueError: - messagebox.showwarning( - "Input Error", "Price must be a number and Stock must be an integer." - ) + def submit_product(): + title_val = prod_title_entry.get().strip() + price_val = price_entry.get().strip() + desc_val = desc_entry.get().strip() + stock_val = stock_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 + # In a real scenario, you would pass the shop_id from view_shop. + # For now, use a placeholder shop_id (e.g., 1) + data = { + "name": title_val, + "price": price_val, + "description": desc_val, + "stock": stock_val, + "shop_id": 1 # Replace with the actual shop id + } + files = {} + if os.path.isfile(image_path.get()): + files["images"] = open(image_path.get(), "rb") headers = {"Authorization": f"Bearer {token}"} - data = {"name": name, "price": price, "stock": stock} - try: - response = requests.post(f"{API_URL}/products", json=data, headers=headers) - - if response.status_code == 200: - messagebox.showinfo( - "Success", f"Product '{name}' created successfully!" - ) - entry_name.delete(0, "end") - entry_price.delete(0, "end") - entry_stock.delete(0, "end") + 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 else: - error_message = response.json().get( - "detail", "Failed to create product" - ) - messagebox.showerror("Error", error_message) - except requests.exceptions.RequestException as e: - messagebox.showerror("Error", f"Failed to connect to server: {e}") + 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: + if files: + files["images"].close() - ctk.CTkButton(frame, text="Create Product", command=create_product).pack(pady=15) - ctk.CTkButton(frame, text="Back", command=lambda: switch_func("view_shop")).pack( - pady=5 - ) + submit_button = ctk.CTkButton(frame, text="Create Product", command=submit_product) + submit_button.pack(pady=10) return frame diff --git a/app/frontend/components/shop/create_shop.py b/app/frontend/components/shop/create_shop.py index e73fb5b..32cbf0d 100644 --- a/app/frontend/components/shop/create_shop.py +++ b/app/frontend/components/shop/create_shop.py @@ -1,71 +1,91 @@ import customtkinter as ctk -from tkinter import messagebox, filedialog +from tkinter import filedialog, messagebox +import requests import os -from utils.api_requests import create_shop_api # Import API function - def create_shop_frame(parent, switch_func, API_URL, token): + """ + Frame for a user to create a shop. + This version stores the token in the frame and provides an update_token method. + """ frame = ctk.CTkFrame(parent) - frame.access_token = token + + # Store the initial token in the frame + frame.token = token + # Define an update_token method so the global token can be propagated. def update_token(new_token): - frame.access_token = new_token - + frame.token = new_token frame.update_token = update_token - selected_file_path = [None] - - def select_file(): - file_path = filedialog.askopenfilename(title="Select Shop Image") - if file_path: - selected_file_path[0] = file_path - file_label.configure(text=os.path.basename(file_path)) - else: - selected_file_path[0] = None - file_label.configure(text="No file selected") - - def create_shop(): - name = entry_name.get() - description = entry_description.get() - - if not name: - messagebox.showwarning("Input Error", "Shop name is required!") - return - - # Call the API function from shop_api.py - status_code, response_data = create_shop_api( - name, description, selected_file_path[0], API_URL, frame.access_token + title = ctk.CTkLabel(frame, text="Create Your Shop", font=("Helvetica", 18, "bold")) + title.pack(pady=10) + + # Shop Name Field + shop_name_label = ctk.CTkLabel(frame, text="Shop Name:") + shop_name_label.pack(pady=5) + shop_name_entry = ctk.CTkEntry(frame, width=300) + shop_name_entry.pack(pady=5) + + # Address Field (using a default for demonstration) + address_label = ctk.CTkLabel(frame, text="Address:") + address_label.pack(pady=5) + address_entry = ctk.CTkEntry(frame, width=300) + address_entry.insert(0, "123 Main St") + address_entry.pack(pady=5) + + # Shop Image Upload + image_path = ctk.StringVar(value="No file selected") + def browse_image(): + filename = filedialog.askopenfilename( + title="Select Shop 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) + image_label.pack(pady=5) + + def submit_shop(): + # Use the token stored in the frame + current_token = frame.token + if not current_token: + messagebox.showerror("Error", "No access token found. Please log in again.") + return - if status_code == 200: - messagebox.showinfo("Shop Created", f"Shop '{name}' created successfully!") - else: - 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="Shop Name:").pack(pady=5) - entry_name = ctk.CTkEntry(frame, placeholder_text="Enter shop name") - entry_name.pack(pady=5) - - ctk.CTkLabel(frame, text="Description:").pack(pady=5) - entry_description = ctk.CTkEntry(frame, placeholder_text="Enter shop description") - entry_description.pack(pady=5) - - ctk.CTkButton(frame, text="Select Image", command=select_file).pack(pady=5) - 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) + name = shop_name_entry.get().strip() + address = address_entry.get().strip() + if not name or not address: + messagebox.showerror("Error", "Please fill all fields.") + return - ctk.CTkButton( - frame, text="Back", fg_color="transparent", command=lambda: switch_func("login") - ).pack(pady=5) + data = { + "name": name, + "address": address, + "description": "Default shop description" + } + files = {} + if os.path.isfile(image_path.get()): + files["file"] = open(image_path.get(), "rb") + headers = {"Authorization": f"Bearer {current_token}"} + print("Creating shop with token:", current_token) + try: + resp = requests.post(f"{API_URL}/shops/create", data=data, files=files, headers=headers) + if resp.status_code == 200: + shop = resp.json() + messagebox.showinfo("Success", "Shop created successfully!") + switch_func("view_shop", shop) # Pass the shop data to the view shop frame + else: + messagebox.showerror("Error", f"Failed to create shop. Status: {resp.status_code}\n{resp.text}") + except Exception as e: + messagebox.showerror("Error", f"Request error: {e}") + finally: + if files: + files["file"].close() + + submit_button = ctk.CTkButton(frame, text="Create Shop", command=submit_shop) + submit_button.pack(pady=10) return frame diff --git a/app/frontend/components/shop/view_shop.py b/app/frontend/components/shop/view_shop.py index 3c4ae23..8840001 100644 --- a/app/frontend/components/shop/view_shop.py +++ b/app/frontend/components/shop/view_shop.py @@ -1,119 +1,75 @@ import customtkinter as ctk +import requests +from PIL import Image, ImageTk from tkinter import messagebox -from PIL import ImageTk -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): +import io + +def view_shop_frame(parent, switch_func, shop_data, token, API_URL): + """ + Frame to display the owner’s shop. + If shop_data is None, display a message that no shop has been created. + Otherwise, display the shop's details and its products. + """ frame = ctk.CTkFrame(parent) - # Title - title_label = ctk.CTkLabel(frame, text="Your Shop", font=("Helvetica", 18, "bold")) - title_label.pack(pady=10) - - # Shop Details - 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.pack(pady=5) - - shop_image_label = ctk.CTkLabel(frame, text="") # Placeholder for shop image - shop_image_label.pack(pady=10) - - # Product List Section - product_list_frame = ctk.CTkFrame(frame) - product_list_frame.pack(fill="both", expand=True, padx=10, pady=10) - - def fetch_and_display_shop(): - """Fetch shop details and update UI""" - shop_data = fetch_shop_details(API_URL, token) - - if "error" in shop_data: - messagebox.showerror("Error", shop_data["error"]) - 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')}" - ) - - if "image_url" in shop_data and shop_data["image_url"]: - image = load_image_from_url(shop_data["image_url"]) - if image: - img_tk = ImageTk.PhotoImage(image) - shop_image_label.configure(image=img_tk, text="") - shop_image_label.image = img_tk # Prevent garbage collection - - fetch_and_display_products(shop_data["id"]) - - def fetch_and_display_products(shop_id): - """Fetch products and display them in the UI""" - products = fetch_shop_products(API_URL, token, shop_id) - - if "error" in products: - messagebox.showerror("Error", products["error"]) - return - - for widget in product_list_frame.winfo_children(): - widget.destroy() - - if not products: - ctk.CTkLabel( - product_list_frame, text="No products found.", font=("Helvetica", 12) - ).pack(pady=10) - return - - for product in products: - product_frame = ctk.CTkFrame(product_list_frame) - product_frame.pack(fill="x", padx=5, pady=5) - - # Product Image - img_label = ctk.CTkLabel(product_frame, text="") # Placeholder - img_label.pack(side="left", padx=5) - - if "images" in product and product["images"]: - image_url = product["images"][0]["image_url"] - image = load_image_from_url(image_url, size=(50, 50)) - if image: - img_tk = ImageTk.PhotoImage(image) - img_label.configure(image=img_tk, text="") - img_label.image = img_tk # Prevent garbage collection - - # Product Details - 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") - - # Refresh Button - 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.pack(pady=10) - - # Fetch shop data on load - fetch_and_display_shop() + # If shop_data is None, show a message and a button to create a shop. + if shop_data is None: + msg = ctk.CTkLabel(frame, text="No shop created yet.", font=("Helvetica", 16, "bold")) + msg.pack(pady=20) + create_button = ctk.CTkButton(frame, text="Create Shop", command=lambda: switch_func("create_shop")) + create_button.pack(pady=10) + return frame + + # Otherwise, proceed normally. + shop_name = shop_data.get("name", "No Name") + shop_image_url = shop_data.get("image_url", "") + + # Header: Shop Image and Name + header_frame = ctk.CTkFrame(frame) + header_frame.pack(pady=10, fill="x") + + image_label = ctk.CTkLabel(header_frame, text="") + image_label.pack(side="left", padx=10) + try: + resp = requests.get(shop_image_url) + if resp.status_code == 200: + pil_img = Image.open(io.BytesIO(resp.content)).resize((80, 80)) + tk_img = ImageTk.PhotoImage(pil_img) + image_label.configure(image=tk_img, text="") + image_label.image = tk_img + except Exception as e: + print("Error loading shop image:", e) + name_label = ctk.CTkLabel(header_frame, text=shop_name, font=("Helvetica", 16, "bold")) + name_label.pack(side="left", padx=10) + + # Button to create a new product + def goto_create_product(): + switch_func("create_product", shop_data) + create_prod_button = ctk.CTkButton(frame, text="Add New Product", command=goto_create_product) + create_prod_button.pack(pady=10) + + # Products list + products_frame = ctk.CTkScrollableFrame(frame, width=750, height=300) + products_frame.pack(padx=10, pady=10, fill="both", expand=True) + + headers = {"Authorization": f"Bearer {token}"} + try: + resp = requests.get(f"{API_URL}/product/list?shop_id={shop_data.get('id')}", headers=headers) + if resp.status_code == 200: + products = resp.json() + if products: + for product in products: + prod_frame = ctk.CTkFrame(products_frame, fg_color="#2b2b2b", corner_radius=5) + prod_frame.pack(padx=5, pady=5, fill="x") + prod_name = product.get("name", "No Name") + prod_label = ctk.CTkLabel(prod_frame, text=prod_name, font=("Helvetica", 12, "bold")) + prod_label.pack(side="left", padx=10) + # Optionally, add more product details here + else: + ctk.CTkLabel(products_frame, text="No products found.").pack(pady=10) + else: + messagebox.showerror("Error", f"Failed to fetch products. Status: {resp.status_code}") + except Exception as e: + messagebox.showerror("Error", f"Request error: {e}") return frame diff --git a/app/frontend/components/user_details.py b/app/frontend/components/user_details.py index a6b1a8f..efa7fd2 100644 --- a/app/frontend/components/user_details.py +++ b/app/frontend/components/user_details.py @@ -8,168 +8,102 @@ 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) + User details page with a "Become Shop Owner" button. + When clicked, it calls switch_func("create_shop") to navigate to shop creation. """ - - # Main container frame (transparent background, like dashboard) + # Main container frame frame = ctk.CTkFrame(parent, fg_color="transparent") - # ----------------- TOP BAR ----------------- + # --- 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) + 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) ----------------- + # --- MAIN SECTION: Sidebar + Content --- main_section = ctk.CTkFrame(frame, fg_color="transparent") main_section.pack(fill="both", expand=True) - # ----------------- LEFT SIDEBAR ----------------- - + # 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", # Active/selected state - 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.pack(fill="x", padx=10, pady=5) - # ----------------- RIGHT CONTENT (User Details) ----------------- + # 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 / 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)) - + 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)) - + 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)) - + 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)) - + 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)) - + 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_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.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.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(): - """ - 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(), @@ -187,67 +121,35 @@ 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, # #00c1ff - 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.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(): - """ - 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 = 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) @@ -264,8 +166,6 @@ def user_details_frame(parent, switch_func, API_URL, token): 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: @@ -281,7 +181,6 @@ 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/main.py b/app/frontend/main.py index 65b8f0a..6a77c15 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -3,26 +3,35 @@ from components.auth.login import login_frame from components.auth.register import register_frame 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.product.create_product import create_product_frame from components.admin.category import category_frame 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 +access_token = None # Global token - -def switch_frame(frame_name, token=None): +def switch_frame(frame_name, *args): + """ + Switch between frames. + Optionally, additional data (like shop_data) can be passed as extra arguments. + """ global access_token - if token: - access_token = token + if args and args[0] and isinstance(args[0], str): + access_token = args[0] + + frame = frames.get(frame_name) + if frame is None: + print(f"Frame {frame_name} not found!") + return - frame = frames.get(frame_name, login) if hasattr(frame, "update_token"): frame.update_token(access_token) - frame.tkraise() + if hasattr(frame, "refresh_data") and len(args) > 0: + frame.refresh_data(*args) + frame.tkraise() ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") @@ -31,12 +40,13 @@ root = ctk.CTk() root.title("Shopping App") root.geometry("1000x800") -# Create Frames +# 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) -view_shop = view_shop_frame(root, switch_frame, API_URL, access_token) -product = product_frame(root, switch_frame, API_URL, access_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) 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) @@ -55,11 +65,7 @@ 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) -# Show the login frame first switch_frame("login") - root.mainloop() -- GitLab