source: frontend/src/pages/Register.tsx@ b70fbeb

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

add toasts on frontend

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