source: frontend/src/pages/Register.tsx@ 7621d7b

main
Last change on this file since 7621d7b was 3de009b, checked in by Filip Gavrilovski <filipgavrilovski28@…>, 5 months ago

enable registering as listener and artist

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