From 23919fa4b04cc87250c7d3694e8ed514c1ae8357 Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Sat, 15 Mar 2025 17:31:02 +0700 Subject: [PATCH 1/5] Saved changes before switching branches --- app/frontend/components/admin/category.py | 94 ++++++++++++++ .../components/{ => auth}/forgot_pass.py | 0 app/frontend/components/{ => auth}/login.py | 32 ++--- app/frontend/components/auth/register.py | 68 ++++++++++ .../components/product/create_product.py | 58 +++++++++ app/frontend/components/register.py | 90 ------------- app/frontend/components/shop/create_shop.py | 33 +++-- app/frontend/components/shop/view_shop.py | 119 ++++++++++++++++++ app/frontend/main.py | 47 ++++--- 9 files changed, 391 insertions(+), 150 deletions(-) create mode 100644 app/frontend/components/admin/category.py rename app/frontend/components/{ => auth}/forgot_pass.py (100%) rename app/frontend/components/{ => auth}/login.py (57%) create mode 100644 app/frontend/components/auth/register.py create mode 100644 app/frontend/components/product/create_product.py delete mode 100644 app/frontend/components/register.py create mode 100644 app/frontend/components/shop/view_shop.py diff --git a/app/frontend/components/admin/category.py b/app/frontend/components/admin/category.py new file mode 100644 index 0000000..c4d3225 --- /dev/null +++ b/app/frontend/components/admin/category.py @@ -0,0 +1,94 @@ +import customtkinter as ctk +import requests +from tkinter import messagebox + +def category_frame(parent, switch_func, API_URL, access_token): + frame = ctk.CTkFrame(parent) + + ctk.CTkLabel(frame, text="Category Management", font=("Helvetica", 18, "bold")).pack(pady=10) + + ctk.CTkLabel(frame, text="Category Name:").pack(pady=5) + entry_name = ctk.CTkEntry(frame) + entry_name.pack(pady=5) + + ctk.CTkLabel(frame, text="Category ID (for update/delete):").pack(pady=5) + entry_id = ctk.CTkEntry(frame) + entry_id.pack(pady=5) + + headers = {"Authorization": f"Bearer {access_token}"} if access_token else {} + + def create_category(): + name = entry_name.get().strip() + if not name: + messagebox.showwarning("Input Error", "Category name is required!") + return + + try: + response = requests.post(f"{API_URL}/category", data={"name": name}, headers=headers) + if response.status_code == 200: + messagebox.showinfo("Success", f"Category '{name}' created successfully!") + entry_name.delete(0, 'end') + else: + messagebox.showerror("Error", response.json().get("detail", "Failed to create category")) + except requests.exceptions.RequestException as e: + messagebox.showerror("Error", f"Failed to connect to server: {e}") + + def list_categories(): + try: + response = requests.get(f"{API_URL}/category", headers=headers) + if response.status_code == 200: + categories = response.json() + if categories: + category_list = "\n".join([f"{cat['id']}: {cat['name']}" for cat in categories]) + messagebox.showinfo("Categories", category_list) + else: + messagebox.showinfo("Categories", "No categories available.") + else: + messagebox.showerror("Error", "Failed to fetch categories") + except requests.exceptions.RequestException as e: + messagebox.showerror("Error", f"Failed to connect to server: {e}") + + def update_category(): + category_id = entry_id.get().strip() + name = entry_name.get().strip() + if not category_id.isdigit(): + messagebox.showwarning("Input Error", "Enter a valid Category ID!") + return + if not name: + messagebox.showwarning("Input Error", "Category name is required for update!") + return + + try: + response = requests.put(f"{API_URL}/category/{category_id}", data={"name": name}, headers=headers) + if response.status_code == 200: + messagebox.showinfo("Success", "Category updated successfully!") + entry_id.delete(0, 'end') + entry_name.delete(0, 'end') + else: + messagebox.showerror("Error", response.json().get("detail", "Failed to update category")) + except requests.exceptions.RequestException as e: + messagebox.showerror("Error", f"Failed to connect to server: {e}") + + def delete_category(): + category_id = entry_id.get().strip() + if not category_id.isdigit(): + messagebox.showwarning("Input Error", "Enter a valid Category ID!") + return + + try: + response = requests.delete(f"{API_URL}/category/{category_id}", headers=headers) + if response.status_code == 200: + messagebox.showinfo("Success", "Category deleted successfully!") + entry_id.delete(0, 'end') + else: + messagebox.showerror("Error", response.json().get("detail", "Failed to delete category")) + except requests.exceptions.RequestException as e: + messagebox.showerror("Error", f"Failed to connect to server: {e}") + + ctk.CTkButton(frame, text="Create Category", command=create_category).pack(pady=5) + ctk.CTkButton(frame, text="List Categories", command=list_categories).pack(pady=5) + ctk.CTkButton(frame, text="Update Category", command=update_category).pack(pady=5) + ctk.CTkButton(frame, text="Delete Category", command=delete_category).pack(pady=5) + ctk.CTkButton(frame, text="Back", command=lambda: switch_func("")).pack(pady=5) + + return frame diff --git a/app/frontend/components/forgot_pass.py b/app/frontend/components/auth/forgot_pass.py similarity index 100% rename from app/frontend/components/forgot_pass.py rename to app/frontend/components/auth/forgot_pass.py diff --git a/app/frontend/components/login.py b/app/frontend/components/auth/login.py similarity index 57% rename from app/frontend/components/login.py rename to app/frontend/components/auth/login.py index bedad7b..8f2b656 100644 --- a/app/frontend/components/login.py +++ b/app/frontend/components/auth/login.py @@ -1,17 +1,11 @@ -import ttkbootstrap as tb -import ttkbootstrap.constants +import customtkinter as ctk from tkinter import messagebox import requests -# Global variable to store the access token -access_token = None - - def login_frame(parent, switch_func, api_url): - frame = tb.Frame(parent) + frame = ctk.CTkFrame(parent) def login(): - global access_token email = entry_email.get() password = entry_password.get() @@ -27,34 +21,28 @@ def login_frame(parent, switch_func, api_url): response_data = response.json() if response.status_code == 200: access_token = response_data["access_token"] - print(f"Access Token: {access_token}") # Debugging line - messagebox.showinfo("Login Successful", f"Welcome back, {email}!") switch_func("create_shop", access_token) else: - messagebox.showerror( - "Login Failed", response_data.get("detail", "Invalid credentials") - ) + messagebox.showerror("Login Failed", response_data.get("detail", "Invalid credentials")) except requests.exceptions.JSONDecodeError: messagebox.showerror("Login Failed", "Server returned an invalid response.") - tb.Label(frame, text="Login", font=("Helvetica", 18, "bold")).pack(pady=10) + ctk.CTkLabel(frame, text="Login", font=("Helvetica", 18, "bold")).pack(pady=10) - tb.Label(frame, text="Email:").pack(pady=5) - entry_email = tb.Entry(frame, bootstyle="info") + ctk.CTkLabel(frame, text="Email:").pack(pady=5) + entry_email = ctk.CTkEntry(frame) entry_email.pack(pady=5) - tb.Label(frame, text="Password:").pack(pady=5) - entry_password = tb.Entry(frame, bootstyle="info", show="*") + ctk.CTkLabel(frame, text="Password:").pack(pady=5) + entry_password = ctk.CTkEntry(frame, show="*") entry_password.pack(pady=5) - btn_login = tb.Button(frame, text="Login", bootstyle="primary", command=login) - btn_login.pack(pady=15) + ctk.CTkButton(frame, text="Login", command=login).pack(pady=15) - tb.Button( + ctk.CTkButton( frame, text="Don't have an account? Register", - bootstyle="link", command=lambda: switch_func("register"), ).pack() diff --git a/app/frontend/components/auth/register.py b/app/frontend/components/auth/register.py new file mode 100644 index 0000000..3223abf --- /dev/null +++ b/app/frontend/components/auth/register.py @@ -0,0 +1,68 @@ +import customtkinter as ctk +from tkinter import messagebox +import requests + +def register_frame(parent, switch_func, api_url): + frame = ctk.CTkFrame(parent) + + def register(): + username = entry_username.get() + email = entry_email.get() + phone_number = entry_phone.get() + 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: + messagebox.showwarning("Input Error", "All fields are required!") + return + + if password != confirm_password: + messagebox.showerror("Password Error", "Passwords do not match!") + return + + response = requests.post( + f"{api_url}/auth/signup", + json={"username": username, "email": email, "phone_number": phone_number, "password": password}, + ) + + try: + response_data = response.json() + if response.status_code == 200: + messagebox.showinfo("Registration Successful", f"Welcome, {username}!") + switch_func("login") + else: + messagebox.showerror("Registration Failed", response_data.get("detail", "Unknown error")) + except requests.exceptions.JSONDecodeError: + messagebox.showerror("Registration Failed", "Server returned an invalid response.") + + 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) + + ctk.CTkButton( + frame, + text="Already have an account? Login", + command=lambda: switch_func("login"), + ).pack() + + return frame diff --git a/app/frontend/components/product/create_product.py b/app/frontend/components/product/create_product.py new file mode 100644 index 0000000..2fc5b7a --- /dev/null +++ b/app/frontend/components/product/create_product.py @@ -0,0 +1,58 @@ +import customtkinter as ctk +import requests +from tkinter import messagebox + +def product_frame(parent, switch_func, API_URL, token): + frame = ctk.CTkFrame(parent) + + 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) + + ctk.CTkLabel(frame, text="Price:").pack(pady=5) + entry_price = ctk.CTkEntry(frame) + entry_price.pack(pady=5) + + ctk.CTkLabel(frame, text="Stock:").pack(pady=5) + entry_stock = ctk.CTkEntry(frame) + entry_stock.pack(pady=5) + + def create_product(): + name = entry_name.get().strip() + price = entry_price.get().strip() + stock = entry_stock.get().strip() + + if not name or not price or not stock: + messagebox.showwarning("Input Error", "All fields are required!") + return + + try: + price = float(price) + stock = int(stock) + except ValueError: + messagebox.showwarning("Input Error", "Price must be a number and Stock must be an integer.") + return + + 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') + 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}") + + 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) + + return frame diff --git a/app/frontend/components/register.py b/app/frontend/components/register.py deleted file mode 100644 index b8aa074..0000000 --- a/app/frontend/components/register.py +++ /dev/null @@ -1,90 +0,0 @@ -import ttkbootstrap as tb -import ttkbootstrap.constants -from tkinter import messagebox -import requests # Import requests for API communication - - -def register_frame(parent, switch_func, api_url): # Added api_url parameter - frame = tb.Frame(parent) - - def register(): - username = entry_username.get() - email = entry_email.get() - phone_number = entry_phone.get() - 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 - ): - messagebox.showwarning("Input Error", "All fields are required!") - return - - if password != confirm_password: - messagebox.showerror("Password Error", "Passwords do not match!") - return - - # Sending registration data to backend - response = requests.post( - f"{api_url}/auth/signup", - json={ - "username": username, - "email": email, - "phone_number": phone_number, - "password": password, - }, - ) - - try: - response_data = response.json() - if response.status_code == 200: - messagebox.showinfo("Registration Successful", f"Welcome, {username}!") - switch_func("login") # Switch to login after successful registration - else: - messagebox.showerror( - "Registration Failed", response_data.get("detail", "Unknown error") - ) - except requests.exceptions.JSONDecodeError: - messagebox.showerror( - "Registration Failed", "Server returned an invalid response." - ) - - tb.Label(frame, text="Register", font=("Helvetica", 18, "bold")).pack(pady=10) - - tb.Label(frame, text="Username:").pack(pady=5) - entry_username = tb.Entry(frame, bootstyle="info") - entry_username.pack(pady=5) - - tb.Label(frame, text="Email:").pack(pady=5) - entry_email = tb.Entry(frame, bootstyle="info") - entry_email.pack(pady=5) - - tb.Label(frame, text="Phone Number:").pack(pady=5) - entry_phone = tb.Entry(frame, bootstyle="info") - entry_phone.pack(pady=5) - - tb.Label(frame, text="Password:").pack(pady=5) - entry_password = tb.Entry(frame, bootstyle="info", show="*") - entry_password.pack(pady=5) - - tb.Label(frame, text="Confirm Password:").pack(pady=5) - entry_confirm_password = tb.Entry(frame, bootstyle="info", show="*") - entry_confirm_password.pack(pady=5) - - btn_register = tb.Button( - frame, text="Register", bootstyle="success", command=register - ) - btn_register.pack(pady=15) - - tb.Button( - frame, - text="Already have an account? Login", - bootstyle="link", - command=lambda: switch_func("login"), - ).pack() - - return frame diff --git a/app/frontend/components/shop/create_shop.py b/app/frontend/components/shop/create_shop.py index b4f8186..14a44a3 100644 --- a/app/frontend/components/shop/create_shop.py +++ b/app/frontend/components/shop/create_shop.py @@ -1,12 +1,11 @@ -import ttkbootstrap as tb -import ttkbootstrap.constants +import customtkinter as ctk from tkinter import messagebox, filedialog import requests import os def create_shop_frame(parent, switch_func, api_url, token): - frame = tb.Frame(parent) + frame = ctk.CTkFrame(parent) selected_file_path = [None] @@ -14,10 +13,10 @@ def create_shop_frame(parent, switch_func, api_url, token): file_path = filedialog.askopenfilename(title="Select Shop Image") if file_path: selected_file_path[0] = file_path - file_label.config(text=os.path.basename(file_path)) + file_label.configure(text=os.path.basename(file_path)) else: selected_file_path[0] = None - file_label.config(text="No file selected") + file_label.configure(text="No file selected") def create_shop(): name = entry_name.get() @@ -64,30 +63,26 @@ def create_shop_frame(parent, switch_func, api_url, token): except Exception as e: messagebox.showerror("Request Error", str(e)) - tb.Label(frame, text="Create Shop", font=("Helvetica", 18, "bold")).pack(pady=10) + ctk.CTkLabel(frame, text="Create Shop", font=("Helvetica", 18, "bold")).pack(pady=10) - tb.Label(frame, text="Shop Name:").pack(pady=5) - entry_name = tb.Entry(frame, bootstyle="info") + ctk.CTkLabel(frame, text="Shop Name:").pack(pady=5) + entry_name = ctk.CTkEntry(frame, placeholder_text="Enter shop name") entry_name.pack(pady=5) - tb.Label(frame, text="Description:").pack(pady=5) - entry_description = tb.Entry(frame, bootstyle="info") + ctk.CTkLabel(frame, text="Description:").pack(pady=5) + entry_description = ctk.CTkEntry(frame, placeholder_text="Enter shop description") entry_description.pack(pady=5) - tb.Button( - frame, text="Select Image", bootstyle="primary", command=select_file - ).pack(pady=5) - file_label = tb.Label(frame, text="No file selected") + 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) - tb.Button(frame, text="Create Shop", bootstyle="success", command=create_shop).pack( - pady=15 - ) + ctk.CTkButton(frame, text="Create Shop", fg_color="green", command=create_shop).pack(pady=15) - tb.Button( + ctk.CTkButton( frame, text="Back", - bootstyle="link", + fg_color="transparent", command=lambda: switch_func("login"), ).pack(pady=5) diff --git a/app/frontend/components/shop/view_shop.py b/app/frontend/components/shop/view_shop.py new file mode 100644 index 0000000..960fec4 --- /dev/null +++ b/app/frontend/components/shop/view_shop.py @@ -0,0 +1,119 @@ +import customtkinter as ctk +import requests +from tkinter import messagebox +from PIL import Image, ImageTk +import io + +def view_shop_frame(parent, switch_func, api_url, token): + 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_shop_data(): + """ Fetch the shop created by the logged-in user """ + headers = {"Authorization": f"Bearer {token}"} + try: + response = requests.get(f"{api_url}/shops/my-shop", headers=headers) # Adjust the endpoint as needed + if response.status_code == 200: + shop_data = response.json() + shop_name_label.configure(text=f"Shop Name: {shop_data['name']}") + shop_description_label.configure(text=f"Description: {shop_data.get('description', 'No description')}") + + # Load and display shop image if available + if "image_url" in shop_data and shop_data["image_url"]: + try: + image_response = requests.get(shop_data["image_url"]) + image = Image.open(io.BytesIO(image_response.content)) + image = image.resize((150, 150)) + img_tk = ImageTk.PhotoImage(image) + + shop_image_label.configure(image=img_tk, text="") + shop_image_label.image = img_tk + except Exception: + pass + + fetch_products(shop_data["id"]) # Fetch products for this shop + else: + messagebox.showerror("Error", "Failed to fetch shop details.") + except Exception as e: + messagebox.showerror("Error", f"Request error: {e}") + + def fetch_products(shop_id): + """ Fetch products that belong to the user's shop """ + headers = {"Authorization": f"Bearer {token}"} + try: + response = requests.get(f"{api_url}/products?shop_id={shop_id}", headers=headers) + if response.status_code == 200: + products = response.json() + display_products(products) + else: + messagebox.showerror("Error", "Failed to fetch products.") + except Exception as e: + messagebox.showerror("Error", f"Request error: {e}") + + def display_products(products): + """ Display the list of products in the shop """ + 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) + + # Load and display product image + if "images" in product and product["images"]: + image_url = product["images"][0]["image_url"] + try: + image_response = requests.get(image_url) + image = Image.open(io.BytesIO(image_response.content)) + image = image.resize((50, 50)) + img_tk = ImageTk.PhotoImage(image) + + img_label.configure(image=img_tk, text="") + img_label.image = img_tk + except Exception: + pass + + # 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 Data Button + refresh_button = ctk.CTkButton(frame, text="Refresh", command=fetch_shop_data) + 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_shop_data() + + return frame diff --git a/app/frontend/main.py b/app/frontend/main.py index 938dbbb..cc8b927 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -1,7 +1,10 @@ -import ttkbootstrap as tb -from components.login import login_frame -from components.register import register_frame +import customtkinter as ctk +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.admin.category import category_frame # Backend API URL API_URL = "http://127.0.0.1:8000" @@ -9,25 +12,19 @@ API_URL = "http://127.0.0.1:8000" # Global variable to store the access token access_token = None - # Function to switch between frames def switch_frame(frame_name, token=None): global access_token if token: access_token = token - if frame_name == "login": - login.tkraise() - elif frame_name == "register": - register.tkraise() - elif frame_name == "create_shop": - create_shop = create_shop_frame(root, switch_frame, API_URL, access_token) - create_shop.place(relx=0, rely=0.2, relwidth=1, relheight=0.8) - create_shop.tkraise() - + frames.get(frame_name, login).tkraise() # Default to login if frame_name is invalid # Create main window -root = tb.Window(themename="superhero") +ctk.set_appearance_mode("dark") # Light, Dark, or System +ctk.set_default_color_theme("blue") + +root = ctk.CTk() root.title("Shopping App") root.geometry("900x800") @@ -35,13 +32,25 @@ root.geometry("900x800") 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) - -# Place all frames responsively within the window. -# Adjust relx, rely, relwidth, and relheight as needed for your layout. -for frame in (login, register, create_shop): +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) + +# Store frames in a dictionary for easier management +frames = { + "login": login, + "register": register, + "create_shop": create_shop, + "create_product": product, + "category": category, + "view_shop": view_shop +} + +# Place all frames responsively +for frame in frames.values(): frame.place(relx=0, rely=0.2, relwidth=1, relheight=0.8) # Show the login frame first -switch_frame("login") +switch_frame("view_shop") root.mainloop() -- GitLab From c12c952b99a45ae663639bd53cf3b041c7d23a03 Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Sat, 15 Mar 2025 17:34:06 +0700 Subject: [PATCH 2/5] Add shop and product --- app/frontend/components/auth/login.py | 4 ++-- app/frontend/components/auth/register.py | 4 ++-- app/frontend/components/shop/create_shop.py | 4 ++-- app/frontend/components/shop/view_shop.py | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index 8f2b656..b0290cc 100644 --- a/app/frontend/components/auth/login.py +++ b/app/frontend/components/auth/login.py @@ -2,7 +2,7 @@ import customtkinter as ctk from tkinter import messagebox import requests -def login_frame(parent, switch_func, api_url): +def login_frame(parent, switch_func, API_URL): frame = ctk.CTkFrame(parent) def login(): @@ -14,7 +14,7 @@ def login_frame(parent, switch_func, api_url): return response = requests.post( - f"{api_url}/auth/login", json={"email": email, "password": password} + f"{API_URL}/auth/login", json={"email": email, "password": password} ) try: diff --git a/app/frontend/components/auth/register.py b/app/frontend/components/auth/register.py index 3223abf..64b238b 100644 --- a/app/frontend/components/auth/register.py +++ b/app/frontend/components/auth/register.py @@ -2,7 +2,7 @@ import customtkinter as ctk from tkinter import messagebox import requests -def register_frame(parent, switch_func, api_url): +def register_frame(parent, switch_func, API_URL): frame = ctk.CTkFrame(parent) def register(): @@ -21,7 +21,7 @@ def register_frame(parent, switch_func, api_url): return response = requests.post( - f"{api_url}/auth/signup", + f"{API_URL}/auth/signup", json={"username": username, "email": email, "phone_number": phone_number, "password": password}, ) diff --git a/app/frontend/components/shop/create_shop.py b/app/frontend/components/shop/create_shop.py index 14a44a3..d7ac4e3 100644 --- a/app/frontend/components/shop/create_shop.py +++ b/app/frontend/components/shop/create_shop.py @@ -4,7 +4,7 @@ import requests import os -def create_shop_frame(parent, switch_func, api_url, token): +def create_shop_frame(parent, switch_func, API_URL, token): frame = ctk.CTkFrame(parent) selected_file_path = [None] @@ -26,7 +26,7 @@ def create_shop_frame(parent, switch_func, api_url, token): messagebox.showwarning("Input Error", "Shop name is required!") return - url = f"{api_url}/shops" + url = f"{API_URL}/shops" data = {"name": name, "description": description} files = {} diff --git a/app/frontend/components/shop/view_shop.py b/app/frontend/components/shop/view_shop.py index 960fec4..2c10314 100644 --- a/app/frontend/components/shop/view_shop.py +++ b/app/frontend/components/shop/view_shop.py @@ -4,7 +4,7 @@ from tkinter import messagebox from PIL import Image, ImageTk import io -def view_shop_frame(parent, switch_func, api_url, token): +def view_shop_frame(parent, switch_func, API_URL, token): frame = ctk.CTkFrame(parent) # Title @@ -29,7 +29,7 @@ def view_shop_frame(parent, switch_func, api_url, token): """ Fetch the shop created by the logged-in user """ headers = {"Authorization": f"Bearer {token}"} try: - response = requests.get(f"{api_url}/shops/my-shop", headers=headers) # Adjust the endpoint as needed + response = requests.get(f"{API_URL}/shops/my-shop", headers=headers) # Adjust the endpoint as needed if response.status_code == 200: shop_data = response.json() shop_name_label.configure(text=f"Shop Name: {shop_data['name']}") @@ -58,7 +58,7 @@ def view_shop_frame(parent, switch_func, api_url, token): """ Fetch products that belong to the user's shop """ 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: products = response.json() display_products(products) -- GitLab From 7c6e5ec8253116cda23e9a37e1e27b3f929597ee Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Sat, 15 Mar 2025 17:37:01 +0700 Subject: [PATCH 3/5] Add many things --- app/frontend/components/admin/category.py | 51 ++++++++++++++----- app/frontend/components/auth/login.py | 5 +- app/frontend/components/auth/register.py | 24 +++++++-- .../components/product/create_product.py | 27 +++++++--- app/frontend/components/shop/create_shop.py | 8 ++- app/frontend/components/shop/view_shop.py | 49 +++++++++++++----- app/frontend/main.py | 4 +- 7 files changed, 124 insertions(+), 44 deletions(-) diff --git a/app/frontend/components/admin/category.py b/app/frontend/components/admin/category.py index c4d3225..29c239e 100644 --- a/app/frontend/components/admin/category.py +++ b/app/frontend/components/admin/category.py @@ -2,10 +2,13 @@ import customtkinter as ctk import requests from tkinter import messagebox + def category_frame(parent, switch_func, API_URL, access_token): frame = ctk.CTkFrame(parent) - ctk.CTkLabel(frame, text="Category Management", font=("Helvetica", 18, "bold")).pack(pady=10) + ctk.CTkLabel( + frame, text="Category Management", font=("Helvetica", 18, "bold") + ).pack(pady=10) ctk.CTkLabel(frame, text="Category Name:").pack(pady=5) entry_name = ctk.CTkEntry(frame) @@ -24,12 +27,18 @@ def category_frame(parent, switch_func, API_URL, access_token): return try: - response = requests.post(f"{API_URL}/category", data={"name": name}, headers=headers) + response = requests.post( + f"{API_URL}/category", data={"name": name}, headers=headers + ) if response.status_code == 200: - messagebox.showinfo("Success", f"Category '{name}' created successfully!") - entry_name.delete(0, 'end') + messagebox.showinfo( + "Success", f"Category '{name}' created successfully!" + ) + entry_name.delete(0, "end") else: - messagebox.showerror("Error", response.json().get("detail", "Failed to create category")) + messagebox.showerror( + "Error", response.json().get("detail", "Failed to create category") + ) except requests.exceptions.RequestException as e: messagebox.showerror("Error", f"Failed to connect to server: {e}") @@ -39,7 +48,9 @@ def category_frame(parent, switch_func, API_URL, access_token): if response.status_code == 200: categories = response.json() if categories: - category_list = "\n".join([f"{cat['id']}: {cat['name']}" for cat in categories]) + category_list = "\n".join( + [f"{cat['id']}: {cat['name']}" for cat in categories] + ) messagebox.showinfo("Categories", category_list) else: messagebox.showinfo("Categories", "No categories available.") @@ -55,17 +66,25 @@ def category_frame(parent, switch_func, API_URL, access_token): messagebox.showwarning("Input Error", "Enter a valid Category ID!") return if not name: - messagebox.showwarning("Input Error", "Category name is required for update!") + messagebox.showwarning( + "Input Error", "Category name is required for update!" + ) return try: - response = requests.put(f"{API_URL}/category/{category_id}", data={"name": name}, headers=headers) + response = requests.put( + f"{API_URL}/category/{category_id}", + data={"name": name}, + headers=headers, + ) if response.status_code == 200: messagebox.showinfo("Success", "Category updated successfully!") - entry_id.delete(0, 'end') - entry_name.delete(0, 'end') + entry_id.delete(0, "end") + entry_name.delete(0, "end") else: - messagebox.showerror("Error", response.json().get("detail", "Failed to update category")) + messagebox.showerror( + "Error", response.json().get("detail", "Failed to update category") + ) except requests.exceptions.RequestException as e: messagebox.showerror("Error", f"Failed to connect to server: {e}") @@ -76,12 +95,16 @@ def category_frame(parent, switch_func, API_URL, access_token): return try: - response = requests.delete(f"{API_URL}/category/{category_id}", headers=headers) + response = requests.delete( + f"{API_URL}/category/{category_id}", headers=headers + ) if response.status_code == 200: messagebox.showinfo("Success", "Category deleted successfully!") - entry_id.delete(0, 'end') + entry_id.delete(0, "end") else: - messagebox.showerror("Error", response.json().get("detail", "Failed to delete category")) + messagebox.showerror( + "Error", response.json().get("detail", "Failed to delete category") + ) except requests.exceptions.RequestException as e: messagebox.showerror("Error", f"Failed to connect to server: {e}") diff --git a/app/frontend/components/auth/login.py b/app/frontend/components/auth/login.py index b0290cc..64a9f81 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 import requests + def login_frame(parent, switch_func, API_URL): frame = ctk.CTkFrame(parent) @@ -24,7 +25,9 @@ def login_frame(parent, switch_func, API_URL): messagebox.showinfo("Login Successful", f"Welcome back, {email}!") switch_func("create_shop", access_token) else: - messagebox.showerror("Login Failed", response_data.get("detail", "Invalid credentials")) + messagebox.showerror( + "Login Failed", response_data.get("detail", "Invalid credentials") + ) except requests.exceptions.JSONDecodeError: messagebox.showerror("Login Failed", "Server returned an invalid response.") diff --git a/app/frontend/components/auth/register.py b/app/frontend/components/auth/register.py index 64b238b..e098132 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 import requests + 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 @@ -22,7 +29,12 @@ def register_frame(parent, switch_func, API_URL): response = requests.post( f"{API_URL}/auth/signup", - json={"username": username, "email": email, "phone_number": phone_number, "password": password}, + json={ + "username": username, + "email": email, + "phone_number": phone_number, + "password": password, + }, ) try: @@ -31,9 +43,13 @@ 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") + ) except requests.exceptions.JSONDecodeError: - messagebox.showerror("Registration Failed", "Server returned an invalid response.") + messagebox.showerror( + "Registration Failed", "Server returned an invalid response." + ) ctk.CTkLabel(frame, text="Register", font=("Helvetica", 18, "bold")).pack(pady=10) diff --git a/app/frontend/components/product/create_product.py b/app/frontend/components/product/create_product.py index 2fc5b7a..b95400c 100644 --- a/app/frontend/components/product/create_product.py +++ b/app/frontend/components/product/create_product.py @@ -2,10 +2,13 @@ import customtkinter as ctk import requests from tkinter import messagebox + def product_frame(parent, switch_func, API_URL, token): frame = ctk.CTkFrame(parent) - ctk.CTkLabel(frame, text="Product Management", font=("Helvetica", 18, "bold")).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) @@ -32,7 +35,9 @@ def product_frame(parent, switch_func, API_URL, token): price = float(price) stock = int(stock) except ValueError: - messagebox.showwarning("Input Error", "Price must be a number and Stock must be an integer.") + messagebox.showwarning( + "Input Error", "Price must be a number and Stock must be an integer." + ) return headers = {"Authorization": f"Bearer {token}"} @@ -42,17 +47,23 @@ def product_frame(parent, switch_func, API_URL, token): 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') + messagebox.showinfo( + "Success", f"Product '{name}' created successfully!" + ) + entry_name.delete(0, "end") + entry_price.delete(0, "end") + entry_stock.delete(0, "end") else: - error_message = response.json().get("detail", "Failed to create product") + 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}") 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) + ctk.CTkButton(frame, text="Back", command=lambda: switch_func("view_shop")).pack( + pady=5 + ) return frame diff --git a/app/frontend/components/shop/create_shop.py b/app/frontend/components/shop/create_shop.py index d7ac4e3..5319ebe 100644 --- a/app/frontend/components/shop/create_shop.py +++ b/app/frontend/components/shop/create_shop.py @@ -63,7 +63,9 @@ def create_shop_frame(parent, switch_func, API_URL, token): except Exception as e: messagebox.showerror("Request Error", str(e)) - 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") @@ -77,7 +79,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, diff --git a/app/frontend/components/shop/view_shop.py b/app/frontend/components/shop/view_shop.py index 2c10314..a5b9141 100644 --- a/app/frontend/components/shop/view_shop.py +++ b/app/frontend/components/shop/view_shop.py @@ -4,6 +4,7 @@ from tkinter import messagebox from PIL import Image, ImageTk import io + def view_shop_frame(parent, switch_func, API_URL, token): frame = ctk.CTkFrame(parent) @@ -12,10 +13,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 @@ -26,14 +31,18 @@ def view_shop_frame(parent, switch_func, API_URL, token): product_list_frame.pack(fill="both", expand=True, padx=10, pady=10) def fetch_shop_data(): - """ Fetch the shop created by the logged-in user """ + """Fetch the shop created by the logged-in user""" headers = {"Authorization": f"Bearer {token}"} try: - response = requests.get(f"{API_URL}/shops/my-shop", headers=headers) # Adjust the endpoint as needed + response = requests.get( + f"{API_URL}/shops/my-shop", headers=headers + ) # Adjust the endpoint as needed if response.status_code == 200: shop_data = response.json() 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')}" + ) # Load and display shop image if available if "image_url" in shop_data and shop_data["image_url"]: @@ -47,7 +56,7 @@ def view_shop_frame(parent, switch_func, API_URL, token): shop_image_label.image = img_tk except Exception: pass - + fetch_products(shop_data["id"]) # Fetch products for this shop else: messagebox.showerror("Error", "Failed to fetch shop details.") @@ -55,10 +64,12 @@ def view_shop_frame(parent, switch_func, API_URL, token): messagebox.showerror("Error", f"Request error: {e}") def fetch_products(shop_id): - """ Fetch products that belong to the user's shop """ + """Fetch products that belong to the user's shop""" 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: products = response.json() display_products(products) @@ -68,12 +79,14 @@ def view_shop_frame(parent, switch_func, API_URL, token): messagebox.showerror("Error", f"Request error: {e}") def display_products(products): - """ Display the list of products in the shop """ + """Display the list of products in the shop""" 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) + ctk.CTkLabel( + product_list_frame, text="No products found.", font=("Helvetica", 12) + ).pack(pady=10) return for product in products: @@ -102,15 +115,23 @@ 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 Data Button refresh_button = ctk.CTkButton(frame, text="Refresh", command=fetch_shop_data) 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/main.py b/app/frontend/main.py index cc8b927..2b14d6b 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -12,6 +12,7 @@ API_URL = "http://127.0.0.1:8000" # Global variable to store the access token access_token = None + # Function to switch between frames def switch_frame(frame_name, token=None): global access_token @@ -20,6 +21,7 @@ def switch_frame(frame_name, token=None): frames.get(frame_name, login).tkraise() # Default to login if frame_name is invalid + # Create main window ctk.set_appearance_mode("dark") # Light, Dark, or System ctk.set_default_color_theme("blue") @@ -43,7 +45,7 @@ frames = { "create_shop": create_shop, "create_product": product, "category": category, - "view_shop": view_shop + "view_shop": view_shop, } # Place all frames responsively -- GitLab From 55c6fc3f978b7afcfd1516e6aa928a9fff2ab362 Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Sat, 15 Mar 2025 17:39:24 +0700 Subject: [PATCH 4/5] Minor changes --- requirements.txt | Bin 978 -> 2142 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/requirements.txt b/requirements.txt index c8b79503eac507894fdbccb6dc8052bfbf436fdd..098a8e9d014d631acb415fded564d5774243a0a0 100644 GIT binary patch literal 2142 zcmezWFOeaSA&()Sp@bokp@booA%#Jgp@gB5p@1Qkp_svz!Ir^*L65<lL65<JftP`c z0i?c?A(J5=EN8-?$6x`MOJYc7C<5y(0m~XQ=rI_9F-T`JLn=cNLkUABLmERSSl)=i zfWe5tltGWdkU@{Zn86TiLo!1eTm?wn5Ntb4E`uSFp@^ZFA(f$oK^N@uB8FUsM1~xO zOol434InpzYzDavrVAvS%#aPX735x!xfW2{@)>d%@)?R462azxbc1{h(rpG+0rOKn zLpoSp0Ye5uCD<$z29P?CdLyXXQifuN5{7(+T!s>cY=%sRJaDKMfmMTiiU^MsuurlX zQow#mWk_a#gc`^+kQ*W4ox+gEP|Q%kP{~ljkin48kOwviWC|iqQo%0IWXNHNXDEY) z1;})eJ0LcL)Ppd@%ru5XuqzW83ZO9oveS^k5E>VdP(xLv%a9BXV_5irbeMzv3<{G} zhE#?;h8(a@Kq^4t4+>+5Nf`_vvq9n@U53yw1Eq-!h7yJnh609Ua15k^{RYwpQU@^| zrUK*_P)s4xq#1)Ag9+FLFf|om{UE(Y;8Y2*J(B?xhKXP~h`S)6kjYTOP|N@dr#yyq zaLUPN0L322ouIG+VTejEa6D!)Br+HwnWD#F3bq}T`idAp@tDt0&XC8T%K$Q27aV6G zvp{x&+ypVp7aHrO3<V6q42cYB3=m(#R6){FE<*}KDMJxM4%j}BZcwPe<SH48844M4 z7(j6XN)dSskkkW8#SnXu(~2cH2g33%NKF<)F+3%K>@@`EaZv1o;-r8fkpYxf5*gCL z{!RzG!3Z2{5Ep<{6f+cq(?b$i7UVimdW6IWC{^Wvb5%K179<bCkT3wH=41wtDUh6u zoJL_bR6=70R9+;5O##IR#HEmY2{Hu|dk{6c45<ti3?<+kie82x^kO$(7h0l#Oai3_ zNGb#A0_8+VIzqQE9wpU+)Ea?HDu@jM43!M&47uQZ0ZM0}lmJp=$N-8Dh>0LIUJT(3 zA!z<X$oeu=G6XXOGWdX1fI<+0A@PJL6Lc9;z-2fn7nZ@*LedSU+FXWGh8%E742c6o zxdYMxDwRQcK(32qaAfd>+78NZ5P6Vn5ko3NAwwy+mO%EOF}TDx2Iq_-hD?TJh772C zKq(F6cSst6$$?@jp8=G!L8T?aC58+j^^mYEX2@VjWdNlpP<fip0ICCWp{9azA*>uI zX2=8AE};4aQa^!03uF!?L?I#R$dChe6{ysNl;WUx0r||B0a7PGa#t=xKDcZ~<Ux=c zBL;}yK&3pWCW4fCpqdjD!ypwV43Lllg%YeLg`{whtRc9Jh4>313-MDtH1&dFuozsw zg35i6nV=fR5FDxyU8M}j`4?2DKyn2nZ$U~8P<@xgU<?i;knccY0Fy5R#|o$%fYc=* yR~dtIH$+7_xFrCJH&8f&{0GSmps)m~g~VSuLn=cOxEukM6`<M}rV8XDh)DptEjSAR literal 978 zcmYey%gZlGEJ;n#EvYO>Ew;5a&@<OF;7ZJ^%*?m7HPN%sGvG=}E~+djv9&eUGtx8S zN=_{*$xO?%wKXy@GSxHGGdAQ(P6P1`^$gAR47rjs5{rscOLX({i*gflGOJRHKsrr8 zGC7&a*|xS8dWL!yT*>)4`9+Dji69$I^vt-xcI2lQB^G2<+S;0!=o#o4ai!!H7gUyH z<mcJi8iC!FnwyxJ6JM5?lbMoOl3xT8HqtZTN=qy*Ni4_&nPq5bs%K~j6VXl10gD>w znRBHVrKaZPq=G$SsAtHPVQ2`}V4`Qhl~GbskepwXYHMq#2T}{-m*nT?fCI!#&jhTX z0;Jr?LeG#ZGbJw(<SRo1uFR6+l*GLB)S~>-VwiKhGV`($jlc@@Ou2Fsi?UPl%ky+I zOLPk=L81nF23)?0McJhV!HH?95D_D;+?3Ly9FP@;dPZEimBocQy2<%@d8x@I`9-?0 zV6fB!`8U5Ps~8k0#(IVZdWNQ41&PJQIhjcy*PH8^a1~_c<m8vz+8P?_8R{8u6;vh{ zfZ_xaTt-|4l_`mNC7H<}=NcO5nZX5gQ!7e}5@9L92xK@ywQg~0Nl9j2I@p(>K!6z& z4+%IUJtK2HBd&nT^xV|E60j;mOFcubfJ(3M5U5oko^NGvpbyBUpg4edOE)FIBsH%L zBxs;#$ORS5EzK#(EJ!RW0fmWyo{<4pKxL$(uaB**nI0%{6r~oHrWQkNGB(mP<|@ie z&akyLG}g1!GXZmSK~b2U2{ypcSkH*7I3qPDCo?ZSBQY0bnW>%$S8-luS{f*=8|oSB z8FB>&`Z(q!XQbv<f}CuiXKcY$T$q!apOOlW8UsBeW3J+o#G;(kl9E)A!%WQd47f^4 zvXk=jONvX15(_{EgJOjXl%a}1N(@c)j6l51y!80gijvg4;>`TKVo>ff1ZAjFNL-qN zg4=+rv?wPhGszfayRn`DS7}*ha(+=B$QWZ#LM=}$NzO>i%mHOpP*OGFDo;%+&QH!x L1qFnmsh$A<%(^IC -- GitLab From 5be1c99530bdad59328f440fd7d25099693c9ffd Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Sat, 15 Mar 2025 17:47:02 +0700 Subject: [PATCH 5/5] change main.py --- app/frontend/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/frontend/main.py b/app/frontend/main.py index 2b14d6b..d81ac74 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -53,6 +53,6 @@ for frame in frames.values(): frame.place(relx=0, rely=0.2, relwidth=1, relheight=0.8) # Show the login frame first -switch_frame("view_shop") +switch_frame("login") root.mainloop() -- GitLab