diff --git a/src/main/java/Team5/SmartTowns/users/NewUser.java b/src/main/java/Team5/SmartTowns/users/NewUser.java new file mode 100644 index 0000000000000000000000000000000000000000..087992044ea74b8ab544ec68855f0d4db35be95e --- /dev/null +++ b/src/main/java/Team5/SmartTowns/users/NewUser.java @@ -0,0 +1,22 @@ +package Team5.SmartTowns.users; + +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class NewUser { + + + @NotEmpty(message = "You must type in a username.") + String name; + + @NotEmpty(message = "You must type in a password.") + String password; + + @NotEmpty(message = "You must type in an email.") + String email; +} diff --git a/src/main/java/Team5/SmartTowns/users/UserController.java b/src/main/java/Team5/SmartTowns/users/UserController.java index a80f139bd0d2ee069f2116c463f3392e029793fe..c937643dd45702cee89ff822f9004cb2c024d97d 100644 --- a/src/main/java/Team5/SmartTowns/users/UserController.java +++ b/src/main/java/Team5/SmartTowns/users/UserController.java @@ -4,13 +4,15 @@ package Team5.SmartTowns.users; import Team5.SmartTowns.rewards.Pack; import Team5.SmartTowns.rewards.RewardsRepository; import Team5.SmartTowns.rewards.Sticker; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; +import java.util.Arrays; import java.util.List; @Controller @@ -26,17 +28,32 @@ public class UserController { @GetMapping("/login") public ModelAndView getLoginPage() { ModelAndView mav = new ModelAndView("users/login"); + mav.addObject("user", new NewUser( "", "", "")); return mav; } @PostMapping("/login/register") - public ModelAndView registerUser(String[] userInfo) { - /* TODO CHECK IS USER ALREADY EXISTS */ + public ModelAndView registerUser(@Valid @ModelAttribute("user") NewUser user, BindingResult bindingResult, Model model) { + ModelAndView mav = new ModelAndView("users/login", model.asMap()); + // TODO VALIDATE EMAIL INPUT - /* TODO CALL FUNCTION HERE TO CRATE USER IN THE DATABASE */ - - ModelAndView mav = new ModelAndView("users/login"); - return mav; + if (bindingResult.hasErrors()) { + System.out.println("Errors"); + } + System.out.println(user.getName()); + System.out.println(user.getPassword()); + + if ( userRepository.doesUserExist(user.getEmail()) ) { + //TODO return modelandview for user already exists + System.out.println(user.getEmail() + " already exists"); + System.out.print("LOG IN:"); + System.out.println(userRepository.userLogIn(user.email, user.password)); + return mav; + } else { + userRepository.addUser(user.name, user.email, user.password); + System.out.println(user.getEmail() + " created"); + return mav; + } } /* USER MAPPING & FUNCTIONS */ diff --git a/src/main/java/Team5/SmartTowns/users/UserRepository.java b/src/main/java/Team5/SmartTowns/users/UserRepository.java index 9fdf4bac8acf480e42ffdecdf21787dd6e68c8cd..f47b120a2c94fe5fa42fedc4a0b9ab20ea828f5e 100644 --- a/src/main/java/Team5/SmartTowns/users/UserRepository.java +++ b/src/main/java/Team5/SmartTowns/users/UserRepository.java @@ -10,4 +10,5 @@ public interface UserRepository { boolean unlockSticker(int userID, int packID, int stickerID); boolean addUser(String username, String email, String password); boolean doesUserExist(String email); + boolean userLogIn(String username, String password); } diff --git a/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java b/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java index 0866a8a104fabd7dea51df6db3df9c8dc759221a..492d7b1ac01c310ae4f76b87d2263b3b3a114884 100644 --- a/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java +++ b/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java @@ -12,6 +12,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.List; +import java.util.Objects; @Repository public class UserRepositoryJDBC implements UserRepository{ @@ -60,13 +61,20 @@ public class UserRepositoryJDBC implements UserRepository{ @Override public boolean addUser(String username, String email, String password){ - String query = "INSERT INTO users (name, email) VALUES (?,?)"; - jdbc.update(query, username, email); - return false; + String query = "INSERT INTO users (name, email, password) VALUES (?, ?, ?)"; + jdbc.update(query, username, email, password); + return true; } + @Override public boolean doesUserExist(String email){ String query = "SELECT COUNT(email) FROM users WHERE (email) = (?)"; return !(jdbc.queryForObject(query, Integer.class, email) == 0); } + @Override + public boolean userLogIn(String email, String password){ + String query = "SELECT (password) FROM users WHERE (email) = (?)"; + String dbpassword = jdbc.queryForObject(query, String.class, email); + return Objects.equals(dbpassword, password); + } } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 3962ef2f59f70a380b1af6684910d37aa8d04044..fdd080fadde0ee09d6c4d4938a45f3e6b11d4183 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,8 +1,8 @@ DELETE FROM users; -INSERT INTO users (id, email, name) VALUE (1, 'admin@gmail.com', 'Admin'); -INSERT INTO users (email, name) VALUE ('hannah@gmail.com', 'Hannah'); -INSERT INTO users (email, name) VALUE ('nigel@gmail.com', 'Nigel'); -INSERT INTO users (email, name) VALUE ('oscar@gmail.com', 'Nigel'); +INSERT INTO users (id, email, name, password) VALUE (1, 'admin@gmail.com', 'Admin', 'admin'); +INSERT INTO users (email, name, password) VALUE ('hannah@gmail.com', 'Hannah', 'root'); +INSERT INTO users (email, name, password) VALUE ('nigel@gmail.com', 'Nigel', 'root'); +INSERT INTO users (email, name, password) VALUE ('oscar@gmail.com', 'Oscar', 'root'); DELETE FROM trails; INSERT INTO trails ( Name) VALUE ( 'Caerphilly Coffee Trail'); diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 0a6a3f2b8bb1377dd81ecb4a50542f363b27507a..6fbb93a5ffb0aeb061192e76daffcae666472882 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -33,6 +33,7 @@ CREATE TABLE IF NOT EXISTS users ( id bigint auto_increment primary key, email varchar(128) NOT NULL , name varchar(30) NOT NULL, + password varchar(30) NOT NULL, dragonProgress int, dragonsLandmarkIDs longtext ); diff --git a/src/main/resources/static/css/login.css b/src/main/resources/static/css/login.css new file mode 100644 index 0000000000000000000000000000000000000000..995128966d52987ab335b78580951baab5022aa7 --- /dev/null +++ b/src/main/resources/static/css/login.css @@ -0,0 +1,365 @@ +@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap'); + +:root { + --container-colour: #2a2a2a; + --details-colour: var(--primary-light); + --details-light: #512da8; + + --font-buttons: 14px; + --font-size-1: 14px; + --font-size-2: 200px; + --font-size-3: 300px; +} + +*{ + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Montserrat', sans-serif; + color: white; +} + +body{ + align-items: center; + height: 100svh; +} + +main { + height: 90%; + width: 90%; + display: flex; + align-items: center; + justify-content: center; +} + +@keyframes move{ + 0%, 49.99%{ + opacity: 0; + z-index: 1; + } + 50%, 100%{ + opacity: 1; + z-index: 5; + } +} + +@media only screen +and (min-device-width: 650px) { + .container{ + background-color: var(--container-colour); + border-radius: 30px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.35); + position: relative; + overflow: hidden; + min-width: 768px; + max-width: 100%; + min-height: 480px; + } + + .container p{ + font-size: 14px; + line-height: 20px; + letter-spacing: 0.3px; + margin: 20px 0; + } + + + .container a{ + font-size: var(--font-size-1); + text-decoration: none; + margin: 15px 0 10px; + } + + .container button{ + background-color: var(--details-colour)/*#512da8*/; + color: #fff; + font-size: var(--font-buttons); + padding: 10px 45px; + border: 1px solid transparent; + border-radius: 8px; + font-weight: 600; + letter-spacing: 0.5px; + text-transform: uppercase; + margin-top: 10px; + cursor: pointer; + } + + .container button.hidden{ + background-color: transparent; + border-color: #fff; + } + + .container form{ + background-color: var(--container-colour); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + padding: 0 40px; + height: 100%; + } + + .container input{ + background-color: #eee; + border: none; + margin: 8px 0; + padding: 10px 15px; + font-size: 13px; + border-radius: 8px; + width: 100%; + outline: none; + } + + .form-container{ + position: absolute; + top: 0; + height: 100%; + transition: all 0.6s ease-in-out; + } + + .sign-in{ + left: 0; + width: 50%; + z-index: 2; + } + + .container.active .sign-in{ + transform: translateX(100%); + } + + .sign-up{ + left: 0; + width: 50%; + opacity: 0; + z-index: 1; + } + + .container.active .sign-up{ + transform: translateX(100%); + opacity: 1; + z-index: 5; + animation: move 0.6s; + } + + + + .toggle-container{ + position: absolute; + top: 0; + left: 50%; + width: 50%; + height: 100%; + overflow: hidden; + transition: all 0.6s ease-in-out; + border-radius: 150px 0 0 100px; + z-index: 1000; + } + + .container.active .toggle-container{ + transform: translateX(-100%); /*BG THING*/ + border-radius: 0 150px 100px 0; + } + + .toggle{ + background-color: var(--details-colour); + height: 100%; + background: linear-gradient(to right, var(--details-light), var(--details-colour)); + color: #fff; + position: relative; + left: -100%; + height: 100%; + width: 200%; + transform: translateX(0); + transition: all 0.6s ease-in-out; + } + + .container.active .toggle{ + transform: translateX(50%); + } + + .toggle-panel{ + position: absolute; + width: 50%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + padding: 0 30px; + text-align: center; + top: 0; + transform: translateX(0); + transition: all 0.6s ease-in-out; + } + + .toggle-left{ + transform: translateX(-200%); + } + + .container.active .toggle-left{ + transform: translateX(0); + } + + .toggle-right{ + right: 0; + transform: translateX(0); + } + + .container.active .toggle-right{ + transform: translateX(200%); + } + .input { + color: black; + } + +} +@media only screen +and (max-device-width: 640px) { + .container { + position: relative; + + background-color: var(--container-colour); + border-radius: 30px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.35); + + overflow: hidden; + + width: 100%; + height: 100%; + + display: flex; + flex-direction: column; + justify-content: space-evenly; + } + .form-container { + height: 100%; + transition: all 0.6s ease-in-out; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + .form-container form { + display: flex; + + flex-direction: column; + align-items: center; + font-size: 3em; + margin-inline: 5%; + } + .form-container input { + flex: 1 1; + font-size: 1em; + color: black; + margin-block: 5%; + padding: 2%; + border-radius: 30px; + } + + .form-container button { + flex: 1 1; + font-size: 1em; + } + .toggle-container { + position: absolute; + top: 0; + width: 100%; + height: 50%; + transition: all 0.6s ease-in-out; + overflow: hidden; + border-radius: 0 0 150px 150px; + z-index: 1000; + + } + .container.active .toggle-container{ + transform: translateY(100%); /*BG THING*/ + border-radius: 150px 150px 0 0; + } + .toggle { + position: absolute; + width: 100%; + height: 200%; + display: flex; + flex-direction: column-reverse; + align-items: center; + justify-content: space-evenly; + background: linear-gradient(to bottom, var(--details-light), var(--details-colour)); + /*background-color: red;*/ + } + .toggle-panel { + flex: 1 1; + width: 100%; + } + .container .toggle-right { + transform: translateY(0); + opacity: 1; + } + .container .toggle-left { + transform: translateY(-100%); + opacity: 0; + } + .container.active .toggle-right{ + transform: translateY(-100%); + opacity: 0; + } + .container.active .toggle-left{ + transform: translateY(-100%); + opacity: 1; + } + + .container .sign-up { + opacity: 0; + z-index: -1; + transform: translateY(100%); + } + .container .sign-in { + opacity: 1; + z-index: 5; + } + + .container.active .sign-in{ + transform: translateY(-100%); + opacity: 0; + z-index: -1; + } + .container.active .sign-up{ + animation: move 0.6s; + opacity: 1; + z-index: 5; + transform: translateY(0); + } + + + .toggle-panel { + transition: all 0.6s ease-in-out; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: xxx-large; + + } + .toggle-panel p { + margin-inline: 10%; + } + .container button{ + background-color: var(--details-colour)/*#512da8*/; + color: #fff; + font-size: xxx-large; + padding: 40px 40px; + border: 5px solid transparent; + border-radius: 14px; + font-weight: 600; + letter-spacing: 1px; + text-transform: uppercase; + margin-top: 30px; + cursor: pointer; + } + + .container button.hidden{ + background-color: transparent; + border-color: #fff; + } +} + + diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index e39ea2ad0a52a0073a365c88eab6dde15fbde7be..ca8e94887f7ff4975d6b8859f24f105b14a2ca8d 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -62,6 +62,11 @@ and (max-device-width: 640px) { width: 100vw; height: 100svh; } + html { + position: fixed; + width: 100vw; + height: 100svh; + } } /*PHONES LANDSCAPE*/ diff --git a/src/main/resources/static/css/userProfile2.css b/src/main/resources/static/css/userProfile2.css index adab43219b85ba98f0db7367490dd683a58bd9cc..0c5c0e9c4aef739bda2f4d38039fa4735a3e30bb 100644 --- a/src/main/resources/static/css/userProfile2.css +++ b/src/main/resources/static/css/userProfile2.css @@ -198,8 +198,14 @@ main { height: 17em; margin: 1.5em; } -/*}*/ +/* LOGIN FORM PAGE */ + + + + + +/* MEDIA TYPE UPDATES*/ @media only screen and (min-device-width: 320px) and (max-device-width: 640px) { @@ -267,4 +273,21 @@ and (min-device-width: 1000px) { .progressionContainer { height: 20svh; } -} \ No newline at end of file +} + +.container { + width: 500px; + margin-block: 5%; + background-color: var(--accent-colour); + border-radius: 10em; + display: flex; + flex-direction: column; + align-items: center; + & form { + margin-inline: auto; + margin-block: 5%; + } +} +.container-active { + background: black; +} diff --git a/src/main/resources/static/scripts/login.js b/src/main/resources/static/scripts/login.js index 38ef327134aa8ae03ddc44ddf796b2df139fc3bb..00acfc8589e5a0ccfce0499fb61e019f6fea2703 100644 --- a/src/main/resources/static/scripts/login.js +++ b/src/main/resources/static/scripts/login.js @@ -1,4 +1,4 @@ -let username = document.forms["loginForm"]["username"]; +/*let username = document.forms["loginForm"]["username"]; let password = document.forms["loginForm"]["password"]; let pattern = new RegExp("^[a-z0-9_-]{3,15}$"); @@ -46,6 +46,39 @@ function validateForm(){ return false; } //TODO SERVER SIDE VALIDATION AND CHECK AGAINST USERS DB TABLE -} +}*/ +/* +function createAccountPOST(){ + let data = { username: username.value, password: password.value} + console.log(username.value); + console.log(password.value); + $.ajax + ({ + type: "POST", + url: "http://localhost:8080/login/register", + dataType: 'json', + data: JSON.stringify(data), + success: function (){ + console.log('Success'); + }, + error: function (request, status, error) { + console.log("ERROR"); + console.log(request); + console.log(status); + console.log(error); + } + }); + return false; +}*/ + +const container = document.getElementById('container'); +const registerBtn = document.getElementById('register'); +const loginBtn = document.getElementById('login'); +registerBtn.addEventListener('click', () => { + container.classList.add("active"); +}); +loginBtn.addEventListener('click', () => { + container.classList.remove("active"); +}); \ No newline at end of file diff --git a/src/main/resources/templates/users/login.html b/src/main/resources/templates/users/login.html index 9c7aea8b67c0d18e2910b8c067ea576899fa6340..65fef847e12a53ce43f9d830efadeaf220d6ffae 100644 --- a/src/main/resources/templates/users/login.html +++ b/src/main/resources/templates/users/login.html @@ -3,41 +3,64 @@ <head> <meta charset="UTF-8"> <title>User Log In</title> - <link rel="stylesheet" th:href="@{/css/userProfile.css}"> + <link rel="stylesheet" th:href="@{/css/style.css}"> + <link rel="stylesheet" th:href="@{/css/login.css}"> + <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body> <header> - <ul class="footerBar"> - <li class="footerButton"><b>Home</b></li> - <li class="footerButton"><b>About</b></li> - <li class="footerButton"><b>Map</b></li> - <li class="footerButton"><b>Facilities</b></li> - <li class="footerButton"><b>Log In</b></li> - </ul> </header> -<main class="solidBg"> - <div class="loginWrapper"> - <form action="" onsubmit="return validateForm()" method="post" name="loginForm"> - <h2>Log In</h2> - <div class="label"> - <label for="username"><b>Username</b><br></label> - <div id="usernameInvalid" class="invalid-tooltip">Please fill out this field.</div> - </div> - <input type="text" name="username" id="username" placeholder="Enter Username" title="Username Input"> - <div class="label"> - <label for="password"><b>Password</b><br></label> - <div id="passwordInvalid" class="invalid-tooltip">Please fill out this field.</div> +<main> + <!--CODE FROM: https://www.youtube.com/watch?v=PlpM2LJWu-s&t=928s --> + <div class="container sign-in"> + <div class="container" id="container"> + <div class="form-container sign-up"> + <form th:object="${user}" action="#" th:action="@{/login/register}" th:method="POST" onsubmit=""> + <h1>Create Account</h1> + <label> + <input class="input" th:field="*{name}" type="text" placeholder="Name"> + </label> + <label> + <input class="input" th:field="*{email}" type="email" placeholder="Email"> + </label> + <label> + <input class="input" th:field="*{password}" type="password" placeholder="Password"> + </label> + <button type="submit" >Sign Up</button> + </form> </div> - <input type="password" id="password" name="password"> - <div id="invalidLogin">Username and/or Password incorrect. Please try again.</div> - <button type="submit"><b>Log In</b></button> - </form> - + <div class="form-container sign-in"> + <form th:object="${user}" action="#" th:action="@{/login/register}" th:method="POST" onsubmit=""> + <h1>Sign In</h1> + <label> + <input class="input" th:field="*{email}" type="email" placeholder="Email"> + </label> + <label> + <input class="input" th:field="*{password}" type="password" placeholder="Password"> + </label> + <a href="#" class="text">Forget Your Password?</a> + <button type="submit">Sign In</button> + </form> + </div> + <div class="toggle-container"> + <div class="toggle"> + <div class="toggle-panel toggle-left"> + <h1>Welcome Back!</h1> + <p>Enter your personal details and start tracking your landmarks!</p> + <button class="hidden" id="login">Sign In</button> + </div> + <div class="toggle-panel toggle-right"> + <h1>Hello, Welcome!</h1> + <p>Register with your personal details and start earning stickers!</p> + <button class="hidden" id="register">Sign Up</button> + </div> + </div> + </div> + </div> </div> - </main> diff --git a/src/main/resources/templates/users/register.html b/src/main/resources/templates/users/register.html new file mode 100644 index 0000000000000000000000000000000000000000..f753fe1ffa470d1b72e9420a0d5ee925bf1e0202 --- /dev/null +++ b/src/main/resources/templates/users/register.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title</title> +</head> +<body> + +<main> + <div class="container sign-in"> + <form action="#" th:action="@{/login/register}" method="post" name="loginForm" th:object="${user}"> + <h2>Log In</h2> + <div class="label"> + <label for="username"><b>Username</b><br></label> + <div th:style="${#fields.hasErrors('name') ? 'opacity: 1;' : 'opacity: 0;'}" id="usernameInvalid" class="invalid-tooltip">Please fill out this field.</div> + </div> + <input type="text" name="username" id="username" th:field="*{name}" placeholder="Enter Username" title="Username Input"> + + <div class="label"> + <label for="password"><b>Password</b><br></label> + <div th:style="${#fields.hasErrors('password') ? 'opacity: 1;' : 'opacity: 0;'}" id="passwordInvalid" class="invalid-tooltip">Please fill out this field.</div> + </div> + <input type="password" id="password" name="password" th:field="*{password}"> + <div id="invalidLogin">Username and/or Password incorrect. Please try again.</div> + <button type="submit" ><b>Log In</b></button> + </form> + </div> + <div class="container sign-up"></div> + + +</main> + + + + +</body> +</html> \ No newline at end of file