diff --git a/app/backend/dummy_data.py b/app/backend/dummy_data.py index c1c4050ef00a1c3dcc39f31b3f09c27c024fb920..a2a9989312cf487af4a89289dc8691639877ac47 100644 --- a/app/backend/dummy_data.py +++ b/app/backend/dummy_data.py @@ -19,12 +19,14 @@ def insert_dummy_data(session: Session): username="string", email="user@example.com", password=hash_password("string"), + phone_number="1234567890", role="customer", ), User( username="shop_owner", email="owner@example.com", password=hash_password("string"), + phone_number="0987654321", role="shop_owner", ), ] diff --git a/app/backend/main.py b/app/backend/main.py index 2cf270aad86fa6f2c97a3177e54bd35781238d1c..ce55719c84245163d88da4438260f8644c036c2e 100644 --- a/app/backend/main.py +++ b/app/backend/main.py @@ -22,11 +22,15 @@ init_db() # Include API routes app.include_router(search.router, prefix="/search", tags=["search"]) app.include_router(auth.router, prefix="/auth", tags=["auth"]) +# Also include auth router at /user for backward compatibility +app.include_router(auth.router, prefix="/user", tags=["user"]) app.include_router(payment.router, prefix="/payment", tags=["payment"]) app.include_router(shop.router, prefix="/shops", tags=["shops"]) app.include_router(product.router, prefix="/product", tags=["product"]) app.include_router(category.router, prefix="/category", tags=["category"]) app.include_router(order.router, prefix="/order", tags=["order"]) +# Also include order router at /orders for backward compatibility +app.include_router(order.router, prefix="/orders", tags=["orders"]) @app.get("/") diff --git a/app/backend/models/models.py b/app/backend/models/models.py index 8308831705efa3732c88a1ce48883179b08c05b8..366abc384d49f7576393a18537bda4c6103b698d 100644 --- a/app/backend/models/models.py +++ b/app/backend/models/models.py @@ -8,6 +8,7 @@ class User(SQLModel, table=True): username: str email: str = Field(unique=True, index=True) password: str + phone_number: str payments: List["Payment"] = Relationship(back_populates="user") role: str = Field(default="customer") # Roles: customer, shop_owner, admin created_at: datetime = Field(default_factory=datetime.utcnow) diff --git a/app/backend/routes/auth.py b/app/backend/routes/auth.py index 7dfc67be951acc8e333e9146a162a65f0e48ae20..b93bff5c44c89acaaeac96780bf458a9d0edb51d 100644 --- a/app/backend/routes/auth.py +++ b/app/backend/routes/auth.py @@ -62,3 +62,34 @@ def login(user_data: UserLogin, session: Session = Depends(get_session)): "access_token": access_token, "token_type": "bearer", } + + +@router.get("/profile") +def get_user_profile(current_user: User = Depends(get_current_user)): + """Get the current user's profile information""" + return { + "username": current_user.username, + "name": current_user.username, # Just use username since name isn't in the model + "email": current_user.email, + "phone": current_user.phone_number, + } + + +@router.put("/update") +def update_user_profile( + user_data: dict, current_user: User = Depends(get_current_user), session: Session = Depends(get_session) +): + """Update the current user's profile information""" + # Update user fields + if "username" in user_data: + current_user.username = user_data["username"] + if "email" in user_data: + current_user.email = user_data["email"] + if "phone" in user_data: + current_user.phone_number = user_data["phone"] + + session.add(current_user) + session.commit() + session.refresh(current_user) + + return {"message": "Profile updated successfully"} diff --git a/app/backend/schemas/category.py b/app/backend/schemas/category.py index 976870f1476cc02a528a9c7c62e1dfeecf064777..79fb6d00805529d7b177b9e4e5a0e7ea9ec409cb 100644 --- a/app/backend/schemas/category.py +++ b/app/backend/schemas/category.py @@ -15,4 +15,4 @@ class CategoryRead(CategoryBase): class CategoryUpdate(SQLModel): - name: Optional[str] = None \ No newline at end of file + name: Optional[str] = None diff --git a/app/backend/schemas/product.py b/app/backend/schemas/product.py index 885907fc590910331ace0684078347126c95bfe2..cd3e4663a2cb9d90c947416b6b4f476df2482a90 100644 --- a/app/backend/schemas/product.py +++ b/app/backend/schemas/product.py @@ -20,7 +20,7 @@ class ProductRead(ProductBase): id: int created_at: datetime images: List["ProductImageRead"] = [] - + class Config: orm_mode = True @@ -45,6 +45,6 @@ class ProductImageCreate(ProductImageBase): class ProductImageRead(ProductImageBase): id: int - + class Config: orm_mode = True diff --git a/app/backend/utils/hashing.py b/app/backend/utils/hashing.py index 02df6fd461aaa069d1223166911129c319e93f5e..e538ce0f88d1d28105447c4d0c04a9f6b1cbc2f8 100644 --- a/app/backend/utils/hashing.py +++ b/app/backend/utils/hashing.py @@ -24,21 +24,30 @@ def create_access_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) + print(f"Creating token with data: {to_encode}") encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) + print(f"Token created: {encoded_jwt[:10]}...") return encoded_jwt def decode_token(token: str) -> int: try: - token = token.replace("Bearer ", "") # Remove "Bearer " prefix + print(f"Decoding token: {token[:10]}...") + # Remove "Bearer " prefix if present + if token.startswith("Bearer "): + token = token[7:] + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - user_id: int = payload.get("sub") + print(f"Decoded payload: {payload}") + user_id = payload.get("sub") if user_id is None: + print("No user_id in token payload") raise HTTPException( status_code=401, detail="Invalid authentication credentials" ) return user_id - except PyJWTError: + except PyJWTError as e: + print(f"JWT error: {str(e)}") raise HTTPException( - status_code=401, detail="Invalid authentication credentials" + status_code=401, detail=f"Invalid authentication credentials: {str(e)}" ) diff --git a/app/frontend/components/user_details.py b/app/frontend/components/user_details.py index 645916f069beb16942e19cb3fe7e81fb2dbe9d19..1425340d15efeac68ec2461095577fa39acf818f 100644 --- a/app/frontend/components/user_details.py +++ b/app/frontend/components/user_details.py @@ -15,6 +15,14 @@ def user_details_frame(parent, switch_func, API_URL, token): """ # Main container frame frame = ctk.CTkFrame(parent, fg_color=DARK_BG) + frame.token = token # Store token as an attribute + + def update_token(new_token): + """Update the token when it changes""" + frame.token = new_token + + # Add the update_token method to the frame + frame.update_token = update_token # --- TOP BAR --- top_bar = ctk.CTkFrame(frame, fg_color=SHOPPING, height=50) @@ -76,7 +84,10 @@ def user_details_frame(parent, switch_func, API_URL, token): nav_profile = create_nav_button("My Profile", is_active=True) nav_profile.pack(fill="x", padx=15, pady=5) - nav_orders = create_nav_button("My Orders", lambda: switch_func("user_orders")) + def open_orders(): + switch_func("user_orders") + + nav_orders = create_nav_button("My Orders", open_orders) nav_orders.pack(fill="x", padx=15, pady=5) def open_payments(): @@ -85,12 +96,13 @@ def user_details_frame(parent, switch_func, API_URL, token): nav_payments = create_nav_button("My Payments", open_payments) nav_payments.pack(fill="x", padx=15, pady=5) - become_owner = create_nav_button( - "Become Shop Owner", lambda: switch_func("create_shop") - ) + def open_create_shop(): + switch_func("create_shop") + + become_owner = create_nav_button("Become Shop Owner", open_create_shop) become_owner.pack(fill="x", padx=15, pady=5) - # RIGHT CONTENT (User Details Form) + # RIGHT CONTENT (User Details) content_frame = ctk.CTkFrame(main_section, fg_color=CARD_BG, corner_radius=15) content_frame.pack(side="left", fill="both", expand=True, padx=(0, 20), pady=20) @@ -108,7 +120,7 @@ def user_details_frame(parent, switch_func, API_URL, token): subtitle_label = ctk.CTkLabel( header_frame, - text="Manage your profile information to keep your account secure", + text="View and manage your profile information", font=("Helvetica", 14), text_color="gray", ) @@ -118,9 +130,50 @@ def user_details_frame(parent, switch_func, API_URL, token): main_content = ctk.CTkFrame(content_frame, fg_color="transparent") main_content.pack(fill="both", expand=True, padx=30, pady=(0, 20)) - # LEFT FORM - form_frame = ctk.CTkFrame(main_content, fg_color="transparent") - form_frame.pack(side="left", fill="both", expand=True, padx=(0, 20)) + # View Mode Frame (initial view) + view_frame = ctk.CTkFrame(main_content, fg_color="transparent") + view_frame.pack(fill="both", expand=True) + + # Edit Mode Frame (hidden initially) + edit_frame = ctk.CTkFrame(main_content, fg_color="transparent") + # Not packing this yet - it will be shown when edit mode is activated + + # --- VIEW MODE UI --- + view_content = ctk.CTkFrame(view_frame, fg_color="transparent") + view_content.pack(fill="both", expand=True, padx=30) + + # Function to create a user detail row in view mode + def create_detail_row(parent, label_text, value_text=""): + row = ctk.CTkFrame(parent, fg_color="transparent", height=50) + row.pack(fill="x", pady=(0, 15)) + row.pack_propagate(False) + + label = ctk.CTkLabel( + row, text=label_text, font=("Helvetica", 14, "bold"), text_color="white", width=120 + ) + label.pack(side="left", anchor="w") + + value = ctk.CTkLabel( + row, text=value_text, font=("Helvetica", 14), text_color="white" + ) + value.pack(side="left", padx=(20, 0), anchor="w") + + return value # Return so we can update it later + + # Create view mode fields + username_view = create_detail_row(view_content, "Username:") + name_view = create_detail_row(view_content, "Name:") + email_view = create_detail_row(view_content, "Email:") + phone_view = create_detail_row(view_content, "Phone:") + + # Button container for view mode + button_container_view = ctk.CTkFrame(view_content, fg_color="transparent") + button_container_view.pack(fill="x", pady=(20, 0)) + + # --- EDIT MODE UI --- + # Edit Form + form_frame = ctk.CTkFrame(edit_frame, fg_color="transparent") + form_frame.pack(fill="both", expand=True, padx=30) def create_form_field(label_text, placeholder_text): field_frame = ctk.CTkFrame(form_frame, fg_color="transparent") @@ -148,53 +201,76 @@ def user_details_frame(parent, switch_func, API_URL, token): email_entry = create_form_field("Email", "Enter your email...") phone_entry = create_form_field("Phone", "Enter your phone number...") - # Gender Selection - gender_frame = ctk.CTkFrame(form_frame, fg_color="transparent") - gender_frame.pack(fill="x", pady=(0, 15)) - - ctk.CTkLabel( - gender_frame, text="Gender", font=("Helvetica", 14), text_color="white" - ).pack(anchor="w") - - gender_var = ctk.StringVar(value="Male") - gender_options = ctk.CTkFrame(gender_frame, fg_color="transparent") - gender_options.pack(fill="x", pady=(5, 0)) - - for gender in ["Male", "Female", "Other"]: - ctk.CTkRadioButton( - gender_options, - text=gender, - variable=gender_var, - value=gender, - text_color="white", - fg_color=SHOPPING, - hover_color="#0096ff", - ).pack(side="left", padx=(0, 20)) - - birthday_entry = create_form_field("Date of Birth", "dd/mm/yyyy") + # Button container for save/cancel in edit mode + button_container_edit = ctk.CTkFrame(form_frame, fg_color="transparent") + button_container_edit.pack(fill="x", pady=(20, 0)) + + # Mode switching functions + def enable_edit_mode(): + view_frame.pack_forget() + edit_frame.pack(fill="both", expand=True) + + # Copy data from view to edit mode fields + username_entry.delete(0, "end") + username_entry.insert(0, username_view.cget("text")) + + name_entry.delete(0, "end") + name_entry.insert(0, name_view.cget("text")) + + email_entry.delete(0, "end") + email_entry.insert(0, email_view.cget("text")) + email_entry.configure(state="disabled") # Make email field read-only + + phone_entry.delete(0, "end") + phone_entry.insert(0, phone_view.cget("text")) + + def cancel_edit(): + edit_frame.pack_forget() + view_frame.pack(fill="both", expand=True) + email_entry.configure(state="normal") # Reset state when canceling + + # Edit button in view mode + edit_button = ctk.CTkButton( + button_container_view, + text="Edit Profile", + command=enable_edit_mode, + corner_radius=8, + height=40, + font=("Helvetica", 14, "bold"), + fg_color=SHOPPING, + hover_color="#0096ff", + ) + edit_button.pack(fill="x") - # Save Button + # Save and Cancel buttons in edit mode def save_profile(): - headers = {"Authorization": f"Bearer {token}"} + headers = {"Authorization": f"Bearer {frame.token}"} payload = { "username": username_entry.get().strip(), - "name": name_entry.get().strip(), - "email": email_entry.get().strip(), - "phone": phone_entry.get().strip(), - "gender": gender_var.get(), - "birthday": birthday_entry.get().strip(), + "name": name_entry.get().strip(), # Backend ignores this field + "phone": phone_entry.get().strip(), # Note: backend expects "phone", not "phone_number" + # Email is intentionally omitted from the payload to prevent changes } try: resp = requests.put(f"{API_URL}/user/update", headers=headers, json=payload) if resp.status_code == 200: messagebox.showinfo("Success", "Profile updated successfully!") + + # Update the view labels with new data + username_view.configure(text=payload["username"]) + name_view.configure(text=payload["name"]) # This will be updated to match username on next fetch + # Email stays the same + phone_view.configure(text=payload["phone"]) + + # Switch back to view mode + cancel_edit() else: messagebox.showerror("Error", "Unable to update profile.") except Exception as e: messagebox.showerror("Error", f"Request error: {e}") save_button = ctk.CTkButton( - form_frame, + button_container_edit, text="Save Changes", command=save_profile, corner_radius=8, @@ -203,90 +279,43 @@ def user_details_frame(parent, switch_func, API_URL, token): fg_color=SHOPPING, hover_color="#0096ff", ) - save_button.pack(fill="x", pady=(20, 0)) - - # RIGHT PICTURE SECTION - pic_frame = ctk.CTkFrame(main_content, fg_color="transparent") - pic_frame.pack(side="left", fill="both", expand=True) - - pic_label = ctk.CTkLabel( - pic_frame, - text="Profile Picture", - font=("Helvetica", 18, "bold"), - text_color=SHOPPING, - ) - pic_label.pack(anchor="w", pady=(0, 10)) - - photo_frame = ctk.CTkFrame(pic_frame, fg_color="#3b3b3b", corner_radius=10) - photo_frame.pack(fill="x", pady=(0, 20)) + save_button.pack(side="left", fill="x", expand=True, padx=(0, 5)) - photo_label = ctk.CTkLabel( - photo_frame, text="No image", text_color="gray", font=("Helvetica", 14) - ) - photo_label.pack(pady=20) - - def choose_photo(): - file_path = filedialog.askopenfilename( - title="Choose a Profile Picture", - filetypes=[("Image Files", "*.png *.jpg *.jpeg *.gif")], - ) - if file_path: - try: - pil_img = Image.open(file_path).resize((150, 150)) - tk_img = ImageTk.PhotoImage(pil_img) - photo_label.configure(image=tk_img, text="") - photo_label.image = tk_img - 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", - command=choose_photo, + cancel_button = ctk.CTkButton( + button_container_edit, + text="Cancel", + command=cancel_edit, corner_radius=8, - height=40, + height=45, font=("Helvetica", 14), - fg_color=SHOPPING, - hover_color="#0096ff", + fg_color="#555555", + hover_color="#777777", ) - change_photo_button.pack(fill="x") + cancel_button.pack(side="right", fill="x", expand=True, padx=(5, 0)) def fetch_user_info(): - headers = {"Authorization": f"Bearer {token}"} + headers = {"Authorization": f"Bearer {frame.token}"} + print(f"Fetching user info with token: {frame.token[:10] if frame.token else 'None'}") try: resp = requests.get(f"{API_URL}/user/profile", headers=headers) + print(f"User profile response: {resp.status_code}") if resp.status_code == 200: data = resp.json() - username_entry.delete(0, "end") - username_entry.insert(0, data.get("username", "")) - name_entry.delete(0, "end") - name_entry.insert(0, data.get("name", "")) - email_entry.delete(0, "end") - email_entry.insert(0, data.get("email", "")) - phone_entry.delete(0, "end") - phone_entry.insert(0, data.get("phone", "")) - gender_var.set(data.get("gender", "Male")) - birthday_entry.delete(0, "end") - birthday_entry.insert(0, data.get("birthday", "")) - pic_url = data.get("photo_url") - if pic_url: - try: - resp_pic = requests.get(pic_url) - if resp_pic.status_code == 200: - pil_img = Image.open(io.BytesIO(resp_pic.content)).resize( - (150, 150) - ) - tk_img = ImageTk.PhotoImage(pil_img) - photo_label.configure(image=tk_img, text="") - photo_label.image = tk_img - except Exception as e: - print(f"Profile picture load error: {e}") + print(f"User data received: {data}") + + # Update view mode displays + username_view.configure(text=data.get("username", "")) + name_view.configure(text=data.get("name", "")) # This will be the username + email_view.configure(text=data.get("email", "")) + phone_view.configure(text=data.get("phone", "")) else: + print(f"Error response: {resp.text}") messagebox.showerror("Error", "Unable to retrieve user information.") except Exception as e: + print(f"Exception in fetch_user_info: {e}") messagebox.showerror("Error", f"Request error: {e}") + # Initialize by fetching user info fetch_user_info() return frame diff --git a/app/frontend/components/user_orders.py b/app/frontend/components/user_orders.py index 3aff27bdebf807559b0b6cdab70010519f835903..849256f215344a3062453aa042b6845098c3df4b 100644 --- a/app/frontend/components/user_orders.py +++ b/app/frontend/components/user_orders.py @@ -26,6 +26,14 @@ def user_orders_frame(parent, switch_func, API_URL, token): """ # Main container frame with dark background frame = ctk.CTkFrame(parent, fg_color=DARK_BG) + frame.token = token # Store token as an attribute + + def update_token(new_token): + """Update the token when it changes""" + frame.token = new_token + + # Add the update_token method to the frame + frame.update_token = update_token # --- TOP BAR --- top_bar = ctk.CTkFrame(frame, fg_color=SHOPPING, height=50) @@ -127,11 +135,14 @@ def user_orders_frame(parent, switch_func, API_URL, token): widget.destroy() try: - headers = {"Authorization": f"Bearer {token}"} - response = requests.get(f"{API_URL}/orders/list", headers=headers) + headers = {"Authorization": f"Bearer {frame.token}"} + print(f"Loading orders with token: {frame.token[:10] if frame.token else 'None'}") + response = requests.get(f"{API_URL}/order/list", headers=headers) + print(f"Orders response status: {response.status_code}") if response.status_code == 200: orders = response.json() + print(f"Orders received: {len(orders)}") if not orders: empty_label = ctk.CTkLabel( @@ -148,9 +159,11 @@ def user_orders_frame(parent, switch_func, API_URL, token): else: error_msg = response.json().get("detail", "Unknown error") + print(f"Error loading orders: {error_msg}") messagebox.showerror("Error", f"Failed to load orders: {error_msg}") except Exception as e: + print(f"Exception in load_order_history: {e}") messagebox.showerror("Error", f"Failed to load order history: {str(e)}") def create_order_history_frame(order): diff --git a/app/frontend/components/user_payments.py b/app/frontend/components/user_payments.py index 2b780d940981620db8abc1b864c9bc370a155542..488d54459bf64653624760cc0f9e5c435d5c1593 100644 --- a/app/frontend/components/user_payments.py +++ b/app/frontend/components/user_payments.py @@ -429,10 +429,13 @@ def user_payments_frame(parent, switch_func, API_URL, token): try: headers = {"Authorization": f"Bearer {frame.token}"} + print(f"Refreshing payments with token: {frame.token[:10] if frame.token else 'None'}") response = requests.get(f"{API_URL}/payment/user", headers=headers) + print(f"Payments response status: {response.status_code}") if response.status_code == 200: payments = response.json() + print(f"Payments received: {len(payments)}") # Clear existing card if any if saved_card_frame is not None: saved_card_frame.destroy() @@ -453,17 +456,20 @@ def user_payments_frame(parent, switch_func, API_URL, token): enable_form_inputs() elif response.status_code == 401: # Handle unauthorized error - likely token expired + print(f"Authentication error: {response.text}") messagebox.showerror( "Authentication Error", "Your session has expired. Please log in again.", ) elif response.status_code == 404: # Handle 404 gracefully - just means no payment methods yet + print("No payment methods found (404)") clear_form() enable_form_inputs() else: # Handle other errors error_msg = response.json().get("detail", "Unknown error") + print(f"Error refreshing payments: {error_msg}") messagebox.showerror( "Error", f"Failed to fetch payment methods: {error_msg}" ) diff --git a/app/frontend/main.py b/app/frontend/main.py index 70d0d796c63914e36fc95de5c11aabdc887e5eb2..7d3d1339715ab96018b19452cd4b7f931800f49e 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -21,33 +21,67 @@ def switch_frame(frame_name, *args): Optionally, additional data (like shop_data) can be passed as extra arguments. """ global access_token - if args and args[0] and isinstance(args[0], str): + + # Special handling for dashboard after login + if frame_name == "dashboard" and args and isinstance(args[0], str): + # This is coming from login with a new token access_token = args[0] + print(f"New login detected. Setting token: {access_token[:10]}...") + + # Recreate all frames that need the token to ensure they start with the correct token + initialize_authenticated_frames(access_token) + # For other frames, update token if provided + elif args and args[0] and isinstance(args[0], str) and frame_name not in ["login", "register"]: + access_token = args[0] + print(f"Token updated in switch_frame: {access_token[:10]}...") # Update token for all frames that need it - for frame_key in [ - "dashboard", - "user_details", - "user_orders", - "user_payments", - "create_shop", - "view_shop", - "product", - "category", - ]: - if frame_key in frames and hasattr(frames[frame_key], "update_token"): - frames[frame_key].update_token(access_token) + for frame_key, frame_obj in frames.items(): + if hasattr(frame_obj, "update_token"): + print(f"Updating token for frame: {frame_key}") + frame_obj.update_token(access_token) frame = frames.get(frame_name) if frame is None: print(f"Frame {frame_name} not found!") return + # If there's a refresh method on the frame and we have arguments, call it if hasattr(frame, "refresh_data") and len(args) > 0: frame.refresh_data(*args) + # Make sure we have a valid token for authenticated pages + if frame_name not in ["login", "register"] and (access_token is None or access_token == ""): + print("No valid token, redirecting to login") + frames["login"].tkraise() + return + + if frame_name not in ["login", "register"]: + print(f"Switching to {frame_name} with token: {access_token[:10] if access_token else 'None'}") + frame.tkraise() +def initialize_authenticated_frames(token): + """Initialize or reinitialize all frames that require authentication""" + global frames + + # Recreate authenticated frames with the new token + frames["create_shop"] = create_shop_frame(root, switch_frame, API_URL, token) + frames["view_shop"] = view_shop_frame(root, switch_frame, API_URL, token, None) + frames["create_product"] = create_product_frame(root, switch_frame, API_URL, token) + frames["view_product"] = view_product_frame(root, switch_frame, API_URL, token, None) + frames["category"] = category_frame(root, switch_frame, API_URL, token) + frames["dashboard"] = dashboard_frame(root, switch_frame, API_URL, token) + frames["user_details"] = user_details_frame(root, switch_frame, API_URL, token) + frames["user_orders"] = user_orders_frame(root, switch_frame, API_URL, token) + frames["user_payments"] = user_payments_frame(root, switch_frame, API_URL, token) + + # Place all authenticated frames + for key, frame in frames.items(): + if key not in ["login", "register"]: + frame.place(relx=0, rely=0, relwidth=1, relheight=1) + + ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") @@ -55,40 +89,26 @@ root = ctk.CTk() root.title("Shopping App") root.geometry("1000x800") -# Create Frames with the token parameter where needed. +# Create frames +frames = {} + +# Login and register don't need tokens 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 -# Fix the parameter order for view_shop_frame -view_shop = view_shop_frame( - root, switch_frame, API_URL, access_token, None -) # Pass shop_id as None initially -product = create_product_frame(root, switch_frame, API_URL, access_token) -view_product = view_product_frame(root, switch_frame, API_URL, access_token, None) -category = category_frame(root, switch_frame, API_URL, access_token) -dashboard = dashboard_frame(root, switch_frame, API_URL, access_token) -user_details = user_details_frame(root, switch_frame, API_URL, access_token) -user_orders = user_orders_frame(root, switch_frame, API_URL, access_token) -user_payments = user_payments_frame(root, switch_frame, API_URL, access_token) +# Collect all frames in a dictionary - start with unauthenticated frames frames = { "login": login, "register": register, - "create_shop": create_shop, - "create_product": product, - "view_product": view_product, - "category": category, - "view_shop": view_shop, - "dashboard": dashboard, - "user_details": user_details, - "user_orders": user_orders, - "user_payments": user_payments, } -for frame in frames.values(): - frame.place(relx=0, rely=0, relwidth=1, relheight=1) +# Place login and register frames +frames["login"].place(relx=0, rely=0, relwidth=1, relheight=1) +frames["register"].place(relx=0, rely=0, relwidth=1, relheight=1) + +# Create authenticated frames with empty token (they'll be recreated on login) +initialize_authenticated_frames(access_token) +# Start with login frame switch_frame("login") root.mainloop() diff --git a/app/frontend/utils/api_requests.py b/app/frontend/utils/api_requests.py index 5b3a0caa06b1b1c7c3f921d7cd767b7231c1c13a..16048686199cdc9a7567245d8ed5b141e28490b7 100644 --- a/app/frontend/utils/api_requests.py +++ b/app/frontend/utils/api_requests.py @@ -14,13 +14,19 @@ def login_api(email, password, api_url): :return: Tuple (status_code, response_data) """ try: + print(f"Attempting login for email: {email}") response = requests.post( f"{api_url}/auth/login", json={"email": email, "password": password} ) - response.raise_for_status() # Raise an error for 4xx and 5xx responses - return response.status_code, response.json() # Return status and response data + print(f"Login response status: {response.status_code}") + if response.status_code == 200: + data = response.json() + print(f"Login successful, token received: {data.get('access_token')[:10]}...") + return response.status_code, data + return response.status_code, response.json() except requests.exceptions.RequestException as e: - return None, {"error": str(e)} # Return error message if request fails + print(f"Login request error: {str(e)}") + return None, {"error": str(e)} # Register API