-
Login
- {error &&
{error}
}
+
+
+
Login
-
+ {error &&
{error}
}
+
+
+
+
+ Need an account? Register here
+
+
- )
-}
\ No newline at end of file
+ );
+}
diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx
index e69de29..3d46424 100644
--- a/frontend/src/pages/Register.jsx
+++ b/frontend/src/pages/Register.jsx
@@ -0,0 +1,110 @@
+import { useContext, useEffect, useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { loginRequest, registerRequest } from "../api/auth";
+import { checkIfUserExists } from "../api/users";
+import { AuthContext } from "../context/AuthContext";
+
+import "../styles/Register.css";
+
+export default function Register() {
+ const navigate = useNavigate();
+ const { login } = useContext(AuthContext);
+
+ const [name, setName] = useState("");
+ const [username, setUsername] = useState("");
+ const [userExists, setUserExists] = useState(false);
+ const [password, setPassword] = useState("");
+ const [passwordMatches, setPasswordMatches] = useState(true);
+ const [confirm, setConfirm] = useState("");
+ const [error, setError] = useState("");
+ const [success, setSuccess] = useState("");
+
+
+ useEffect(() => { checkIfUserExistsHandler(); }, [username]);
+ async function checkIfUserExistsHandler() {
+ setUserExists((await checkIfUserExists(username)).data);
+ }
+
+ useEffect(() => { setError(userExists ? `Username '${username}' already taken` : ""); }, [userExists]);
+
+ useEffect(() => {
+ setPasswordMatches(
+ !password ||
+ !confirm ||
+ password === confirm
+ );
+ }, [password, confirm]);
+
+ useEffect(() => { setError(passwordMatches ? "" : "Passwords are not matching"); }, [passwordMatches]);
+
+
+
+ const submit = async (e) => {
+ e.preventDefault();
+ setError("");
+ setSuccess("");
+
+ try {
+ await registerRequest(username, password, name);
+ console.log("Registered user:", username);
+ const data = await loginRequest(username, password);
+ console.log(data);
+ login(data);
+ setSuccess("Account created! Redirecting the grocery list...");
+ setTimeout(() => navigate("/"), 2000);
+ } catch (err) {
+ setError(err.response?.data?.message || "Registration failed");
+ setTimeout(() => {
+ setError("");
+ }, 1000);
+ }
+ };
+
+
+ return (
+
+
Register
+
+ {
{error}
}
+ {success &&
{success}
}
+
+
+
+
+ Already have an account? Login here
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/styles/Login.css b/frontend/src/styles/Login.css
new file mode 100644
index 0000000..034b067
--- /dev/null
+++ b/frontend/src/styles/Login.css
@@ -0,0 +1,70 @@
+.login-wrapper {
+ font-family: Arial, sans-serif;
+ padding: 1em;
+ background: #f8f9fa;
+ min-height: 100vh;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.login-box {
+ width: 100%;
+ max-width: 360px;
+ background: white;
+ padding: 1.5em;
+ border-radius: 8px;
+ box-shadow: 0 0 10px rgba(0,0,0,0.12);
+}
+
+.login-title {
+ text-align: center;
+ font-size: 1.6em;
+ margin-bottom: 1em;
+}
+
+.login-input {
+ width: 100%;
+ padding: 0.6em;
+ margin: 0.4em 0;
+ font-size: 1em;
+ border-radius: 4px;
+ border: 1px solid #ccc;
+}
+
+.login-button {
+ width: 100%;
+ padding: 0.7em;
+ margin-top: 0.6em;
+ background: #007bff;
+ border: none;
+ color: white;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 1em;
+}
+
+.login-button:hover {
+ background: #0068d1;
+}
+
+.login-error {
+ color: red;
+ text-align: center;
+ margin-bottom: 0.6em;
+}
+
+.login-register {
+ text-align: center;
+ margin-top: 1em;
+}
+
+.login-register a {
+ color: #007bff;
+ text-decoration: none;
+}
+
+.login-register a:hover {
+ text-decoration: underline;
+}
diff --git a/frontend/src/styles/Register.css b/frontend/src/styles/Register.css
new file mode 100644
index 0000000..10a57d0
--- /dev/null
+++ b/frontend/src/styles/Register.css
@@ -0,0 +1,84 @@
+.register-container {
+ max-width: 400px;
+ margin: 50px auto;
+ padding: 2rem;
+ border-radius: 12px;
+ background: #ffffff;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+ font-family: Arial, sans-serif;
+}
+
+.register-container h1 {
+ text-align: center;
+ margin-bottom: 1.5rem;
+ font-size: 1.8rem;
+ font-weight: bold;
+}
+
+.register-form {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.register-form input {
+ padding: 12px 14px;
+ border: 1px solid #ccc;
+ border-radius: 8px;
+ font-size: 1rem;
+ outline: none;
+ transition: border-color 0.2s ease;
+}
+
+.register-form input:focus {
+ border-color: #0077ff;
+}
+
+.register-form button {
+ padding: 12px;
+ border: none;
+ background: #0077ff;
+ color: white;
+ font-size: 1rem;
+ border-radius: 8px;
+ cursor: pointer;
+ margin-top: 10px;
+ transition: background 0.2s ease;
+}
+
+.register-form button:hover:not(:disabled) {
+ background: #005fcc;
+}
+
+.register-form button:disabled {
+ background: #a8a8a8;
+ cursor: not-allowed;
+}
+
+.error-message {
+ height: 15px;
+ color: red;
+ text-align: center;
+ margin-bottom: 10px;
+}
+
+.success-message {
+ color: green;
+ text-align: center;
+ margin-bottom: 10px;
+}
+
+.register-link {
+ text-align: center;
+ margin-top: 1rem;
+}
+
+.register-link a {
+ color: #0077ff;
+ text-decoration: none;
+ font-weight: bold;
+}
+
+.register-link a:hover {
+ text-decoration: underline;
+}