Changeset 484dc3f for frontend/src
- Timestamp:
- 01/26/26 19:03:37 (6 months ago)
- Branches:
- main
- Children:
- d47e225
- Parents:
- 3727852
- Location:
- frontend/src
- Files:
-
- 4 edited
-
Login.tsx (modified) (1 diff)
-
Nav.tsx (modified) (3 diffs)
-
Register.tsx (modified) (3 diffs)
-
api/axiosInstance.ts (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/Login.tsx
r3727852 r484dc3f 20 20 }>("/auth/login", { username, password }); 21 21 scheduleTokenRefresh(response.data.tokenExpiresIn); 22 setUser({ 23 username: response.data.user.username, 24 fullName: "demo", 25 email: "demo@demo.com", 26 role: response.data.user.role, 27 }); 22 setUser(response.data.user); 28 23 navigate("/"); 29 24 } catch (error) { -
frontend/src/Nav.tsx
r3727852 r484dc3f 1 1 import { Link } from "react-router-dom"; 2 import axiosInstance from "./api/axiosInstance";2 import axiosInstance, { baseURL } from "./api/axiosInstance"; 3 3 import Logo from "./assets/logo-finkwave.png"; 4 4 import { useAuth } from "./context/authContext"; … … 29 29 <div className="flex items-center space-x-3"> 30 30 <div className="flex items-center space-x-2 bg-slate-700 rounded-lg px-3 py-2"> 31 <div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center"> 32 <span className="text-white text-sm font-semibold"> 33 {user.username.charAt(0).toUpperCase()} 34 </span> 35 </div> 31 {user.profilePhoto ? ( 32 <img 33 src={`${baseURL}/${user.profilePhoto}`} 34 alt={`${user.username}'s profile`} 35 className="w-8 h-8 rounded-full object-cover" 36 /> 37 ) : ( 38 <div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center"> 39 <span className="text-white text-sm font-semibold"> 40 {user.username.charAt(0).toUpperCase()} 41 </span> 42 </div> 43 )} 44 36 45 <div className="text-white"> 37 46 <p className="text-sm font-medium">{user.username}</p> … … 42 51 </div> 43 52 <button 44 onClick={(e: React.MouseEvent<HTMLButtonElement>) => 45 handleLogout(e) 46 } 53 onClick={handleLogout} 47 54 className="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg text-sm 48 55 font-medium transition-colors duration-200 flex items-center space-x-1 cursor-pointer" -
frontend/src/Register.tsx
r3727852 r484dc3f 1 import { use State } from "react";1 import { useEffect, useState } from "react"; 2 2 import { useNavigate } from "react-router"; 3 3 import axiosInstance, { scheduleTokenRefresh } from "./api/axiosInstance"; … … 11 11 const [fullname, setFullname] = useState(""); 12 12 const [email, setEmail] = useState(""); 13 const [profilePhoto, setProfilePhoto] = useState<string | null>(null); 13 const [profilePhotoFile, setProfilePhotoFile] = useState<File | null>(null); 14 const [previewProfilePhoto, setPreviewProfilePhoto] = useState<string | null>( 15 null, 16 ); 14 17 15 18 const navigate = useNavigate(); 16 19 20 useEffect(() => { 21 return () => { 22 if (previewProfilePhoto) { 23 URL.revokeObjectURL(previewProfilePhoto); 24 } 25 }; 26 }, [previewProfilePhoto]); 27 17 28 const handleRegister = async (e: React.FormEvent) => { 18 29 e.preventDefault(); 30 // todo: add proper error handling 31 if (profilePhotoFile && profilePhotoFile.size > 5 * 1024 * 1024) { 32 alert("Max file size is 5MB"); 33 return; 34 } 35 36 if (profilePhotoFile && !profilePhotoFile.type.startsWith("image/")) { 37 alert("Only images allowed"); 38 return; 39 } 40 const formData = new FormData(); 41 if (profilePhotoFile) formData.append("profilePhoto", profilePhotoFile); 42 formData.append("username", username); 43 formData.append("fullname", fullname); 44 formData.append("email", email); 45 formData.append("password", password); 46 19 47 try { 20 48 const response = await axiosInstance.post<{ 21 49 user: User; 22 50 tokenExpiresIn: number; 23 }>("/auth/register", { 24 username, 25 fullname, 26 email, 27 password, 28 profilePhoto, 29 }); 51 }>("/auth/register", formData); 30 52 scheduleTokenRefresh(response.data.tokenExpiresIn); 31 53 setUser(response.data.user); … … 89 111 </div> 90 112 <div className="mb-4"> 91 <label className="block text-gray-700 mb-2" htmlFor="profilePhoto"> 92 Profile Photo URL 93 </label> 113 <label className="block text-gray-700 mb-2">Profile Photo</label> 114 <div className="flex items-center gap-3"> 115 <label 116 htmlFor="profilePhoto" 117 className="px-4 py-2 bg-blue-500 text-white rounded cursor-pointer hover:bg-blue-600 transition-colors text-sm font-medium" 118 > 119 Choose File 120 </label> 121 <span className="text-gray-600 text-sm"> 122 {profilePhotoFile ? profilePhotoFile.name : "No file chosen"} 123 </span> 124 </div> 94 125 <input 95 placeholder="todo"96 type="text"126 type="file" 127 accept="image/*" 97 128 id="profilePhoto" 98 className="w-full p-2 border border-gray-300 rounded" 99 value={profilePhoto || ""} 100 onChange={(e) => setProfilePhoto(e.target.value)} 129 className="hidden" 130 onChange={(e) => { 131 if (e.target.files && e.target.files[0]) { 132 setProfilePhotoFile(e.target.files[0]); 133 setPreviewProfilePhoto(URL.createObjectURL(e.target.files[0])); 134 } 135 }} 101 136 /> 102 137 </div> 138 {previewProfilePhoto && ( 139 <div className="mb-4"> 140 <p className="block text-gray-700 mb-2">Profile Photo Preview:</p> 141 <img 142 src={previewProfilePhoto} 143 alt="Profile Preview" 144 className="w-20 h-20 object-cover rounded-full" 145 /> 146 </div> 147 )} 103 148 <button 104 149 type="submit" 105 150 className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600 cursor-pointer" 106 onClick={(e: React.MouseEvent<HTMLButtonElement>) => 107 handleRegister(e) 108 } 151 onClick={handleRegister} 109 152 > 110 153 Register -
frontend/src/api/axiosInstance.ts
r3727852 r484dc3f 5 5 export const axiosInstance = axios.create({ 6 6 baseURL, 7 headers: {8 "Content-Type": "application/json",9 },10 7 withCredentials: true, 8 }); 9 10 // set content-type to application/json unless sending FormData 11 axiosInstance.interceptors.request.use((config) => { 12 if (!(config.data instanceof FormData)) { 13 config.headers["Content-Type"] = "application/json"; 14 } 15 return config; 11 16 }); 12 17 … … 23 28 export const scheduleTokenRefresh = (expiryTimeSeconds: number) => { 24 29 clearRefreshTimeout(); 30 31 if (!expiryTimeSeconds || expiryTimeSeconds <= 0) { 32 return; 33 } 25 34 26 35 const refreshTime = (expiryTimeSeconds - 60) * 1000; … … 44 53 scheduleTokenRefresh(refreshResponse.data.tokenExpiresIn); 45 54 } catch (err) { 55 clearRefreshTimeout(); 46 56 console.error(err); 47 57 throw err; … … 53 63 }; 54 64 55 axios .interceptors.response.use(65 axiosInstance.interceptors.response.use( 56 66 (response) => response, 57 67 async (error) => {
Note:
See TracChangeset
for help on using the changeset viewer.
