diff --git a/src/main/java/Team5/SmartTowns/rewards/Badge.java b/src/main/java/Team5/SmartTowns/rewards/Badge.java new file mode 100644 index 0000000000000000000000000000000000000000..25c231e14e3f0afdd4ecfc2d6e9f352925224097 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/rewards/Badge.java @@ -0,0 +1,51 @@ +/*AUTHOR: Gabriel Copat*/ +package Team5.SmartTowns.rewards; + +import lombok.Data; + +import java.io.File; +import java.util.Objects; + +@Data +public class Badge { + /* Badges can be earned by completing certain goals. + * They are displayed in the user profile page + * + * For example, one might earn a badge after visiting 20 locations */ + + int id; + String name; + String description; + String imgPath; + int difficulty; //1-5 + + public Badge(int id, String name, String description, int difficulty) { + this.id = id; + this.name = name; + this.description = description; + this.difficulty = difficulty; + imgPath = findImagePath(); + } + + private String findImagePath(){ + /* Finds the image in the Path folder, if image is not found assigns default image */ + String imgPath = "images/rewards/badges/" + id + ".jpg"; + String notFoundPath = "/images/rewards/badges/0.png"; + + File imgFile = new File("src/main/resources/static/" + imgPath); + return imgFile.exists() ? imgPath : notFoundPath; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Badge badge = (Badge) o; + return id == badge.id && Objects.equals(name, badge.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } +} diff --git a/src/main/java/Team5/SmartTowns/rewards/RewardsController.java b/src/main/java/Team5/SmartTowns/rewards/RewardsController.java new file mode 100644 index 0000000000000000000000000000000000000000..af9d33cba34dd12ac6aa5ca1e54320b8124af9d4 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/rewards/RewardsController.java @@ -0,0 +1,7 @@ +package Team5.SmartTowns.rewards; + +import org.springframework.stereotype.Controller; + +@Controller +public class RewardsController { +} diff --git a/src/main/java/Team5/SmartTowns/rewards/Sticker.java b/src/main/java/Team5/SmartTowns/rewards/Sticker.java new file mode 100644 index 0000000000000000000000000000000000000000..274091bdacbaf6199349956be041eb09e34074ff --- /dev/null +++ b/src/main/java/Team5/SmartTowns/rewards/Sticker.java @@ -0,0 +1,50 @@ +/*AUTHOR: Gabriel Copat*/ +package Team5.SmartTowns.rewards; + +import lombok.Data; + +import java.io.File; +import java.util.Objects; + +@Data +public class Sticker { + /* Stickers are trade-able rewards, they vary in rarity and are earned at random */ + + int id; + String name; + String description; + String imgPath; + int rarity; //1-5 + + public Sticker(int id, String name, String description, int rarity) { + this.id = id; + this.name = name; + this.description = description; + this.rarity = rarity; + imgPath = findImagePath(); + } + + private String findImagePath(){ + /* Finds the image in the Path folder, if image is not found assigns default image */ + String imgPath = "images/rewards/stickers/" + id + ".jpg"; + String notFoundPath = "images/rewards/stickers/0.png"; + + File imgFile = new File("src/main/resources/static/" + imgPath); + return imgFile.exists() ? imgPath : notFoundPath; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Sticker sticker = (Sticker) o; + return id == sticker.id && Objects.equals(name, sticker.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } +} + diff --git a/src/main/java/Team5/SmartTowns/users/User.java b/src/main/java/Team5/SmartTowns/users/User.java new file mode 100644 index 0000000000000000000000000000000000000000..667222393c481b9e2f573ca1773700a93cf7667d --- /dev/null +++ b/src/main/java/Team5/SmartTowns/users/User.java @@ -0,0 +1,37 @@ +package Team5.SmartTowns.users; + +import Team5.SmartTowns.rewards.Badge; +import Team5.SmartTowns.rewards.Sticker; +import lombok.Data; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +@Data +public class User { + + int id; + String email; //Validation would be done by email, since they will have that + String name; + String imgPath; + + 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) { + this.id = id; + this.email = email; + this.name = name; + 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"; + String notFoundPath = "../images/users/0.png"; + + File imgFile = new File("src/main/resources/static/" + imgPath); + return imgFile.exists() ? "../" + imgPath : notFoundPath; + } +} diff --git a/src/main/java/Team5/SmartTowns/users/UserController.java b/src/main/java/Team5/SmartTowns/users/UserController.java new file mode 100644 index 0000000000000000000000000000000000000000..0df6457070de7a7db2ff33576c569e20120be554 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/users/UserController.java @@ -0,0 +1,57 @@ +package Team5.SmartTowns.users; + +import Team5.SmartTowns.rewards.Badge; +import Team5.SmartTowns.rewards.Sticker; +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; + +@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) + ); + + @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)); + mav.addObject("badges", badges); + mav.addObject("stickers", stickers); + return mav; + } +// @GetMapping("/userProfile") +// public ModelAndView getUserPage(ModelAndView mav) { +// return mav; +// } +} diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index 3d1973fac582203a1fbfa9cab0937ff213a45c51..08273bc1bba5a74c518361f71f7d88bb53774811 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -183,6 +183,7 @@ main .badgesBlock{ } footer { + z-index: 99; bottom: 0%; left: 0%; position: fixed; diff --git a/src/main/resources/static/css/userProfile.css b/src/main/resources/static/css/userProfile.css new file mode 100644 index 0000000000000000000000000000000000000000..d59daa0689371b0adb2a204035b6a42d89dadab3 --- /dev/null +++ b/src/main/resources/static/css/userProfile.css @@ -0,0 +1,230 @@ +/* AUTHOR: Gabriel Copat*/ + +/*FONTS, TYPOGRAPHY & BACKGROUNDS*/ +* { + margin: 0; + padding: 0; + + & h1, & h2 { + letter-spacing: 0.25vw; + line-height: 1.3; + text-align: center; + color: white; + text-justify: inter-word; + } +} +@media only screen and (max-device-width: 500px) { + /*ADJUSTING FOR SMALLER SCREENS*/ + * { + & h1, & h2 { text-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh 1svh;} + & p { line-height: 1.1; color: white;} + } +} + +body { + background: linear-gradient(135deg, #9f74be, #3e126b); + height: 100svh; +} +main { + background: linear-gradient(to bottom, #1e1e1e 10%, darkgoldenrod 50%, #1e1e1e 90%); + border-radius: 1vw; + margin-inline: 5%; + /*margin-block: 5%;*/ + width: auto; + padding-block: 2svh; + 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; + /*padding: min(2vw, 4em);*/ + text-align: center; + + & #userPicture { + width: min(30vw, 30em); + margin-inline: auto; + border-radius: 100%; + border: solid #a2a2a2 4px; + box-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh max(1vw, 1em); + } + + & h1 { + font-size: max(5vw, 2em); + margin: 1svh 25%; + color:white; + border-bottom: #36454F solid 2px; + border-radius: 5vw; + box-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh 1vw -1vw; + } +} +#badgesBar::-webkit-scrollbar { + display: none; + -ms-scrollbar-darkshadow-color: transparent; +} +#badgesBar { + display: grid; + grid-template-areas: + "header" + "badges"; + overflow-x: scroll; + overflow-y: hidden; + color: white; + padding-bottom: 2%; + @media only screen and (min-device-width: 501px) { + height: 24vw; + } + & h2 { + position: absolute; + grid-area: header; + margin-inline: 5vw; + padding-inline: 2vw; + margin-block: -1svh; + box-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh 1vw -1vw; + border-bottom: #36454F solid 2px; + font-size: 4vw; + width: 7em; + height: 1.2em; + + } + & #allBadgesContainer { + margin-top: 3svh; + grid-area: badges; + height: 10svh; + align-content: center; + display: flex; + @media only screen and (min-device-width: 501px) { + height: 20vw; + margin-top: 6vw; + } + } + & .badgeImg { + margin-inline: 3vw; + height: 8svh; + z-index: 50; + @media only screen and (min-device-width: 501px) { + height: 15vw; + } + transition: 0.3s ease-out 100ms; + } + & .badgeImg:hover { + /*box-shadow: 0 0 20px 10px #bbbb00;*/ + transform: scale(1.5,1.5); + + } +} + +#stickersBox { + padding-top: 5%; + display: flex; + flex-direction: column; + /* border-bottom-left-radius: 2vw; */ + /* border-bottom-right-radius: 2vw; */ + /*background: linear-gradient(to bottom, darkgoldenrod, transparent 90%);*/ + margin-top: -1%; + & h2 { + font-size: 4em; + text-align: center; + box-shadow: rgba(0, 0, 0, 0.7) 0 2vw 2vw -2vw; + border-bottom: #36454F solid 2px; + margin-block: 1svh; + margin-inline: 25%; + } + & .stickersContainer { + margin-block: 1svh; + display: flex; + flex-wrap: wrap; + justify-content: space-around; + width: 100%; + + & .stickerImg { + width: 20vw; + margin-block: 1em; + + } + } +} +.locked { + filter: grayscale(100%); +} + +.dragonProgression { + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + height: 16svh; + box-sizing: content-box; + /*background: linear-gradient(to bottom, transparent -50%, darkgoldenrod 50%);*/ + width: 100%; + /*padding-top: 1svh;*/ + + @media only screen and (min-device-width: 501px) { + height: 28vw; + margin-bottom: 0; + padding-bottom: 5svh; + } + & h1 { + font-size: 3em; + 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-bottom: 1%; + } + & .dragonContainer { + position: relative; + margin: auto; + } + & .dragonImg { + height: 10svh; + width: 16svh; + @media only screen and (min-device-width: 501px) { + height: 22vw; + width: 30vw; + } + + } + & .dragonFill { + position: absolute; + overflow: hidden; + width: 40%; + } + & .dragonOut { + /*position: absolute;*/ + overflow: hidden; + } +} +header { + z-index: 99; + top: 0.5svh; + left: 0; + position: fixed; + width: 100vw; + justify-content: center; + display: flex; +} +header .footerBar { + display: flex; + list-style: none; + border-radius: 1vw; + overflow: hidden; + justify-content: space-evenly; + background-color: rgba(0, 0, 0, 0.7); +} +header .footerButton { + padding: 1vw; + text-align: center; + /*flex: 1 1;*/ + color:crimson; + background-color: rgba(31, 31, 31, 0.7); + font-size: 2.5em; + width: 15vw; +} +header .footerButton:hover { + background-color: #36454F; +} \ No newline at end of file diff --git a/src/main/resources/static/images/rewards/badges/0.png b/src/main/resources/static/images/rewards/badges/0.png new file mode 100644 index 0000000000000000000000000000000000000000..57e379c719f5fe28ba827d7007be9637cfd9ff73 Binary files /dev/null and b/src/main/resources/static/images/rewards/badges/0.png differ diff --git a/src/main/resources/static/images/rewards/dragonFilled.png b/src/main/resources/static/images/rewards/dragonFilled.png new file mode 100644 index 0000000000000000000000000000000000000000..7b0be9867d8b02500393ef2488dc8facf6c20711 Binary files /dev/null and b/src/main/resources/static/images/rewards/dragonFilled.png differ diff --git a/src/main/resources/static/images/rewards/dragonOutline.png b/src/main/resources/static/images/rewards/dragonOutline.png new file mode 100644 index 0000000000000000000000000000000000000000..12f6c621e392b57573717b0f4cb496a0b9e96b99 Binary files /dev/null and b/src/main/resources/static/images/rewards/dragonOutline.png differ diff --git a/src/main/resources/static/images/rewards/stickers/0.png b/src/main/resources/static/images/rewards/stickers/0.png new file mode 100644 index 0000000000000000000000000000000000000000..54eb8acb855af1730b2ba00b947a957ac7dfddca Binary files /dev/null and b/src/main/resources/static/images/rewards/stickers/0.png differ diff --git a/src/main/resources/static/images/users/0.png b/src/main/resources/static/images/users/0.png new file mode 100644 index 0000000000000000000000000000000000000000..75a9de71d2fd7e9b4f8e5a0992d0edeabe50c888 Binary files /dev/null and b/src/main/resources/static/images/users/0.png differ diff --git a/src/main/resources/static/scripts/gabScripts.js b/src/main/resources/static/scripts/gabScripts.js deleted file mode 100644 index 3053fc2316552e2f8c588847aa67e1aaa44aed44..0000000000000000000000000000000000000000 --- a/src/main/resources/static/scripts/gabScripts.js +++ /dev/null @@ -1,16 +0,0 @@ -function selectTrail(string, element) { - console.log('Clicked') - let listEl = document.getElementsByClassName('liHeader') - for (let i = 0; i < listEl.length; i++) { - listEl[i].classList.remove('selected') - } - document.getElementById(string).classList.add("selected") - -} - -function updateOutput() { - $.post("test_ajax_frag").done(function (fragment) { - console.log(fragment); - $("#trailInfoBox").replaceWith(fragment); - }); -} \ No newline at end of file diff --git a/src/main/resources/templates/rewards/userProfile.html b/src/main/resources/templates/rewards/userProfile.html new file mode 100644 index 0000000000000000000000000000000000000000..bd502cfc61f804533f7195de53ef812187d3f0ec --- /dev/null +++ b/src/main/resources/templates/rewards/userProfile.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title th:text="'VZLA Profile Page of ' + ${user.getName()}"></title> + <link rel="stylesheet" th:href="@{/css/userProfile.css}"> +<!-- <link rel="stylesheet" th:href="@{/css/templatingstyle.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>Search</b></li> + </ul> +</header> + +<main> + <!--PICTURE - DATA - BADGES --> + <div class="userInfo"> + <img th:src="@{${user.getImgPath()}}" + th:alt="${user.getName()}" + id="userPicture" + > + <h1 th:text="${user.getName()}"></h1> + <!--TODO add some progression info here?--> + </div> + <section class="rewards"> <!--Reward lists, badges on top, stickers (larger) on the bottom--> + <article id="badgesBar"> + <h2>Your Badges: </h2> <!--Shows first earned badges, followed by greyed out badges--> + <div id="allBadgesContainer" class="centerFlex"> + <img class="badgeImg" th:each="badge : ${badges}" th:src="@{'..' + ${badge.getImgPath()}}" + th:id="'img' + ${badge.getId()}" th:alt="${badge.getName()}" > + </div> + </article> + <article class="dragonProgression"> + <h1>The Dragon Trail</h1> + <div class="dragonContainer"> + <div class="dragonFill"> + <img th:src="@{/images/rewards/dragonFilled.png}" + alt="Filled Dragon" id="FilledDragon" class="dragonImg"> + </div> + <div class="dragonOut"> + <img th:src="@{/images/rewards/dragonOutline.png}" + alt="Outline Dragon" id="OutlineDragon" class="dragonImg"> + </div> + </div> + <h2>40%</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()}}" + th:id="'img' + ${sticker.getId()}" th:alt="${sticker.getName()}" > + </div> + </article> + </section> + + +</main> + +<footer> + +</footer> + + + +</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