source: frontend/src/pages/Register.tsx@ 16aed54

main
Last change on this file since 16aed54 was 16aed54, checked in by Filip Gavrilovski <filipgavrilovski28@…>, 6 months ago

minor changes with error handling

  • Property mode set to 100644
File size: 5.4 KB
Line 
1import { useEffect, useState } from "react";
2import { useNavigate } from "react-router";
3import { toast } from "react-toastify";
4import axiosInstance, { scheduleTokenRefresh } from "../api/axiosInstance";
5import { useAuth } from "../context/authContext";
6import type { User } from "../utils/types";
7
8const Register = () => {
9 const { setUser } = useAuth();
10 const [username, setUsername] = useState("");
11 const [password, setPassword] = useState("");
12 const [fullname, setFullname] = useState("");
13 const [email, setEmail] = useState("");
14 const [profilePhotoFile, setProfilePhotoFile] = useState<File | null>(null);
15 const [previewProfilePhoto, setPreviewProfilePhoto] = useState<string | null>(
16 null,
17 );
18 const [error, setError] = useState<string | null>(null);
19
20 const navigate = useNavigate();
21
22 useEffect(() => {
23 return () => {
24 if (previewProfilePhoto) {
25 URL.revokeObjectURL(previewProfilePhoto);
26 }
27 };
28 }, [previewProfilePhoto]);
29
30 const handleRemoveFile = () => {
31 if (previewProfilePhoto) {
32 URL.revokeObjectURL(previewProfilePhoto);
33 }
34 setProfilePhotoFile(null);
35 setPreviewProfilePhoto(null);
36 };
37
38 const handleRegister = async (e: React.FormEvent) => {
39 e.preventDefault();
40 if (profilePhotoFile && profilePhotoFile.size > 5 * 1024 * 1024) {
41 alert("Max file size is 5MB");
42 return;
43 }
44
45 if (username === "" || password === "" || fullname === "" || email === "") {
46 setError("Please fill in all required fields.");
47 return;
48 }
49
50 if (profilePhotoFile && !profilePhotoFile.type.startsWith("image/")) {
51 alert("Only images allowed");
52 return;
53 }
54 const formData = new FormData();
55 if (profilePhotoFile) formData.append("profilePhoto", profilePhotoFile);
56 formData.append("username", username);
57 formData.append("fullname", fullname);
58 formData.append("email", email);
59 formData.append("password", password);
60
61 try {
62 const response = await axiosInstance.post<{
63 user: User;
64 tokenExpiresIn: number;
65 }>("/auth/register", formData);
66 scheduleTokenRefresh(response.data.tokenExpiresIn);
67 setUser(response.data.user);
68 navigate("/");
69 toast.success("Registration successful!");
70 } catch (error: any) {
71 const errorMessage = error.response?.data?.error || "Please try again.";
72 setError(`Registration failed: ${errorMessage}`);
73 }
74 };
75
76 return (
77 <div className="flex flex-col items-center justify-center min-h-[90vh] bg-gray-100">
78 <h2 className="text-2xl mb-4">Register</h2>
79 <form className="bg-white p-6 rounded shadow-md w-full max-w-md">
80 {error && <p className="text-red-500 mb-4">{error}</p>}
81 <div className="mb-4">
82 <label className="block text-gray-700 mb-2" htmlFor="username">
83 Username
84 </label>
85 <input
86 type="text"
87 id="username"
88 className="w-full p-2 border border-gray-300 rounded"
89 value={username}
90 onChange={(e) => setUsername(e.target.value)}
91 />
92 </div>
93 <div className="mb-4">
94 <label className="block text-gray-700 mb-2" htmlFor="password">
95 Password
96 </label>
97 <input
98 type="password"
99 id="password"
100 className="w-full p-2 border border-gray-300 rounded"
101 value={password}
102 onChange={(e) => setPassword(e.target.value)}
103 />
104 </div>
105 <div className="mb-4">
106 <label className="block text-gray-700 mb-2" htmlFor="fullName">
107 Full Name
108 </label>
109 <input
110 type="text"
111 id="fullName"
112 className="w-full p-2 border border-gray-300 rounded"
113 value={fullname}
114 onChange={(e) => setFullname(e.target.value)}
115 />
116 </div>
117 <div className="mb-4">
118 <label className="block text-gray-700 mb-2" htmlFor="email">
119 Email
120 </label>
121 <input
122 type="email"
123 id="email"
124 className="w-full p-2 border border-gray-300 rounded"
125 value={email}
126 onChange={(e) => setEmail(e.target.value)}
127 />
128 </div>
129 <div className="mb-4">
130 <label className="block text-gray-700 mb-2">Profile Photo</label>
131 <div className="flex items-center gap-3">
132 <label
133 htmlFor="profilePhoto"
134 className="px-4 py-2 bg-blue-500 text-white rounded cursor-pointer hover:bg-blue-600 transition-colors text-sm font-medium text-center"
135 >
136 Choose File
137 </label>
138 <span className="text-gray-600 text-sm flex-1">
139 {profilePhotoFile ? profilePhotoFile.name : "No file chosen"}
140 </span>
141 {profilePhotoFile && (
142 <button
143 type="button"
144 onClick={handleRemoveFile}
145 className="px-3 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition-colors text-sm font-medium"
146 >
147 Remove
148 </button>
149 )}
150 </div>
151 <input
152 type="file"
153 accept="image/*"
154 id="profilePhoto"
155 className="hidden"
156 onChange={(e) => {
157 if (e.target.files && e.target.files[0]) {
158 setProfilePhotoFile(e.target.files[0]);
159 setPreviewProfilePhoto(URL.createObjectURL(e.target.files[0]));
160 }
161 }}
162 />
163 </div>
164 {previewProfilePhoto && (
165 <div className="mb-4">
166 <p className="block text-gray-700 mb-2">Profile Photo Preview:</p>
167 <img
168 src={previewProfilePhoto}
169 alt="Profile Preview"
170 className="w-20 h-20 object-cover rounded-full"
171 />
172 </div>
173 )}
174 <button
175 type="submit"
176 className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600 cursor-pointer"
177 onClick={handleRegister}
178 >
179 Register
180 </button>
181 </form>
182 </div>
183 );
184};
185
186export default Register;
Note: See TracBrowser for help on using the repository browser.