From 5053815436a511f962e9cd3bf2d0f370d0391709 Mon Sep 17 00:00:00 2001 From: nn2-minh <Nguyen12.Minh@live.uwe.ac.uk> Date: Thu, 6 Mar 2025 17:16:45 +0700 Subject: [PATCH] Add frontend for login and sign up --- app/core/config.py | 2 +- app/frontend/components/login.py | 80 ++++++++++-------- app/frontend/components/register.py | 125 +++++++++++++++------------- app/frontend/main.py | 104 ++++------------------- requirements.txt | Bin 959 -> 1996 bytes 5 files changed, 129 insertions(+), 182 deletions(-) diff --git a/app/core/config.py b/app/core/config.py index d61a265..4045933 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -6,7 +6,7 @@ class Settings(BaseSettings): app_name: str = "Shopping App" database_url: str # Ensure lowercase to match the .env key secret_key: str - debug: bool = True # Lowercase to match Python conventions + debug: bool = True model_config = SettingsConfigDict( env_file=str(Path(__file__).resolve().parent.parent / ".env"), diff --git a/app/frontend/components/login.py b/app/frontend/components/login.py index 34a1122..a52331b 100644 --- a/app/frontend/components/login.py +++ b/app/frontend/components/login.py @@ -1,39 +1,49 @@ import ttkbootstrap as tb from ttkbootstrap.constants import * from tkinter import messagebox +import requests # Import requests for API communication -# Function to handle login -def login(): - username = entry_username.get() - password = entry_password.get() - - if username == "admin" and password == "password": # Dummy credentials - messagebox.showinfo("Login Successful", "Welcome, Admin!") - else: - messagebox.showerror("Login Failed", "Invalid username or password.") - -# Create main window -root = tb.Window(themename="superhero") # Try different themes like "darkly", "minty", etc. -root.title("Login Form") -root.geometry("900x800") - -# Title Label -label_title = tb.Label(root, text="Login", font=("Helvetica", 18, "bold")) -label_title.pack(pady=10) - -# Username Entry -tb.Label(root, text="Username:").pack(pady=5) -entry_username = tb.Entry(root, bootstyle="info") -entry_username.pack(pady=5) - -# Password Entry -tb.Label(root, text="Password:").pack(pady=5) -entry_password = tb.Entry(root, bootstyle="info", show="*") # Masked input -entry_password.pack(pady=5) - -# Login Button -btn_login = tb.Button(root, text="Login", bootstyle="primary", command=login) -btn_login.pack(pady=15) - -# Run the GUI -root.mainloop() +def login_frame(parent, switch_func, api_url): # Added api_url parameter + frame = tb.Frame(parent) + + def login(): + email = entry_email.get() + password = entry_password.get() + + if not email or not password: + messagebox.showwarning("Input Error", "Both fields are required!") + return + + # Sending login request to backend + response = requests.post(f"{api_url}/auth/login", json={ + "email": email, + "password": password + }) + + try: + response_data = response.json() + if response.status_code == 200: + messagebox.showinfo("Login Successful", f"Welcome back, {email}!") + # TODO: Implement navigation after login (e.g., open dashboard) + else: + 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) + + tb.Label(frame, text="Email:").pack(pady=5) + entry_email = tb.Entry(frame, bootstyle="info") + entry_email.pack(pady=5) + + tb.Label(frame, text="Password:").pack(pady=5) + entry_password = tb.Entry(frame, bootstyle="info", show="*") + entry_password.pack(pady=5) + + btn_login = tb.Button(frame, text="Login", bootstyle="primary", command=login) + btn_login.pack(pady=15) + + tb.Button(frame, text="Don't have an account? Register", bootstyle="link", + command=lambda: switch_func("register")).pack() + + return frame diff --git a/app/frontend/components/register.py b/app/frontend/components/register.py index f682945..4f0b034 100644 --- a/app/frontend/components/register.py +++ b/app/frontend/components/register.py @@ -1,63 +1,70 @@ import ttkbootstrap as tb from ttkbootstrap.constants import * from tkinter import messagebox +import requests # Import requests for API communication -# Function to handle registration -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 - - # Dummy registration success message (Replace with DB logic) - messagebox.showinfo("Registration Successful", f"Welcome, {username}!") - -# Create main window -root = tb.Window(themename="superhero") # Change to "darkly", "superhero", etc. -root.title("Register") -root.geometry("900x800") - -# Title Label -label_title = tb.Label(root, text="Register", font=("Helvetica", 18, "bold")) -label_title.pack(pady=10) - -# Username Entry -tb.Label(root, text="Username:").pack(pady=5) -entry_username = tb.Entry(root, bootstyle="info") -entry_username.pack(pady=5) - -# Email Entry -tb.Label(root, text="Email:").pack(pady=5) -entry_email = tb.Entry(root, bootstyle="info") -entry_email.pack(pady=5) - -# Phone Entry -tb.Label(root, text="Phone Number:").pack(pady=5) -entry_phone = tb.Entry(root, bootstyle="info") -entry_phone.pack(pady=5) - -# Password Entry -tb.Label(root, text="Password:").pack(pady=5) -entry_password = tb.Entry(root, bootstyle="info", show="*") -entry_password.pack(pady=5) - -# Confirm Password Entry -tb.Label(root, text="Confirm Password:").pack(pady=5) -entry_confirm_password = tb.Entry(root, bootstyle="info", show="*") -entry_confirm_password.pack(pady=5) - -# Register Button -btn_register = tb.Button(root, text="Register", bootstyle="success", command=register) -btn_register.pack(pady=15) - -# Run the GUI -root.mainloop() +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", f"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/main.py b/app/frontend/main.py index ca21b73..680f7df 100644 --- a/app/frontend/main.py +++ b/app/frontend/main.py @@ -1,101 +1,31 @@ import ttkbootstrap as tb -from ttkbootstrap.constants import * -from tkinter import messagebox -import requests +from components.login import login_frame +from components.register import register_frame # Backend API URL API_URL = "http://127.0.0.1:8000" -# Function to handle registration -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 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 - - # Send 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}!") - else: - messagebox.showerror("Registration Failed", response_data.get("detail", "Unknown error")) - except requests.exceptions.JSONDecodeError: - messagebox.showerror("Registration Failed", f"Server returned non-JSON response: {response.text}") - -# Function to handle login -def login(): - email = entry_email.get() - password = entry_password.get() - - # Send login data to backend - response = requests.post(f"{API_URL}/auth/login", json={ - "email": email, - "password": password - }) - - if response.status_code == 200: - messagebox.showinfo("Login Successful", "Welcome!") - else: - messagebox.showerror("Login Failed", response.json().get("detail", "Invalid credentials")) +# Function to switch between login and register +def switch_frame(frame_name): + if frame_name == "login": + login.tkraise() + elif frame_name == "register": + register.tkraise() # Create main window -root = tb.Window(themename="superhero") # Change to "darkly", "superhero", etc. +root = tb.Window(themename="superhero") root.title("Shopping App") root.geometry("900x800") -# Title Label -label_title = tb.Label(root, text="Shopping App", font=("Helvetica", 18, "bold")) -label_title.pack(pady=10) - -# Username Entry -tb.Label(root, text="Username:").pack(pady=5) -entry_username = tb.Entry(root, bootstyle="info") -entry_username.pack(pady=5) - -# Email Entry -tb.Label(root, text="Email:").pack(pady=5) -entry_email = tb.Entry(root, bootstyle="info") -entry_email.pack(pady=5) - -# Phone Entry -tb.Label(root, text="Phone Number:").pack(pady=5) -entry_phone = tb.Entry(root, bootstyle="info") -entry_phone.pack(pady=5) - -# Password Entry -tb.Label(root, text="Password:").pack(pady=5) -entry_password = tb.Entry(root, bootstyle="info", show="*") -entry_password.pack(pady=5) - -# Confirm Password Entry -tb.Label(root, text="Confirm Password:").pack(pady=5) -entry_confirm_password = tb.Entry(root, bootstyle="info", show="*") -entry_confirm_password.pack(pady=5) +# Create Frames +login = login_frame(root, switch_frame, API_URL) +register = register_frame(root, switch_frame, API_URL) -# Register Button -btn_register = tb.Button(root, text="Register", bootstyle="success", command=register) -btn_register.pack(pady=15) +for frame in (login, register): + frame.place(relx=0, rely=0.2, relwidth=1, relheight=1) -# Login Button -btn_login = tb.Button(root, text="Login", bootstyle="primary", command=login) -btn_login.pack(pady=15) +# Show Login Frame First +switch_frame("login") # Run the GUI -root.mainloop() \ No newline at end of file +root.mainloop() diff --git a/requirements.txt b/requirements.txt index a9f5493b0494ce61126f5380e9f8c496c94b7327..274404719ffab8b40b176df1b46e4394eb5c7ed1 100644 GIT binary patch literal 1996 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-k;0J2 zP|Q%kP{~ljkin48kOwve<S#_1q=NNkGUPDCGn7GHlL8J=kjWr3LFz#m5{hXIi44Wy zFfV|H1;|cA2196QK-`V0N|zxS>`RboAk`pj4t8NWLlHwNLn=caLk>7xKq^2X0P-co zqznd-*&uO{E<*;8?;tM8U?^cIVJKio28Tf^*l!?xAaxMaVJb=(@)<y3g-8`<40;SE zU>CsDRDkt^^csQF1;qAD22dC#g5@CYg4mtOP{L5m01Br(hIDY;<ujB*QynO*Kp3LZ ziy@OCk0Fa8k--Sb6g>u0u<fAuFJj1MNMXolC}+rH&}9IbtP74ake@+zg4_f#%NLx2 zvKdMl3K)VJ5*gAMAijpFGJ=|!!cfXk#E=8F52PCuDloZ9hGK?7h8zY^oPbh99z!Za zG6N`tiWngFBBvEga2|l=AW(|PVkm~EB#^y^;9LfZeNav*0H=;(aBL@m?FZQnN^OwP z2Bn!CaH=hb%7WxU7~(@vT1{pEnF7fvMPOB+&@^Iz_yD8=67Hb<m<*SPxC4^z(becO zq%u@6lz?*zdKm#S8=@Dx`MS^?3$hIq*N`*>(gn$l5MNh9(*j6UGDAE{ngppe0+$33 z8v+<A8PXYY!KogUia_xUQe(&f3T;Tb1F7+4sALFc2xRa9`w<jI5Dc>s7Eik1)B`F3 z^BBtDY9aBCsWz9PlpzOPmO(-pQr>}7gD|LE1L*<TAIadz;0v`KlwTn7AX!kJEo3MK zm$S$|Fb0>I#^6*~#E{95%#Z<f4=AO8d<RJdFgZ|MKyqd_INgIn0%VIJ14un2w2B!r z7*ZKPX$F*!(iuRdXfD)LP%eVy<6?$9aQO@>p&=zaD6~N4KtdD}l8y{HU{`@ky-Kht zppXaIY0LmARUx?}mm!}a1#B<OO&~Q!3=qFT%0EyJ1C`~VkcX)<VSt1bD3m~@Q#L~q zxa<Li5+wZ<fNcf24U+00u7lNEkn|3+%Me_iLc#zd3-NzEG%bV5%wlkD1}fu0_Jc}B zLvRd0bd@q7=T%5u1u_|u(;y`TsCG$WFb2B`6cV8Dg2|VGV-S?*A@v4GuQ5{jTMn)z oKyeHTb5QvW$r~WQfz(3cx||`EAqiY&fNC*Nc)?VGTm-QT0P1)m+yDRo literal 959 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+Y@`?*8OEU8FY;BFePDss7%*=@|OU%hk zNi4}P0tp-G8E~Z~7MCOzWP-FA8k*`E8p1?$lXJkL272aP=|!ojc{!<IHyi31a%C7A zf;E`v8E|EkloTZA7p2<T8tQ@6g7_u*`8i;}o9UT=6;yzf8(HWXa%HCEC4xL)Xuy?O zQk;^Qm!4XbUs?=vj#p-0R-zGDfu1Q>Zeme(N`85sZf1#YK_y7kK+k~7H?b(Yv>-S! zEfpeS#Fd*;T9gB_!cfnME4Q+^Fh@5zKQAvexg@_x7ZwbbdLaMi7iATL0>xO*&_K`7 zl&c`IxHu;>3FLZnJrk~i%$%J3a$8$NLp?)11FnL~<N{FeLxRhQtDrI^F|Q;u8RT3; z13fdifNp9<Nl_v!(v3idBUI}ar<Rmt=B0yu2?_+5G4YUqGtx6M*E8Y@s7%jI%_{+` zGPKk)<O-<ttqczI0l5tn>JX3UrsS8T=9Pg24fG7Tpn|!jIVG6|iA5!#P%zLlGT;iR zjCAz%v9&eRGte{QDoQOZO)Z9)ZEU1x%vF?`oMCHgXsl<cX9DKvf+8(D6KsH?v7QlE zaYkxRPG(+uMq)0=GE+ShuHwARv@}pcHq<lLGvo>m^l{8d&PdIz1cirzp0Nd2abZqw zeo87hG7R*LjJb+S5{q(DOG;8f4l^;+GvF#I$xh18FDWi5N-O{w42lOXP;My#DKRwF zGXn84^U~u}D@s!HiZk=`ia}Y&5R_X=A+cu)3Sa}S(xRN4%p_xw?Z$crT%~21$@xWj zAY+U{Nwc&pCqKUc#5Dp%W_e;saz<Ka4k%lK(y9qpd1_K|esXpyC|nFp^$fTGoKYfn -- GitLab