diff --git a/build.gradle b/build.gradle index 8d359787ce8628dbb4933c80c53d4a6d91fad470..0bdcc02bb2a5367c58f9bb20ef3f61c21057242b 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.mariadb.jdbc:mariadb-java-client:2.1.2' - compileOnly 'org.projectlombok:lombok' + testImplementation 'junit:junit:4.13.1' + compileOnly 'org.projectlombok:lombok' testImplementation 'org.projectlombok:lombok:1.18.28' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' diff --git a/src/main/java/Team5/SmartTowns/Data/DatabaseController.java b/src/main/java/Team5/SmartTowns/Data/DatabaseController.java index 0eed9aca6601fa11eab58684cbf54bae225d5a35..0d38e955dd0a2e38e9585e5560d1cd4b6df83033 100644 --- a/src/main/java/Team5/SmartTowns/Data/DatabaseController.java +++ b/src/main/java/Team5/SmartTowns/Data/DatabaseController.java @@ -1,5 +1,7 @@ package Team5.SmartTowns.Data; +import Team5.SmartTowns.users.User; +import Team5.SmartTowns.users.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @@ -9,20 +11,13 @@ import java.util.*; @Controller public class DatabaseController { - @Autowired - private UserRepository userRepository; + @Autowired private locationRepository locationRepository; @Autowired private trailsRepository trailsRepository; - @GetMapping("/userList") - public ModelAndView userList() { - ModelAndView mav = new ModelAndView("towns/userData"); - List<user> users = userRepository.getAllUsers(); - mav.addObject("users", users); - return mav; - } + @GetMapping("/trailList") public ModelAndView trailList() { ModelAndView mav1 = new ModelAndView("towns/trailsData"); diff --git a/src/main/java/Team5/SmartTowns/Data/UserRepository.java b/src/main/java/Team5/SmartTowns/Data/UserRepository.java deleted file mode 100644 index 172591d6500927a341d6534dca6c34728965db06..0000000000000000000000000000000000000000 --- a/src/main/java/Team5/SmartTowns/Data/UserRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -//Holds users data repository -package Team5.SmartTowns.Data; - -import java.util.List; - -public interface UserRepository { - List<user> getAllUsers(); -} diff --git a/src/main/java/Team5/SmartTowns/Data/UserRepositoryJDBC.java b/src/main/java/Team5/SmartTowns/Data/UserRepositoryJDBC.java deleted file mode 100644 index 63e0bad0a8a3abf5f09c435a749066f51f7100fc..0000000000000000000000000000000000000000 --- a/src/main/java/Team5/SmartTowns/Data/UserRepositoryJDBC.java +++ /dev/null @@ -1,36 +0,0 @@ -//Implements the users repository using JDBC -package Team5.SmartTowns.Data; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -public class UserRepositoryJDBC implements UserRepository{ - - private JdbcTemplate jdbc; - private RowMapper<user> userMapper; - - public UserRepositoryJDBC(JdbcTemplate aJdbc){ - this.jdbc = aJdbc; - setuserMapper(); - } - - - private void setuserMapper(){ - userMapper = (rs, i) -> new user( - rs.getInt("userID"), - rs.getString("name") - ); - } - - - - @Override - public List<user> getAllUsers(){ - String sql= "SELECT * FROM users"; - return jdbc.query(sql, userMapper); - } -} diff --git a/src/main/java/Team5/SmartTowns/Data/user.java b/src/main/java/Team5/SmartTowns/Data/user.java deleted file mode 100644 index a40350ebc13621da4fb8a89f4e58d2ccb5be2568..0000000000000000000000000000000000000000 --- a/src/main/java/Team5/SmartTowns/Data/user.java +++ /dev/null @@ -1,12 +0,0 @@ -//Holds variable data for the users table -package Team5.SmartTowns.Data; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class user { - private int userId; - private String name; -} diff --git a/src/main/java/Team5/SmartTowns/rewards/BadgesRepository.java b/src/main/java/Team5/SmartTowns/rewards/BadgesRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..56b1019cbe016a7c687b5ac83973679e8f498eef --- /dev/null +++ b/src/main/java/Team5/SmartTowns/rewards/BadgesRepository.java @@ -0,0 +1,9 @@ +//Holds locations data repository +package Team5.SmartTowns.rewards; + +import java.util.List; + +public interface BadgesRepository { + List<Badge> getAllBadges(); +} + diff --git a/src/main/java/Team5/SmartTowns/rewards/BadgesRepositoryJDBC.java b/src/main/java/Team5/SmartTowns/rewards/BadgesRepositoryJDBC.java new file mode 100644 index 0000000000000000000000000000000000000000..7c22061e894a633bf9c047f69f575daf9366b493 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/rewards/BadgesRepositoryJDBC.java @@ -0,0 +1,33 @@ +//Implements the locations repository using JDBC +package Team5.SmartTowns.rewards; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class BadgesRepositoryJDBC implements BadgesRepository { + private JdbcTemplate jdbc; + private RowMapper<Badge> badgeMapper; + + public BadgesRepositoryJDBC(JdbcTemplate aJdbc) { + this.jdbc = aJdbc; + setBadgeMapper(); + } + private void setBadgeMapper(){ + badgeMapper = (rs, i) -> new Badge( + rs.getInt("badgeID"), + rs.getString("name"), + rs.getString("description"), + rs.getInt("difficulty") + ); + } + + @Override + public List<Badge> getAllBadges(){ + String sql= "SELECT * FROM badges"; + return jdbc.query(sql, badgeMapper); + } +} diff --git a/src/main/java/Team5/SmartTowns/rewards/Sticker.java b/src/main/java/Team5/SmartTowns/rewards/Sticker.java index 274091bdacbaf6199349956be041eb09e34074ff..4fa498c61e7d5748e7d6dcfc26c7c6442dfd8cc4 100644 --- a/src/main/java/Team5/SmartTowns/rewards/Sticker.java +++ b/src/main/java/Team5/SmartTowns/rewards/Sticker.java @@ -15,6 +15,7 @@ public class Sticker { String description; String imgPath; int rarity; //1-5 + boolean hasSticker; public Sticker(int id, String name, String description, int rarity) { this.id = id; @@ -33,6 +34,16 @@ public class Sticker { return imgFile.exists() ? imgPath : notFoundPath; } + public boolean hasSticker(){ + return hasSticker; + } + public void setVisibility(boolean hasSticker){ + this.hasSticker = hasSticker; + } + public String getVisibility(){ + return hasSticker? "" : "grayedOut"; + } + @Override public boolean equals(Object o) { diff --git a/src/main/java/Team5/SmartTowns/rewards/StickersRepository.java b/src/main/java/Team5/SmartTowns/rewards/StickersRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..869c2d07353a7aa090fb7fee79b2d44feacd9b12 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/rewards/StickersRepository.java @@ -0,0 +1,9 @@ +//Holds locations data repository +package Team5.SmartTowns.rewards; + +import java.util.List; + +public interface StickersRepository { + List<Sticker> getAllStickers(); +} + diff --git a/src/main/java/Team5/SmartTowns/rewards/StickersRepositoryJDBC.java b/src/main/java/Team5/SmartTowns/rewards/StickersRepositoryJDBC.java new file mode 100644 index 0000000000000000000000000000000000000000..64df71bc3da3eff327377798185111aee3be7867 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/rewards/StickersRepositoryJDBC.java @@ -0,0 +1,33 @@ +//Implements the locations repository using JDBC +package Team5.SmartTowns.rewards; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class StickersRepositoryJDBC implements StickersRepository { + private JdbcTemplate jdbc; + private RowMapper<Sticker> stickerMapper; + + public StickersRepositoryJDBC(JdbcTemplate aJdbc) { + this.jdbc = aJdbc; + setStickerMapper(); + } + private void setStickerMapper(){ + stickerMapper = (rs, i) -> new Sticker( + rs.getInt("stickerID"), + rs.getString("name"), + rs.getString("description"), + rs.getInt("rarity") + ); + } + + @Override + public List<Sticker> getAllStickers(){ + String sql= "SELECT * FROM stickers"; + return jdbc.query(sql, stickerMapper); + } +} diff --git a/src/main/java/Team5/SmartTowns/users/User.java b/src/main/java/Team5/SmartTowns/users/User.java index 667222393c481b9e2f573ca1773700a93cf7667d..84887664fbee090abb68281f28b5f2b0832af9ae 100644 --- a/src/main/java/Team5/SmartTowns/users/User.java +++ b/src/main/java/Team5/SmartTowns/users/User.java @@ -15,17 +15,20 @@ public class User { String email; //Validation would be done by email, since they will have that String name; String imgPath; + int dragonProgress; Map<Badge, Integer> badgeProgress = new HashMap<>(); // Demonstrates the progress towards a specific badge (0-100) Map<Sticker, Boolean> hasStickers = new HashMap<>(); // True if User has sticker (key) - public User(int id, String email, String name) { + public User(int id, String email, String name, int dragonProgress) { this.id = id; this.email = email; this.name = name; + this.dragonProgress = dragonProgress; imgPath = findImagePath(); } + private String findImagePath(){ /* Finds the image in the Path folder, if image is not found assigns default image */ String imgPath = "images/users/" + id + ".jpg"; diff --git a/src/main/java/Team5/SmartTowns/users/UserController.java b/src/main/java/Team5/SmartTowns/users/UserController.java index 0df6457070de7a7db2ff33576c569e20120be554..d635c8a401d85ef6d930142882d69b5528f38fd7 100644 --- a/src/main/java/Team5/SmartTowns/users/UserController.java +++ b/src/main/java/Team5/SmartTowns/users/UserController.java @@ -1,57 +1,62 @@ package Team5.SmartTowns.users; + import Team5.SmartTowns.rewards.Badge; +import Team5.SmartTowns.rewards.BadgesRepository; import Team5.SmartTowns.rewards.Sticker; +import Team5.SmartTowns.rewards.StickersRepository; +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.servlet.ModelAndView; import java.util.List; +import java.util.Map; @Controller public class UserController { - /* TEMPORARY USER LIST --- TODO REPLACE IT WITH DATABASE LIST*/ - static List<User> users = List.of( - new User(1, "johndoe@gmail.com", "Claire Redfield"), - new User(2, "johndoe@gmail.com", "Albert Wesker"), - new User(3, "johndoe@gmail.com", "Leon Kennedy"), - new User(4, "johndoe@gmail.com", "Jill Valentine") - ); - static List<Badge> badges = List.of( - new Badge(1, "Badge1", "Bage One is This", 1), - new Badge(2, "Badge1", "Bage One is This", 4), - new Badge(3, "Badge1", "Bage One is This", 4), - new Badge(4, "Badge1", "Bage One is This", 5), - new Badge(5, "Badge1", "Bage One is This", 5), - new Badge(46, "Badge1", "Bage One is This", 5), - new Badge(7, "Badge1", "Bage One is This", 2) - ); - - static List<Sticker> stickers = List.of( - new Sticker(1, "Sticker", "Sticker", 1), - new Sticker(2, "Sticker", "Sticker", 4), - new Sticker(3, "Sticker", "Sticker One is This", 4), - new Sticker(4, "Sticker", "Sticker One is This", 5), - new Sticker(5, "Sticker", "Sticker One is This", 5), - new Sticker(46, "Sticker", "Sticker One is This", 5), - new Sticker(7, "Sticker", "Sticker One is This", 2) - ); + @Autowired + private UserRepository userRepository; + @Autowired + private BadgesRepository badgesRepository; + @Autowired + private StickersRepository stickersRepository; + + @GetMapping("/login") + public ModelAndView getLoginPage() { + ModelAndView mav = new ModelAndView("rewards/login"); +// List<User> users = userRepository.getAllUsers(); +// mav.addObject("users", users); + return mav; + } + + @GetMapping("/userList") + public ModelAndView userList() { + ModelAndView mav = new ModelAndView("towns/userData"); + List<User> users = userRepository.getAllUsers(); + mav.addObject("users", users); + return mav; + } @GetMapping("/user/{id}") public ModelAndView getUserPage(@PathVariable int id) { ModelAndView mav = new ModelAndView("rewards/userProfile"); - users.stream() - .filter(user -> user.getId() == id) - .findFirst() //Convoluted way of finding the matching user to the id, probably easier to do a hashmap - .ifPresent(result -> mav.addObject("user", result)); + List<Badge> badges = badgesRepository.getAllBadges(); /*DEPRECATED FOR THE MOMENT*/ mav.addObject("badges", badges); - mav.addObject("stickers", stickers); + List<Sticker> allStickers = stickersRepository.getAllStickers(); + List<User> users = userRepository.getAllUsers(); + Map<Long, Boolean> userStickers = userRepository.getStickers(id); + + for (Long stickerID : userStickers.keySet()) { //Finds and updates visibility of stickers based on what the user has + allStickers.stream() + .filter(sticker -> sticker.getId()==stickerID) + .findFirst().ifPresent(sticker -> sticker.setVisibility(userStickers.get(stickerID))); + } + + mav.addObject("user", userRepository.getUser(id)); + mav.addObject("stickers", allStickers); return mav; } -// @GetMapping("/userProfile") -// public ModelAndView getUserPage(ModelAndView mav) { -// return mav; -// } } diff --git a/src/main/java/Team5/SmartTowns/users/UserRepository.java b/src/main/java/Team5/SmartTowns/users/UserRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..9f4e828f17c231179df1a9fcd6a87cc5c8cf75b6 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/users/UserRepository.java @@ -0,0 +1,12 @@ +//Holds users data repository +package Team5.SmartTowns.users; + +import java.util.List; +import java.util.Map; + +public interface UserRepository { + List<User> getAllUsers(); +// Map<Long, Integer> getBadgeProgress(int id); + Map<Long, Boolean> getStickers(int id); + User getUser(int id); +} diff --git a/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java b/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java new file mode 100644 index 0000000000000000000000000000000000000000..9b24461d06d300172d904272edf0cc20c51b99f0 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java @@ -0,0 +1,69 @@ +//Implements the users repository using JDBC +package Team5.SmartTowns.users; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Repository +public class UserRepositoryJDBC implements UserRepository{ + + private JdbcTemplate jdbc; + private RowMapper<User> userMapper; + + public UserRepositoryJDBC(JdbcTemplate aJdbc){ + this.jdbc = aJdbc; + setUserMapper(); + } + + + private void setUserMapper(){ + userMapper = (rs, i) -> new User( + rs.getInt("userID"), + rs.getString("email"), + rs.getString("name"), + rs.getInt("dragonProgress") + ); + } + + @Override + public List<User> getAllUsers(){ + String sql= "SELECT * FROM users"; + return jdbc.query(sql, userMapper); + } + + @Override + public User getUser(int id){ + String sql= "SELECT * FROM users WHERE userID="+id; + List<User> result = jdbc.query(sql, userMapper); + return result.get(0); + } + + @Override + public Map<Long, Boolean> getStickers(int id){ + String sql = "SELECT stickerID, hasSticker FROM stickerprogress WHERE userID=" + id; + List<Map<String, Object>> query = jdbc.queryForList(sql); + Map<Long, Boolean> progress = new HashMap<>(); + for (Map<String, Object> result : query) { + progress.put((Long)result.get("stickerID"), (boolean)result.get("hasSticker")); + } + return progress; + } + + +// @Override +// public Map<Long, Integer> getBadgeProgress(int id){ +// String sql = "SELECT badgeID, progress FROM badgeprogress WHERE userID=" + id; +// List<Map<String, Object>> query = jdbc.queryForList(sql); +// Map<Long, Integer> progress = new HashMap<>(); +// for (Map<String, Object> result : query) { +// progress.put((Long)result.get("badgeID"), (int)result.get("progress")); +// } +// return progress; +// } +} diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 13137e1537cdf71c3ea608325401c3e70c58cfa4..e40208ba343bab8aeb675e792a3566fd0e573028 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,6 +1,6 @@ delete from users; -insert into users (userID, Name) value ('1', 'Hannah'); -insert into users (userID, Name) value ('2', 'Nigel'); +insert into users (email, name, dragonProgress) value ('hannah@gmail.com', 'Hannah', '90'); +insert into users (userID, email, name, dragonProgress) value ('2', 'nigel@gmail.com', 'Nigel', '40'); delete from trails; insert into trails ( Name) value ( 'Caerphilly Coffee Trail'); @@ -30,5 +30,28 @@ insert into locations ( locationName , locationEmail,locationDescription,locatio insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'Black Vein Colliery Disaster','','Location description here','Risca',0201); -insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'The Esplanade','','Location description here','Penarth',0301); -insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'The Old Swimming Baths','','Location description here','Penarth',0301); +insert into locations (locationID, locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value (19, 'The Esplanade','','Location description here','Penarth',0301); +insert into locations (locationID, locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value (20, 'The Old Swimming Baths','','Location description here','Penarth',0301); +delete from badges; +insert into badges (name, description, difficulty) value ('TownConnoisseur', 'You know the town very well!', '2'); +insert into badges (name, description, difficulty) value ('TownRegular', 'You visited the town 3 days in a row!', '1'); +insert into badges (name, description, difficulty) value ('TownMaster', 'You visited the town 7 days in a row!', '1'); +insert into badges (name, description, difficulty) value ('TownRegular', 'You visited the town 3 days in a row!', '1'); +insert into badges (name, description, difficulty) value ('TownRegular', 'You visited the town 3 days in a row!', '1'); + +delete from stickers; +insert into stickers (name, description, rarity) value ('TownConnoisseur', 'You know the town very well!', '2'); +insert into stickers (name, description, rarity) value ('TownRegular', 'You visited the town 3 days in a row!', '1'); +insert into stickers (name, description, rarity) value ('TownMaster', 'You visited the town 7 days in a row!', '1'); +insert into stickers (name, description, rarity) value ('TownRegular', 'You visited the town 3 days in a row!', '1'); +insert into stickers (name, description, rarity) value ('TownRegular', 'You visited the town 3 days in a row!', '1'); + +delete from badgeprogress; +insert into badgeprogress (userID, badgeID, progress) value ('1', '1', '40'); +insert into badgeprogress (userID, badgeID, progress) value ('1', '2', '70'); +insert into badgeprogress (userID, badgeID, progress) value ('2', '2', '70'); + +delete from stickerprogress; +insert into stickerprogress (userID, stickerID, hasSticker) value ('1', '1', true); +insert into stickerprogress (userID, stickerID, hasSticker) value ('1', '3', true); +insert into stickerprogress (userID, stickerID, hasSticker) value ('2', '2', true); \ No newline at end of file diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index abb296cb3ddc14856259de8efe5819cabb19f505..92b8d4e0377d6ceeb58aef53b35e946670b6bc62 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -4,6 +4,7 @@ create table if not exists trails trailID bigint auto_increment primary key, name varchar(128) ) engine=InnoDB; + drop table if exists locations; create table if not exists locations @@ -15,9 +16,45 @@ create table if not exists locations locationPlace varchar(255), locationTrailID varchar(128) ) engine=InnoDB; + drop table if exists users; create table if not exists users ( userID bigint auto_increment primary key, - name varchar(128) + email varchar(128), + name varchar(128), + dragonProgress int +) engine=InnoDB; + +drop table if exists badges; +create table if not exists badges +( + badgeID bigint auto_increment primary key, + name varchar(128), + description varchar(128), + difficulty bigint +) engine=InnoDB; + +drop table if exists stickers; +create table if not exists stickers +( + stickerID bigint auto_increment primary key, + name varchar(128), + description varchar(128), + rarity bigint +) engine=InnoDB; + +drop table if exists badgeProgress; +create table if not exists badgeProgress +( + userID bigint, + badgeID bigint, + progress int /*0-100*/ +) engine=InnoDB; + +create table if not exists stickerProgress +( + userID bigint, + stickerID bigint, + hasSticker boolean /*Has sticker or not*/ ) engine=InnoDB; \ No newline at end of file diff --git a/src/main/resources/static/css/userProfile.css b/src/main/resources/static/css/userProfile.css index d59daa0689371b0adb2a204035b6a42d89dadab3..8ceb77b4e82d8c44f9aefdcf9c1f54493771e668 100644 --- a/src/main/resources/static/css/userProfile.css +++ b/src/main/resources/static/css/userProfile.css @@ -1,4 +1,17 @@ /* AUTHOR: Gabriel Copat*/ +@import url('https://fonts.googleapis.com/css2?family=MedievalSharp&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=MedievalSharp&display=swap'); + +/*COLOUR PALETTE*/ +* { + --main-colour: #e47201; + --secondary-colour: #e47201; + --accent-colour: #e47201; + --accent-border: #b25901; + + --accent-shadow: rgba(0, 0, 0, 0.7) 0 0.5em 1em -0.5em; +} + /*FONTS, TYPOGRAPHY & BACKGROUNDS*/ * { @@ -12,6 +25,10 @@ color: white; text-justify: inter-word; } + & label { + color: white; + } + } @media only screen and (max-device-width: 500px) { /*ADJUSTING FOR SMALLER SCREENS*/ @@ -22,8 +39,11 @@ } body { - background: linear-gradient(135deg, #9f74be, #3e126b); + background: linear-gradient(135deg, #f7e8fd, #9914d1); height: 100svh; + display: flex; + flex-direction: column; + justify-content: space-evenly; } main { background: linear-gradient(to bottom, #1e1e1e 10%, darkgoldenrod 50%, #1e1e1e 90%); @@ -35,9 +55,9 @@ main { margin-top: 6em; padding-inline: 1vw; box-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh max(1vw, 1em); -} +} .userInfo { display: flex; flex-direction: column; @@ -170,10 +190,12 @@ main { } & h1 { font-size: 3em; + font-family: 'MedievalSharp', cursive; + letter-spacing: 1vw; box-shadow: rgba(0, 0, 0, 0.7) 0 2vw 2vw -2vw; border-bottom: #36454F solid 2px; border-top: #36454F solid 2px; - margin-inline: 25%; + margin-inline: 15%; margin-bottom: 1%; } & .dragonContainer { @@ -227,4 +249,115 @@ header .footerButton { } header .footerButton:hover { background-color: #36454F; +} + +.grayedOut { + filter: grayscale(1); +} + +.solidBg { + background: #1e1e1e; + display: flex; +} +.loginWrapper { + margin-inline: auto; + margin-block: 5svh; + display: flex; + text-align: center; + justify-content: center; + background: rgba(196, 92, 239, 0.75); + padding: 2em; + flex: 0 0; + border-radius: 1vw; + box-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh max(1vw, 1em); + & h2 { + margin-left: 0; + margin-right: auto; + margin-bottom: 0.5em; + } + & form{ + margin-block: auto; + font-size: 3em; + display: flex; + flex-direction: column; + } + & label { + position: relative; + margin-top: 1em; + /*width: fit-content;*/ + font-size: 0.8em; + & b { + float: left; + } + & a { + position: absolute; + font-size: 0.4em; + right: 0.2em; + bottom: 0.2em; + height: fit-content; + } + + } + & input { + font-size: 0.6em; + height: 1.5em; + width: 40vw; + border-radius: 0.2em; + padding-inline: 0.4em; + border: transparent solid 0.1em; + margin-bottom: 1em; + } + & button { + font-size: 1em; + height: 2em; + width: 4em; + box-shadow: var(--accent-shadow); + margin:auto; + margin-top: 1em; + background-color: var(--accent-colour); + border: 0.1em solid var(--accent-border); + border-radius: 1em; + color: white; + + } + & button:hover{ + background-color: var(--accent-border); + border: 0.1em solid var(--accent-colour); + } +} +.label { + position: relative; +} +.invalid-tooltip { + color: red; + width: fit-content; + opacity: 0; + font-size: 0.6em; + text-shadow: red 0 0.2em 1em; + transition: 0.3s ease-in-out 1ms; + padding:0.1em; + position: absolute; + right: 0.2em; + bottom: 0.2em; + height: fit-content; +} +.invalid-field { + box-shadow: red 0 0 1em; + transition: 0.3s ease-in-out 1ms; +} +.valid-field { + box-shadow: #40ff00 0 0 1em; + border: #40ff00 solid 0.1em; + transition: 0.3s ease-in-out 1ms; +} + + +#invalidLogin { + color: red; + text-shadow: black 0 0.1em 0.2em; + width: auto; + height: auto; + font-size: 0.6em; + opacity: 0; + transition: 0.5s ease-in-out 1ms; } \ No newline at end of file diff --git a/src/main/resources/static/scripts/login.js b/src/main/resources/static/scripts/login.js new file mode 100644 index 0000000000000000000000000000000000000000..929b8b61e7a6f0f71a97937b2bdf2fcd83a5ce4f --- /dev/null +++ b/src/main/resources/static/scripts/login.js @@ -0,0 +1,49 @@ +let username = document.forms["loginForm"]["username"]; +let password = document.forms["loginForm"]["password"]; +let pattern = new RegExp("^[a-z0-9_-]{3,15}$"); + +username.addEventListener("input", validateUsername) +password.addEventListener("input", validatePassword) + +function validateUsername() { + if (!(username.value === "") && pattern.test(username.value)){ + username.classList.remove("invalid-field"); + username.classList.add("valid-field"); + document.getElementById(username.name+"Invalid").style.opacity = 0; + username.style.borderColor = "green"; + return true; + } else if( ! (username.classList.contains("invalid-field") ) ){ + username.classList.add("invalid-field"); + username.classList.remove("valid-field"); + document.getElementById(username.name+"Invalid").style.opacity = 1; + username.style.borderColor = "red"; + } + return false; +} +function validatePassword(){ + if (password.value === "") { + password.classList.add("invalid-field"); + password.classList.remove("valid-field"); + document.getElementById(password.name+"Invalid").style.opacity = 1; + password.style.borderColor = "red"; + return false; + } else if( ! (password.classList.contains("valid-field") ) ) { + password.classList.remove("invalid-field"); + password.classList.add("valid-field"); + document.getElementById(password.name+"Invalid").style.opacity = 0; + password.style.borderColor = "green"; + } + return true; +} + +function validateForm(){ + if (validateUsername() & validatePassword()) { //Using just & so it checks both, even if the first is false (it applies the style) + console.log("VALID"); + return false; + } else { + console.log("Invalid"); + document.getElementById("invalidLogin").style.opacity = 1; + return false; + } + //TODO SERVER SIDE VALIDATION AND CHECK AGAINST USERS DB TABLE +} \ No newline at end of file diff --git a/src/main/resources/templates/rewards/login.html b/src/main/resources/templates/rewards/login.html new file mode 100644 index 0000000000000000000000000000000000000000..9c7aea8b67c0d18e2910b8c067ea576899fa6340 --- /dev/null +++ b/src/main/resources/templates/rewards/login.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>User Log In</title> + <link rel="stylesheet" th:href="@{/css/userProfile.css}"> + +</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> + </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> + +</main> + + + +<script type="text/javascript" th:src="@{scripts/login.js}"></script> + +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/rewards/userProfile.html b/src/main/resources/templates/rewards/userProfile.html index bd502cfc61f804533f7195de53ef812187d3f0ec..a193bf7614603637614dd0cfdc0974fdd8c44767 100644 --- a/src/main/resources/templates/rewards/userProfile.html +++ b/src/main/resources/templates/rewards/userProfile.html @@ -14,7 +14,7 @@ <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>Search</b></li> + <li class="footerButton"><b>Log In</b></li> </ul> </header> @@ -37,9 +37,9 @@ </div> </article> <article class="dragonProgression"> - <h1>The Dragon Trail</h1> + <h1>The Dragon's Tale</h1> <div class="dragonContainer"> - <div class="dragonFill"> + <div class="dragonFill" th:style="'width:'+ ${user.getDragonProgress()} + '%;'"> <img th:src="@{/images/rewards/dragonFilled.png}" alt="Filled Dragon" id="FilledDragon" class="dragonImg"> </div> @@ -48,12 +48,12 @@ alt="Outline Dragon" id="OutlineDragon" class="dragonImg"> </div> </div> - <h2>40%</h2> + <h2 th:text="${user.getDragonProgress()} + '%'"></h2> </article> <article id="stickersBox"> <!--Need a controller to show earned stickers --> <h2> STICKERS! </h2> <div class="stickersContainer"> - <img class="stickerImg" th:each="sticker : ${stickers}" th:src="@{'../' + ${sticker.getImgPath()}}" + <img th:class="'stickerImg ' + ${sticker.getVisibility()}" th:each="sticker : ${stickers}" th:src="@{'../' + ${sticker.getImgPath()}}" th:id="'img' + ${sticker.getId()}" th:alt="${sticker.getName()}" > </div> </article> @@ -67,10 +67,7 @@ </footer> - +<script> +</script> </body> -</html> - -<!--TODO finished doing the tooltips, need to add some more changes to them for sure - TODO afterwards probably need to implement thymeleaf so it shows badges based on the list - TODO implement some placeholder pictures as well for the badges and for the stickers --> \ No newline at end of file +</html> \ No newline at end of file