diff --git a/DevElevate/Client/src/components/Auth/LoginRegister.tsx b/DevElevate/Client/src/components/Auth/LoginRegister.tsx index cb7acfdc..8dc8f528 100644 --- a/DevElevate/Client/src/components/Auth/LoginRegister.tsx +++ b/DevElevate/Client/src/components/Auth/LoginRegister.tsx @@ -8,7 +8,6 @@ import { Eye, EyeOff, User as UserIcon, - Shield, Mail, Lock, UserPlus, @@ -21,7 +20,6 @@ const LoginRegister: React.FC = () => { const navigate = useNavigate(); const [isLogin, setIsLogin] = useState(true); const [showPassword, setShowPassword] = useState(false); - const [role, setRole] = useState<"user" | "admin">("user"); const [formData, setFormData] = useState({ name: "", email: "", @@ -52,7 +50,6 @@ const LoginRegister: React.FC = () => { name: firebaseUser.displayName || "", email: firebaseUser.email || "", password: "", - role: role, }; const response = await fetch(`${baseUrl}/api/v1/auth/google`, { method: "POST", @@ -67,9 +64,9 @@ const LoginRegister: React.FC = () => { id: data.user.id, name: data.user.name, email: data.user.email, - role: data.user.role, + role: data.user.role, // Role determined by backend avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${data.user.name}`, - bio: data.user.bio, + bio: data.user.bio || "", socialLinks: {}, joinDate: new Date().toISOString(), lastLogin: new Date().toISOString(), @@ -118,7 +115,7 @@ const LoginRegister: React.FC = () => { dispatch({ type: "LOGIN_FAILURE", payload: - "Please use a valid email from gmail.com, outlook.com. To log in, verify your email via OTP using your original and active email address.", + "Please use a valid email from gmail.com, outlook.com, yahoo.com, hotmail.com, icloud.com, protonmail.com, or aol.com.", }); return; } @@ -130,7 +127,8 @@ const LoginRegister: React.FC = () => { try { if (isLogin) { - await login(formData.email, formData.password, role); + // Login without role; backend determines role based on email + await login(formData.email, formData.password); } else { if (state.otpPending) { await verifySignupOtp( @@ -138,16 +136,13 @@ const LoginRegister: React.FC = () => { otp.join("") ); } else { - await register( - formData.name, - formData.email, - formData.password, - role - ); + // Register as user only + await register(formData.name, formData.email, formData.password); } } } catch (error) { console.error("Auth error:", error); + dispatch({ type: "LOGIN_FAILURE", payload: "Authentication failed" }); } }; @@ -182,7 +177,10 @@ const LoginRegister: React.FC = () => { } }; - const handleOtpKeyDown = (e: React.KeyboardEvent, index: number) => { + const handleOtpKeyDown = ( + e: React.KeyboardEvent, + index: number + ) => { if (e.key === "Backspace" && !otp[index] && index > 0) { otpRefs.current[index - 1]?.focus(); } @@ -202,50 +200,10 @@ const LoginRegister: React.FC = () => {

{isLogin ? "Sign in to continue your learning journey" - : "Start your learning journey today"} + : "Create a user account to start learning"}

- {/* Role Toggle */} -
- -
- - - -
-
- {state.error && (
@@ -290,8 +248,8 @@ const LoginRegister: React.FC = () => { name="email" value={formData.email} onChange={handleInputChange} - pattern="^[a-zA-Z0-9._%+-]+@(gmail\.com|outlook\.com|yahoo\.com)$" - title="Only gmail.com, outlook.com, or yahoo.com emails are allowed" + pattern="^[a-zA-Z0-9._%+-]+@(gmail\.com|outlook\.com|yahoo\.com|hotmail\.com|icloud\.com|protonmail\.com|aol\.com)$" + title="Only gmail.com, outlook.com, yahoo.com, hotmail.com, icloud.com, protonmail.com, or aol.com emails are allowed" className="w-full py-3 pl-10 pr-4 text-gray-900 bg-white border border-gray-300 rounded-lg dark:border-gray-600 focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white" placeholder="Enter your email" required @@ -338,7 +296,6 @@ const LoginRegister: React.FC = () => { )}
)} - {!isLogin && (
)} - {isLogin && role === "admin" && ( -
-

- Admin Credentials: -

-

- Email: officialdevelevate@gmail.com -

-

- Password: Develevate@2025 -

+ {isLogin && ( +
+ {/* Swing wrapper (everything inside sways) */} +
+ {/* Right rope */} +
+
+
+
+ + {/* Hanging card */} +
+

+ 🔑 Admin Credentials +

+

+ Email:{" "} + + officialdevelevate@gmail.com + +

+

+ Password:{" "} + Develevate@2025 +

+
+
+ + {/* Shadow under everything */} +
)} @@ -412,8 +397,8 @@ const LoginRegister: React.FC = () => { {isLogin ? "Sign In" : state.otpPending - ? "Verify OTP" - : "Create Account"} + ? "Verify OTP" + : "Create User Account"} )} @@ -478,10 +463,16 @@ const LoginRegister: React.FC = () => { {isLogin ? "Sign Up" : "Sign In"}

+ {!isLogin && ( +

+ Note: Only user accounts can be created. Admin access is + restricted. +

+ )}
); }; -export default LoginRegister; \ No newline at end of file +export default LoginRegister; diff --git a/DevElevate/Client/src/contexts/AuthContext.tsx b/DevElevate/Client/src/contexts/AuthContext.tsx index 6415eebc..8dbe739b 100644 --- a/DevElevate/Client/src/contexts/AuthContext.tsx +++ b/DevElevate/Client/src/contexts/AuthContext.tsx @@ -177,14 +177,12 @@ const AuthContext = createContext<{ dispatch: React.Dispatch; login: ( email: string, - password: string, - role: "user" | "admin" + password: string ) => Promise; register: ( name: string, email: string, - password: string, - role: "user" | "admin" + password: string ) => Promise; verifySignupOtp: (email: string, otp: string) => Promise; logout: () => void; @@ -223,8 +221,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ const login = async ( email: string, - password: string, - role: "user" | "admin" + password: string ) => { dispatch({ type: "LOGIN_START" }); try { @@ -239,8 +236,8 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ }); const data = await response.json(); - - + + if (!response.ok) { throw new Error(data.message || "Login failed"); @@ -248,20 +245,13 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ // Backend returns real JWT token and user data if (data.token && data.user) { - // Check if the role matches what the user selected - if (data.user.role !== role) { - throw new Error( - `unauthozrized - expected role ${role}` - ); - } - const user: User = { id: data.user.id, name: data.user.name, email: data.user.email, role: data.user.role, avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${data.user.name}`, - bio:data.user.bio, + bio: data.user.bio, socialLinks: {}, joinDate: new Date().toISOString(), lastLogin: new Date().toISOString(), @@ -299,8 +289,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ const register = async ( name: string, email: string, - password: string, - role: "user" | "admin" + password: string ) => { dispatch({ type: "REGISTER_START" }); @@ -312,7 +301,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ name, email, password, role }), + body: JSON.stringify({ name, email, password, role: "user" }), }); const data = await response.json(); @@ -361,7 +350,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ email: data.user.email, role: data.user.role, avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${data.user.name}`, - bio:data.user.bio, + bio: data.user.bio, socialLinks: {}, joinDate: new Date().toISOString(), lastLogin: new Date().toISOString(), @@ -400,68 +389,49 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ localStorage.removeItem("devElevateAuth"); }; -const updateProfile = async (data: Partial) => { - if (!state.user) return; - + const updateProfile = async (data: Partial) => { + if (!state.user) return; - - try { - const response = await fetch(`${baseUrl}/api/v1/update-profile`, { - method: "POST", - credentials: "include", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }); - const updateData = await response.json(); - // merge properly - const updatedUser = { ...state.user, ...updateData }; - - // Update in localStorage - const savedUsers = JSON.parse( - localStorage.getItem("devElevateUsers") || "[]" - ); - const userIndex = savedUsers.findIndex( - (u: User) => u.id === state.user!.id - ); - if (userIndex !== -1) { - savedUsers[userIndex] = updatedUser; - localStorage.setItem("devElevateUsers", JSON.stringify(savedUsers)); - } - - // dispatch full updated user - dispatch({ type: "UPDATE_PROFILE", payload: updatedUser }); - } catch (error) { - console.error("Profile update failed:", error); - } -}; - - - const changePassword = async ( - currentPassword: string, - newPassword: string - ) => { try { - // Simulate API call - await new Promise((resolve) => setTimeout(resolve, 500)); + const response = await fetch(`${baseUrl}/api/v1/update-profile`, { + method: "POST", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); - // In real app, verify current password and update - if (currentPassword !== "password123") { - throw new Error("Current password is incorrect"); + const updateData = await response.json(); + + // merge properly + const updatedUser = { ...state.user, ...updateData }; + + // Update in localStorage + const savedUsers = JSON.parse( + localStorage.getItem("devElevateUsers") || "[]" + ); + const userIndex = savedUsers.findIndex( + (u: User) => u.id === state.user!.id + ); + if (userIndex !== -1) { + savedUsers[userIndex] = updatedUser; + localStorage.setItem("devElevateUsers", JSON.stringify(savedUsers)); } - dispatch({ type: "CHANGE_PASSWORD_SUCCESS" }); + // dispatch full updated user + dispatch({ type: "UPDATE_PROFILE", payload: updatedUser }); } catch (error) { - throw error; + console.error("Profile update failed:", error); } }; + const loadUsers = () => { const savedUsers = JSON.parse( localStorage.getItem("devElevateUsers") || "[]" @@ -504,7 +474,6 @@ const updateProfile = async (data: Partial) => { verifySignupOtp, logout, updateProfile, - changePassword, loadUsers, updateUser, deleteUser, @@ -521,4 +490,4 @@ export const useAuth = () => { throw new Error("useAuth must be used within an AuthProvider"); } return context; -}; +}; \ No newline at end of file diff --git a/DevElevate/Client/src/index.css b/DevElevate/Client/src/index.css index 4144a09f..5498a47e 100644 --- a/DevElevate/Client/src/index.css +++ b/DevElevate/Client/src/index.css @@ -91,3 +91,13 @@ section[id] { .custom-scrollbar.scrollbar-dark { scrollbar-color: #374151 #1f2937; } + +@keyframes sway { + 0% { transform: translate(-50%, 0) rotate(-2deg); } + 50% { transform: translate(-50%, 0) rotate(2deg); } + 100% { transform: translate(-50%, 0) rotate(-2deg); } +} + +.animate-sway { + animation: sway 4s ease-in-out infinite; +}