diff --git a/build.gradle b/build.gradle index 43f4975ebf905eca7ab49e3622e40468b7ad812f..0bdcc02bb2a5367c58f9bb20ef3f61c21057242b 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,10 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' 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' + 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/identifier.sqlite b/identifier.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/main/java/Team5.Smarttowns/Data/Towns.db b/src/main/java/Team5.Smarttowns/Data/Towns.db new file mode 100644 index 0000000000000000000000000000000000000000..2532550d9acf4d8abc21a6e6ed89629908b72a3e Binary files /dev/null and b/src/main/java/Team5.Smarttowns/Data/Towns.db differ diff --git a/src/main/java/Team5/SmartTowns/Data/DatabaseController.java b/src/main/java/Team5/SmartTowns/Data/DatabaseController.java new file mode 100644 index 0000000000000000000000000000000000000000..0d38e955dd0a2e38e9585e5560d1cd4b6df83033 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/Data/DatabaseController.java @@ -0,0 +1,35 @@ +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; +import org.springframework.web.servlet.ModelAndView; + +import java.util.*; +@Controller +public class DatabaseController { + + + @Autowired + private locationRepository locationRepository; + @Autowired + private trailsRepository trailsRepository; + + + @GetMapping("/trailList") + public ModelAndView trailList() { + ModelAndView mav1 = new ModelAndView("towns/trailsData"); + List<trail> trail = trailsRepository.getAllTrails(); + mav1.addObject("trails", trail); + return mav1; + } + @GetMapping("locationList") + public ModelAndView locationList(){ + ModelAndView mav2 = new ModelAndView("towns/locationData"); + List<Location> Locations = locationRepository.getAllLocation(); + mav2.addObject("location", Locations); + return mav2; + } +} diff --git a/src/main/java/Team5/SmartTowns/Data/Location.java b/src/main/java/Team5/SmartTowns/Data/Location.java new file mode 100644 index 0000000000000000000000000000000000000000..01ba2932c6b7d0a6201aa8128161bf7ad8b27526 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/Data/Location.java @@ -0,0 +1,48 @@ +//Holds variable data for the locations table (landmarks) +package Team5.SmartTowns.Data; + +import lombok.AllArgsConstructor; +import lombok.Data; +@Data +@AllArgsConstructor +public class Location { +// private int locationID; + private String locationName; + private String locationEmail; + private String locationDescription; + private String locationPlace; + private int locationTrailID; + + @Override + public String toString() { + return "Location{" + + locationName + '\'' + + locationEmail + '\'' + + locationDescription + '\'' + + locationPlace + '\'' + + locationTrailID + + '}'; + } + + public String getLocationName() { + return locationName; + } + + public String getLocationEmail() { + return locationEmail; + } + + public String getLocationDescription() { + return locationDescription; + } + + public String getLocationPlace() { + return locationPlace; + } + + public int getLocationTrailID() { + return locationTrailID; + } + + +} diff --git a/src/main/java/Team5/SmartTowns/Data/locationRepository.java b/src/main/java/Team5/SmartTowns/Data/locationRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..822a18cb860654694734c7f591cbb5b216eb8a39 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/Data/locationRepository.java @@ -0,0 +1,12 @@ +//Holds locations data repository (landmarks) +package Team5.SmartTowns.Data; + +import java.util.List; + + +public interface locationRepository { + List<Location> getAllLocation(); + void addLocation(Location loc); + + +} diff --git a/src/main/java/Team5/SmartTowns/Data/locationRepositoryJDBC.java b/src/main/java/Team5/SmartTowns/Data/locationRepositoryJDBC.java new file mode 100644 index 0000000000000000000000000000000000000000..a5e79d4442d540f5dba67ee7f2b6217984044ebb --- /dev/null +++ b/src/main/java/Team5/SmartTowns/Data/locationRepositoryJDBC.java @@ -0,0 +1,42 @@ +//Implements the locations 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 locationRepositoryJDBC implements locationRepository { + private JdbcTemplate jdbc; + private RowMapper<Location> locationMapper; + + public locationRepositoryJDBC(JdbcTemplate aJdbc) { + this.jdbc = aJdbc; + setlocationMapper(); + } + private void setlocationMapper(){ + locationMapper = (rs, i) -> new Location( + + rs.getString("locationName"), + rs.getString("locationEmail"), + rs.getString("locationDescription"), + rs.getString("locationPlace"), + rs.getInt("locationTrailID") + ); + } + public List<Location> getAllLocation(){ + String sql= "SELECT * FROM locations"; + return jdbc.query(sql, locationMapper); + } + + @Override // intended implementation at current: user data from templates/Landmarks/LandmarkFormTh.html is added to the Location table + public void addLocation(Location loc) { + String sql = "insert into locations( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) values (?,?,?,?,?)"; + + jdbc.update(sql,loc.getLocationName(),loc.getLocationEmail(),loc.getLocationDescription(),loc.getLocationPlace(),loc.getLocationTrailID()); + } + + +} diff --git a/src/main/java/Team5/SmartTowns/Data/trail.java b/src/main/java/Team5/SmartTowns/Data/trail.java new file mode 100644 index 0000000000000000000000000000000000000000..86e78b96712ecf7a5756edbf20a4bae35839ca70 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/Data/trail.java @@ -0,0 +1,12 @@ +//Holds variable data for the trails table +package Team5.SmartTowns.Data; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class trail { + private int trailsId; + private String name; +} diff --git a/src/main/java/Team5/SmartTowns/Data/trailsRepository.java b/src/main/java/Team5/SmartTowns/Data/trailsRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..52d8dc39b9bda0a7390f06afca8769a5fd0607a9 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/Data/trailsRepository.java @@ -0,0 +1,8 @@ +//Holds trails data repository +package Team5.SmartTowns.Data; + +import java.util.List; + +public interface trailsRepository { + List<trail> getAllTrails(); +} diff --git a/src/main/java/Team5/SmartTowns/Data/trailsRepositoryJDBC.java b/src/main/java/Team5/SmartTowns/Data/trailsRepositoryJDBC.java new file mode 100644 index 0000000000000000000000000000000000000000..58aa84a95b00b8f8b5d7f97d240e04f405d2ac3b --- /dev/null +++ b/src/main/java/Team5/SmartTowns/Data/trailsRepositoryJDBC.java @@ -0,0 +1,28 @@ +//Implements the trails 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 trailsRepositoryJDBC implements trailsRepository{ + private JdbcTemplate jdbc; + private RowMapper<trail> trailMapper; + public trailsRepositoryJDBC(JdbcTemplate aJdbc){ + this.jdbc = aJdbc; + settrailsMapper(); + } + private void settrailsMapper(){ + trailMapper = (rs, i) -> new trail( + rs.getInt("trailID"), + rs.getString("name") + ); + } + public List<trail> getAllTrails(){ + String sql= "SELECT * FROM trails"; + return jdbc.query(sql, trailMapper); + } +} diff --git a/src/main/java/Team5/SmartTowns/Landmarks/Landmarks.java b/src/main/java/Team5/SmartTowns/Landmarks/Landmarks.java index 94fec3e5f18cf082f58ca3dbca4223b1c50429b7..381ccf04293b752f03e2eb743ba820c186b277dd 100644 --- a/src/main/java/Team5/SmartTowns/Landmarks/Landmarks.java +++ b/src/main/java/Team5/SmartTowns/Landmarks/Landmarks.java @@ -15,18 +15,19 @@ import java.util.List; @NoArgsConstructor public class Landmarks { - // Intialised object to getID from trail. + // Initialized object to getID from trail. //Predefined Landmark for Dragons Tale. public static List<Landmarks> landmarksDragonstrail = List.of( new Landmarks( 1, "A scent of...Dragon", "The Dragon has been spotted near by, find the QR code to continue" , "Start your discovery, at the sweet shop."), - new Landmarks( 2, "They've been found!", "Don't let them escape, find the next QR code to continue!", "location test") + new Landmarks( 2, "They've been found!", "Don't let them escape, find the next QR code to continue!", "Location test") ); private Integer trailID; private int landmarkID; @NotEmpty(message = "You must type in a username.") private String landmarkName; + @NotEmpty(message = "You must attach a contact address.") // Requires @NotEmpty for form validation @Email(message = "You must attach a contact address.") private String landmarkEmail; private String landmarkDescription; diff --git a/src/main/java/Team5/SmartTowns/Landmarks/LandmarksController.java b/src/main/java/Team5/SmartTowns/Landmarks/LandmarksController.java index 98c35594da947d261f6980b10a516a75b4583431..a94feef88935a8d3ca71f6326bdb8f44cda6dd0e 100644 --- a/src/main/java/Team5/SmartTowns/Landmarks/LandmarksController.java +++ b/src/main/java/Team5/SmartTowns/Landmarks/LandmarksController.java @@ -1,6 +1,9 @@ package Team5.SmartTowns.Landmarks; +import Team5.SmartTowns.Data.Location; +import Team5.SmartTowns.Data.locationRepository; import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -22,7 +25,8 @@ public class LandmarksController { } - + @Autowired + private locationRepository locationRepository; @PostMapping("/landmarkSub") public ModelAndView landmarkSent(@Valid @ModelAttribute("landmarkData") Landmarks landmarks, BindingResult bindingResult, Model model ) { @@ -32,11 +36,11 @@ public class LandmarksController { return modelAndView; } else{ - System.out.println(landmarks); - // current functionality only prints successful Landmarks, (todo )database integration is necessary here - - - ModelAndView modelAndView = new ModelAndView("redirect:/test.html"); + // converts valid response using Location constructor into a submittable format to the sql table + Location loc= new Location(landmarks.getLandmarkName(), landmarks.getLandmarkEmail(), landmarks.getLandmarkDescription(), landmarks.getLandmarkLocation(), landmarks.getTrailID()); + System.out.println(loc); + locationRepository.addLocation(loc); // adds valid landmark to locations table + ModelAndView modelAndView = new ModelAndView("redirect:/home"); return modelAndView; } diff --git a/src/main/java/Team5/SmartTowns/Towns/TownController.java b/src/main/java/Team5/SmartTowns/Towns/TownController.java new file mode 100644 index 0000000000000000000000000000000000000000..aa15a341da7fe2ef346f5278ae1a55faaa3136fc --- /dev/null +++ b/src/main/java/Team5/SmartTowns/Towns/TownController.java @@ -0,0 +1,21 @@ +package Team5.SmartTowns.Towns; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; + +import java.util.List; + +@Controller +public class TownController { + + @GetMapping("/home") + public ModelAndView getTownList(){ + ModelAndView modelAndView = new ModelAndView("Towns/home/homePage"); + TownStorage townsCurrent= new TownStorage().getInstance(); + List<Towns> towns = townsCurrent.getTownList(); + modelAndView.addObject("towns",towns); + return modelAndView; + + } +} diff --git a/src/main/java/Team5/SmartTowns/Towns/TownStorage.java b/src/main/java/Team5/SmartTowns/Towns/TownStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..d2d99ea992086630324fa131ad5bb410acd3c425 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/Towns/TownStorage.java @@ -0,0 +1,44 @@ +package Team5.SmartTowns.Towns; + +import java.util.ArrayList; +import java.util.List; + +public class TownStorage { + private List<Towns> townList; + private static TownStorage singleton; + + TownStorage() { + townList = new ArrayList<>(); + townList.addAll( + List.of( + new Towns("Caerphilly",01,3,70,"/images/CaerphillyCastle.jpg"), + new Towns("Risca",02,2,34,"/images/RiscaBanner.jpg"), + new Towns("Penarth",03,5,0,"/images/PenarthBanner.jpg"), + new Towns("Penarth",03,5,50,"/images/PenarthBanner.jpg"), + new Towns("Caerphilly",01,3,70,"/images/CaerphillyCastle.jpg"), + new Towns("Risca",02,2,90,"/images/RiscaBanner.jpg"), + new Towns("Penarth",03,5,100,"/images/PenarthBanner.jpg") + + + + ) + + ); + + } + public static TownStorage getInstance() { + if (singleton == null) { + singleton = new TownStorage(); + + } + return singleton; + } + + public List<Towns> getTownList() { + return townList; + } + + + + +} diff --git a/src/main/java/Team5/SmartTowns/Towns/Towns.java b/src/main/java/Team5/SmartTowns/Towns/Towns.java new file mode 100644 index 0000000000000000000000000000000000000000..a1b103f7abf6aa4379d877c645185f2c522a25ba --- /dev/null +++ b/src/main/java/Team5/SmartTowns/Towns/Towns.java @@ -0,0 +1,62 @@ +package Team5.SmartTowns.Towns; + +import lombok.Data; + +@Data +public class Towns { + private String name; + private Integer id; + private int trailNumber; + private int trailProgress; + + private String imageTown; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public int getTrailNumber() { + return trailNumber; + } + + public void setTrailNumber(int trailNumber) { + this.trailNumber = trailNumber; + } + + public int getTrailProgress() { + return trailProgress; + } + + public void setTrailProgress(int trailProgress) { + this.trailProgress = trailProgress; + } + + public String getImageTown() { + return imageTown; + } + + public void setImageTown(String imageTown) { + this.imageTown = imageTown; + } + + public Towns(String name, Integer id, int trailNumber, int trailProgress, String imageTown) { + this.name = name; + this.id = id; + this.trailNumber = trailNumber; + this.trailProgress = trailProgress; + this.imageTown = imageTown; + } +} diff --git a/src/main/java/Team5/SmartTowns/Webpages/WebpageController.java b/src/main/java/Team5/SmartTowns/Webpages/WebpageController.java index 5338f32f6dcbd262d1b9070169fcbaa211363a44..37a30366e5335d1870c202d1a49b4a5d2bca474d 100644 --- a/src/main/java/Team5/SmartTowns/Webpages/WebpageController.java +++ b/src/main/java/Team5/SmartTowns/Webpages/WebpageController.java @@ -11,27 +11,28 @@ import org.springframework.stereotype.Controller; public class WebpageController { @GetMapping("/Caerleon") public ModelAndView getCaerleonPage(){ - ModelAndView modelAndView = new ModelAndView("towns/caerleon"); + ModelAndView modelAndView = new ModelAndView("Towns/caerleon"); return modelAndView; } @GetMapping("/Caerphilly") public ModelAndView getCaerphillyPage(){ - ModelAndView modelAndView = new ModelAndView("towns/caerphilly"); + ModelAndView modelAndView = new ModelAndView("Towns/caerphilly"); return modelAndView; } @GetMapping("/Risca") public ModelAndView getRiscaPage(){ - ModelAndView modelAndView = new ModelAndView("towns/risca"); + ModelAndView modelAndView = new ModelAndView("Towns/risca"); return modelAndView; } - @GetMapping("/home") - public ModelAndView getHome(){ - ModelAndView modelAndView = new ModelAndView("towns/home/homePage"); - return modelAndView; - } +// @GetMapping("/home") +// public ModelAndView getHome(){ +// ModelAndView modelAndView = new ModelAndView("Towns/home/homePage"); +// return modelAndView; +// } + @RequestMapping(value="/test_ajax_frag", method=RequestMethod.POST) public String sendHtmlFragment(Model map) { 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/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/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..4fa498c61e7d5748e7d6dcfc26c7c6442dfd8cc4 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/rewards/Sticker.java @@ -0,0 +1,61 @@ +/*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 + boolean hasSticker; + + 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; + } + + 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) { + 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/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 new file mode 100644 index 0000000000000000000000000000000000000000..84887664fbee090abb68281f28b5f2b0832af9ae --- /dev/null +++ b/src/main/java/Team5/SmartTowns/users/User.java @@ -0,0 +1,40 @@ +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; + 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, 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"; + 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..d635c8a401d85ef6d930142882d69b5528f38fd7 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/users/UserController.java @@ -0,0 +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 { + + @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"); + List<Badge> badges = badgesRepository.getAllBadges(); /*DEPRECATED FOR THE MOMENT*/ + mav.addObject("badges", badges); + 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; + } +} 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/QRCodeScanner.html b/src/main/resources/QRCodeScanner.html new file mode 100644 index 0000000000000000000000000000000000000000..11da0658b1421e28159c97fc5abae82030ff3630 --- /dev/null +++ b/src/main/resources/QRCodeScanner.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>QR Camera</title> +</head> +<body> + +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b137891791fe96927ad78e64b0aad7bded08bdc..95f46c69ab2c6b38e1c520a5b8438c8f87aba0a3 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,5 @@ +spring.datasource.url=jdbc:mariadb://localhost:3306/towns +spring.datasource.username=root +spring.datasource.password=comsc +spring.sql.init.mode=always diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000000000000000000000000000000000000..e40208ba343bab8aeb675e792a3566fd0e573028 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,57 @@ +delete from users; +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'); +insert into trails ( Name) value ( 'Penarth Dragon Trail'); + +delete from locations; +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'St Cenydd','','Location description here','Caerphilly',0101); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'The Castle','','Location description here','Caerphilly',0101); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'Medieval Trades','','Location description here','Caerphilly',0101); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'The Queen''s War','','Location description here','Caerphilly',0101); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'The Green Lady','','Location description here','Caerphilly',0101); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'Armoury','','Location description here','Caerphilly',0101); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'Architecture','','Location description here','Caerphilly',0101); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( '21st Century Landmark','','Location description here','Caerphilly',0101); + +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'JD Wetherspoons-Malcolm Uphill','','Location description here','Caerphilly',0102); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'Caerphilly Cwtch','','Location description here','Caerphilly',0102); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'Caerphilly Conservative Club','','Location description here','Caerphilly',0102); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'The King''s Arms','','Location description here','Caerphilly',0102); + +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'Caerphilly Bus Station','','Location description here','Caerphilly',0103); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'The Medieval Courthouse','','Location description here','Caerphilly',0103); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ('Caerphilly Castle','','Location description here','Caerphilly',0103); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'Ty Vaughan House','','Location description here','Caerphilly',0103); + +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'Risca Colliery','','Location description here','Risca',0201); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID) value ( 'Black Vein Colliery Disaster','','Location description here','Risca',0201); + + +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 new file mode 100644 index 0000000000000000000000000000000000000000..92b8d4e0377d6ceeb58aef53b35e946670b6bc62 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,60 @@ +drop table if exists trails; +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 +( + locationID bigint auto_increment primary key, + locationName varchar(128), + locationEmail varchar(128), + locationDescription longtext, + 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, + 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/homePageStyle.css b/src/main/resources/static/css/homePageStyle.css index 6e2a91e341594ae4f89a867da1bc89fb93d4c4f1..64f767d40b32ccef413ea7b749e11356ef045aa4 100644 --- a/src/main/resources/static/css/homePageStyle.css +++ b/src/main/resources/static/css/homePageStyle.css @@ -1,3 +1,4 @@ + body{ background-color: rgb(41, 41, 41) } @@ -8,117 +9,164 @@ body{ .submitLand{ grid-area: submitButton; } -.caerphillyBanner , .riscaBanner,.penarthBanner{ +.Banner { margin-top: 20px; background-color: darkslategrey; margin-bottom: 20px; - border: solid 2px whitesmoke; + /*border: solid 2px whitesmoke;*/ /*border colour here at .banner and .bannertrail*/ border-right: none; } -.caerphillyBannerTrail,.riscaBannerTrail,.penarthBannerTrail{ - background-color: darkslategrey; + +/* .BannerTrail.Complete(100%) .BannerTrail.closeComplete(90-99%) .BannerTrail.nearComplete(70-99%) .BannerTrail.farComplete(50-69%) .BannerTrail.notComplete(0-49%) */ +.BannerTrail, .BannerTrail.Complete, .BannerTrail.closeComplete, .BannerTrail.nearComplete, .BannerTrail.farComplete, .BannerTrail.notComplete { margin-top: 20px; margin-bottom: 20px; border: solid 2px whitesmoke; - border-left: none; text-align: center; } -.penarthBannerTrail{ - background-image: linear-gradient(to right, darkslategrey , darkgoldenrod 40%); +.BannerTrail th, .BannerTrail.Complete th, .BannerTrail.closeComplete th, .BannerTrail.nearComplete th, .BannerTrail.farComplete th, .BannerTrail.notComplete th{ + grid-area:townBannerDetsR } -.caerphillyBannerTrail{ - margin-top: 20px; - margin-bottom: 20px; - border: solid 2px whitesmoke; - border-left: none; - background-image: linear-gradient(to right, darkslategrey , darkgoldenrod 30%); +.BannerTrail, .BannerTrail.Complete, .BannerTrail.closeComplete, .BannerTrail.nearComplete, .BannerTrail.farComplete, .BannerTrail.notComplete{ + grid-area:townBannerDets; } -.caerphillyBanner{ - grid-area:townBannerC; - background-image: url('/images/CaerphillyCastle.jpg'); - /*// from cadw*/ - background-size: 850px 200px; - background-repeat: no-repeat; - background-position: left 35%; - color: inherit; - text-decoration: none; - /*filter: grayscale(30%);*/ +.BannerTrail { + background-color: darkslategrey; + } -/*!*https://www.stayinwales.co.uk/wales_picture.cfm?p=4454*! Risca img*/ +.BannerTrail.Complete { + background-color: gold; + border: 2px solid gold; + border-left: solid 2px whitesmoke; +} +.BannerTrail.closeComplete { + background-color: darkgoldenrod; + background-color: darkgoldenrod; + border: 2px solid darkgoldenrod; + border-left: solid 2px whitesmoke; +} -.riscaBanner { - /* #caerUrl{*/ - grid-area: townBannerR; - background-image: url('/images/RiscaBanner.jpg'); - background-size: 850px 200px; - background-repeat: no-repeat; - background-position: left 65%; - color: inherit; - text-decoration: none; +.BannerTrail.nearComplete { + background-color: deepskyblue; + background-color: deepskyblue; + border: 2px solid deepskyblue; + border-left: solid 2px whitesmoke; } -.penarthBanner { - grid-area: townBannerP; - background-image: url('/images/PenarthBanner.jpg'); - background-size: 850px 200px; - background-repeat: no-repeat; - background-position: left 78%; - color: inherit; - text-decoration: none; +.BannerTrail.farComplete { + background-color: green; + background-color: green; + border: 2px solid green; + border-left: solid 2px whitesmoke; +} + +.BannerTrail.notComplete { + background-color: red; + background-color: red; + border: 2px solid red; + border-left: solid 2px whitesmoke; } -.caerphillyBannerTrail{ - grid-area:townBannerDetsC; + +/*Below selects banner that has the Complete class to change color*/ +.Banner:has(+.BannerTrail.Complete){ + border: 2px solid gold; + border-right: solid 2px whitesmoke; } -.riscaBanner{ - grid-area:townBannerR; + +.Banner:has(+.BannerTrail.closeComplete){ + border: 2px solid darkgoldenrod; + border-right: solid 2px whitesmoke; } -.riscaBannerTrail{ - grid-area:townBannerDetsR; + + +.Banner:has(+.BannerTrail.nearComplete){ + border: 2px solid deepskyblue; + border-right: solid 2px whitesmoke; } -.penarthBanner{ - grid-area:townBannerP; +.Banner:has(+.BannerTrail.farComplete){ + border: 2px solid green; + border-right: solid 2px whitesmoke; } -.penarthBannerTrail{ - grid-area:townBannerDetsP; +.Banner:has(+.BannerTrail.notComplete){ + border: 2px solid red; + border-right: solid 2px whitesmoke; } + + +.BannerTrail { + background-color: darkslategrey; + +} + + + + + + +.Banner { + grid-area:townBanner; + color: inherit; + text-decoration: none; + background-color: hotpink; +} + + +a{ + background-size: contain; + +} + + + #aboutUsFlavour{ grid-area: textFlavour; margin-top: 25px; margin-bottom: 15px; color: whitesmoke; } -#trailCountCaer,#trailCountRisca,#trailCountPenarth{ +#trailCount{ flex:1; - /*align-items: center;*/ - /*justify-content: center;*/ + } -#trailProgressCaer,#trailProgressRisca,#trailProgressPenarth{ +#trailProgress{ flex:2; align-content: center; } -.gridContainer1{ +.gridContainer1 { display:grid; grid-template-columns: 10% 10% 60% 5% 5% 10%; grid-template-rows: auto; grid-template-areas: ". pageTitle pageTitle pageTitle pageTitle ." - ". . . submitButton submitButton ." - ". townBannerC townBannerC townBannerDetsC . ." - ". townBannerR townBannerR townBannerDetsR . ." - ". townBannerP townBannerP townBannerDetsP . ." - " . . textFlavour . . ."; + ". . . submitButton submitButton ."; +} + +.gridContainer2 { + display:grid; + grid-template-columns: 10% 10% 60% 5% 5% 10%; + grid-template-rows: auto; + grid-template-areas: + + ". townBanner townBanner townBannerDets . ."; } +.gridContainer3 { + display:grid; + grid-template-columns: 10% 10% 60% 5% 5% 10%; + grid-template-rows: auto; + grid-template-areas: + " . . textFlavour . . ."; +} 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..8ceb77b4e82d8c44f9aefdcf9c1f54493771e668 --- /dev/null +++ b/src/main/resources/static/css/userProfile.css @@ -0,0 +1,363 @@ +/* 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*/ +* { + margin: 0; + padding: 0; + + & h1, & h2 { + letter-spacing: 0.25vw; + line-height: 1.3; + text-align: center; + color: white; + text-justify: inter-word; + } + & label { + color: white; + } + +} +@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, #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%); + 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; + 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: 15%; + 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; +} + +.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/images/Facebook.png b/src/main/resources/static/images/Facebook.png index 259359777e3e7c2ea4f31e00df81dbf075570f84..e510c31874240120ec7ce84676f8a486bbc86923 100644 Binary files a/src/main/resources/static/images/Facebook.png and b/src/main/resources/static/images/Facebook.png differ diff --git a/src/main/resources/static/images/Instagram.jpg b/src/main/resources/static/images/Instagram.jpg index 0c7f47366beef8b56464e6214276ab46e2c8ab66..59e94d4c36bac36d4cdc346293568721a23d0082 100644 Binary files a/src/main/resources/static/images/Instagram.jpg and b/src/main/resources/static/images/Instagram.jpg differ diff --git a/src/main/resources/static/images/LinkedIn.jpg b/src/main/resources/static/images/LinkedIn.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6bf094d20c89883756ec5a105ec2cea1f9eeaf76 Binary files /dev/null and b/src/main/resources/static/images/LinkedIn.jpg differ diff --git "a/src/main/resources/static/images/Twitter-Log\320\276.png" "b/src/main/resources/static/images/Twitter-Log\320\276.png" new file mode 100644 index 0000000000000000000000000000000000000000..c475939bfacb4be3559aab3d58a9a5eadf036afb Binary files /dev/null and "b/src/main/resources/static/images/Twitter-Log\320\276.png" differ 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/static/scripts/landmarkFormThScript.js b/src/main/resources/static/scripts/landmarkFormThScript.js new file mode 100644 index 0000000000000000000000000000000000000000..dc8ca6d96e55ff68b95da302e566db992a5eb409 --- /dev/null +++ b/src/main/resources/static/scripts/landmarkFormThScript.js @@ -0,0 +1,35 @@ + + + // verification function of the above form + function landmarkFormValidation(){ + var pass=true; + var trail = document.forms["landmarkSubmission"]["trailID"].value + var location = document.forms["landmarkSubmission"]["landmarkLocation"].value + var description = document.forms["landmarkSubmission"]["landmarkDescription"].value; + var descriptionWrdCount=description.split(" ").length + if (descriptionWrdCount>200){ + alert('Please keep your description to a maximum of 200 words.'); + pass = false; +} + + if (trail==0){ // values of 0 and "" used to ensure default drop down option isn't submittable + alert('Invalid trail selected. \nPlease select the trail you wish to join.'); + pass = false; +} + + if (location==""){ + alert('Invalid location selected. \nPlease select the location you wish to join.'); + + pass = false; // ensure correct trail is selected in accordance to the town it takes place in + +} if ( (location=="Caerphilly" & (parseInt(trail/100)!==1)) || + (location=="Risca" & (parseInt(trail/100)!==2)) || + (location=="Penarth" & (parseInt(trail/100)!==3)) ){ + alert('Trail unavailable in your current location. \nPlease choose an available trail'); + pass = false; +} + + return pass; +} + + 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/static/users.html b/src/main/resources/static/users.html new file mode 100644 index 0000000000000000000000000000000000000000..566549bdf8fae810809c1a81066000687cb338f6 --- /dev/null +++ b/src/main/resources/static/users.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title</title> +</head> +<body> + +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/Landmarks/LandmarkFormTh.html b/src/main/resources/templates/Landmarks/LandmarkFormTh.html index 9690356f54031ce873453d68aa97a248ac89f687..b77bb90ece04cc582bc5fa5ea369bd8ba45e864b 100644 --- a/src/main/resources/templates/Landmarks/LandmarkFormTh.html +++ b/src/main/resources/templates/Landmarks/LandmarkFormTh.html @@ -5,6 +5,7 @@ <title>Landmark Sign Up</title> <link rel="stylesheet" th:href="@{css/landmarkFormStyle.css}"> <link rel="stylesheet" th:href="@{css/templatingstyle.css}"> + <script src="scripts/landmarkFormThScript.js"></script> </head> @@ -30,7 +31,8 @@ </label><br><br> <label>Your Location: <select th:field="*{landmarkLocation}"> - <option value="" disabled selected>Select Location</option> + <option value="" hidden="true">Select Location</option> + <option value="" selected disabled>Select Location</option> <option value="Caerphilly">Caerphilly</option> <option value="Risca">Risca</option> <option value="Penarth">Penarth</option> @@ -38,6 +40,7 @@ </label><br><br> <label>Trail: <select th:field="*{trailID}"> + <option value=0 hidden="true">Select Trail</option> <option value=0 disabled selected>Select Trail</option> <option value=0101>(Caerphilly) Castle Trail</option> <option value=0102>(Caerphilly) Pub Trail</option> @@ -56,45 +59,7 @@ <hr style="height:40px; visibility:hidden;" /> </main> -<script> - - - -// verification function of the above form - function landmarkFormValidation(){ - var pass=true; - var trail = document.forms["landmarkSubmission"]["trailID"].value - var location = document.forms["landmarkSubmission"]["landmarkLocation"].value - var description = document.forms["landmarkSubmission"]["landmarkDescription"].value; - var descriptionWrdCount=description.split(" ").length - if (descriptionWrdCount>200){ - alert('Please keep your description to a maximum of 200 words.'); - pass = false; - } - - if (trail==0){ // values of 0 and "" used to ensure default drop down option isn't submittable - alert('Invalid trail selected. \nPlease select the trail you wish to join.'); - pass = false; - } - - if (location==""){ - alert('Invalid location selected. \nPlease select the location you wish to join.'); - - pass = false; // ensure correct trail is selected in accordance to the town it takes place in - - } if ( (location=="Caerphilly" & (parseInt(trail/100)!==1)) || - (location=="Risca" & (parseInt(trail/100)!==2)) || - (location=="Penarth" & (parseInt(trail/100)!==3)) ){ - alert('Trail unavailable in your current location. \nPlease choose an available trail'); - pass = false; - } - - return pass; - } - - -</script> <footer th:insert="~{/towns/Templating.html :: footer}"></footer> </body> </html> \ 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 new file mode 100644 index 0000000000000000000000000000000000000000..a193bf7614603637614dd0cfdc0974fdd8c44767 --- /dev/null +++ b/src/main/resources/templates/rewards/userProfile.html @@ -0,0 +1,73 @@ +<!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>Log In</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's Tale</h1> + <div class="dragonContainer"> + <div class="dragonFill" th:style="'width:'+ ${user.getDragonProgress()} + '%;'"> + <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 th:text="${user.getDragonProgress()} + '%'"></h2> + </article> + <article id="stickersBox"> <!--Need a controller to show earned stickers --> + <h2> STICKERS! </h2> + <div class="stickersContainer"> + <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> + </section> + + +</main> + +<footer> + +</footer> + + +<script> +</script> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/towns/UserData.html b/src/main/resources/templates/towns/UserData.html new file mode 100644 index 0000000000000000000000000000000000000000..3545df540058819df3883482d36f916407e449c8 --- /dev/null +++ b/src/main/resources/templates/towns/UserData.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>User Information</title> +</head> +<body> +<div> + <ul th:each="user:${users}"> + <li th:text="${user}"></li> + </ul> +</div> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/towns/home/homePage.html b/src/main/resources/templates/towns/home/homePage.html index 0f80da587b942b6f12ef7bce29913df3fa077838..2cdc1e854fba95f89dd2da702bbeb6d5011ed5d8 100644 --- a/src/main/resources/templates/towns/home/homePage.html +++ b/src/main/resources/templates/towns/home/homePage.html @@ -2,62 +2,58 @@ <html lang="en"> <head> <meta charset="UTF-8"> - <title>Title</title> + <title>VZTA - Smart Towns Trails</title> <link rel="stylesheet" th:href="@{/css/homePageStyle.css}"> <link rel="stylesheet" th:href="@{/css/templatingstyle.css}"> - </head> <body> <header th:insert="~{/towns/Templating.html::header}"></header> <main> -<div class="gridContainer1"> - <H1 id="homeTitle"> VZTA Smart Towns - Trails</H1> - <a class="submitLand" href="/landmarkSubmission"> <button> Submit Landmark!</button></a> - - <a href="/allTrails" class="caerphillyBanner"> - <H2> Caerphilly</H2></a> - - - <div class="caerphillyBannerTrail"> - <div id="trailCountCaer">Trails: 3</div> - <div id="trailProgressCaer">70%</div> - </div> - - <a href="/allTrails" class="riscaBanner"> - <H2> Risca</H2> </a> - - <div class="riscaBannerTrail"> - <div id="trailCountRisca">Trails: 2</div> - <div id="trailProgressRisca">0%</div> + <div class="gridContainer1"> + <H1 id="homeTitle"> VZTA Smart Towns - Trails</H1> + <a class="submitLand" href="/landmarkSubmission"> <button> Submit Landmark!</button></a> +<!-- <div th:text="${towns}">--> </div> + <div th:each="town:${towns}" class="gridContainer2"> - <a href="/allTrails" class="penarthBanner"> - <H2> Penarth</H2> </a> - <div class="penarthBannerTrail"> - <div id="trailCountPenarth">Trails: 5</div> - <div id="trailProgressPenarth">60%</div> - </div> - - <div id="aboutUsFlavour"> This is placeholder text about VZTA, this application, - the trails and towns and resultant awards written in an excitable manner!!</div> + <a href="/allTrails" class="Banner" + th:style="'background:url('+ ${town.getImageTown()} +');'" + th:styleappend="'background-size: 80vw 24vh;'+'background-repeat: no-repeat;'+'background-position: left 30%;'" /> -</div> + <H2 th:text="${town.getName()}"> </H2></a> + <div class="BannerTrail"th:classappend="|${town.getTrailProgress()==100 ? 'Complete' : ''} ${town.getTrailProgress()<100 and town.getTrailProgress()>=90 ? 'closeComplete' : ''''} ${town.getTrailProgress()<90 and town.getTrailProgress()>=70 ? 'nearComplete' : ''''} ${town.getTrailProgress()<70 and town.getTrailProgress()>=50 ? 'farComplete' : ''''} ${town.getTrailProgress()<50 ? 'notComplete' : ''''}|" + > +<!--Rough class append used to distinguish progress, using a class append based on progress can allow for further customisation to banners later on +rough % outline; +100 -gold +90-99 - darkgold +70-89 - blue +50-69-green +0-49-red--> + <div id="trailCount" th:text="'Trails: '+ ${town.getTrailNumber()}">Trails: </div> + <div id="trailProgress" th:text="${town.getTrailProgress()}+'%'"></div> + </div> + </div> +<div class="gridContainer3"> + <div id="aboutUsFlavour"> This is placeholder text about VZTA, this application, + the trails and towns and resultant awards written in an excitable manner!!</div> + </div> </main> <footer th:insert="~{/towns/Templating.html::footer}"></footer> diff --git a/src/main/resources/templates/towns/home/homePageOld.html b/src/main/resources/templates/towns/home/homePageOld.html new file mode 100644 index 0000000000000000000000000000000000000000..bf35e662607a4b2a101aeef83f3a05a65fd50ad2 --- /dev/null +++ b/src/main/resources/templates/towns/home/homePageOld.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title</title> +<!-- <link rel="stylesheet" th:href="@{/css/homePageStyle.css}">--> + <link rel="stylesheet" th:href="@{/css/templatingstyle.css}"> + +</head> +<body> + +<header th:insert="~{/towns/Templating.html::header}"></header> +<main> +<div class="gridContainer1"> + <H1 id="homeTitle"> VZTA Smart Towns - Trails</H1> + <a class="submitLand" href="/landmarkSubmission"> <button> Submit Landmark!</button></a> + + <a href="/allTrails" class="caerphillyBanner"> + <H2> Caerphilly</H2></a> + + + <div class="caerphillyBannerTrail"> + <div id="trailCountCaer">Trails: 3</div> + <div id="trailProgressCaer">70%</div> + </div> + + <a href="/allTrails" class="riscaBanner"> + <H2> Risca</H2> </a> + + <div class="riscaBannerTrail"> + <div id="trailCountRisca">Trails: 2</div> + <div id="trailProgressRisca">0%</div> + </div> + + + <a href="/allTrails" class="penarthBanner"> + <H2> Penarth</H2> </a> + <div class="penarthBannerTrail"> + <div id="trailCountPenarth">Trails: 5</div> + <div id="trailProgressPenarth">60%</div> + </div> + + <div id="aboutUsFlavour"> This is placeholder text about VZTA, this application, + the trails and towns and resultant awards written in an excitable manner!!</div> + +</div> + + + + + + + + + + + + + + + +</main> +<footer th:insert="~{/towns/Templating.html::footer}"></footer> + +</body> +</html> diff --git a/src/main/resources/templates/towns/locationData.html b/src/main/resources/templates/towns/locationData.html new file mode 100644 index 0000000000000000000000000000000000000000..f7636f05a3a0dbfc2ed2e87a8964f637b5e91046 --- /dev/null +++ b/src/main/resources/templates/towns/locationData.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Location Data</title> +</head> +<body> +<div> + <ul th:each="locations:${location}"> + <li th:text="${locations}"></li> + </ul> +</div> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/towns/templates.server/Templating.html b/src/main/resources/templates/towns/templates.server/Templating.html new file mode 100644 index 0000000000000000000000000000000000000000..14343a76ea3cc2974dbdd50cc7bd3ade124adc17 --- /dev/null +++ b/src/main/resources/templates/towns/templates.server/Templating.html @@ -0,0 +1,149 @@ +<header class="headerBar th:fragment="header"> + + <div class="Logo"> + <img th:src="@{images/VZTA.png}" alt="VZTA Logo"> + </div> + <nav class="navBar"> + <ul> + <li>Home</li> + <li>FAQs</li> + <li>Contact us</li> + </ul> + <label class="work">Who we Work with:</label> + <select> + <ul> + <option value="localauthorities">Local Authorities</option> + <option value="towns">Towns</option> + <option value="businesses">Businesses</option> + <option value="consumers">Consumers</option> + </ul> + </select> + </nav> + </header> + + <div class="footerBar" th:fragment="footer"> + <div class="containerFooter"> + <div class="leftFooter"> + <h3>VZTA</h3> + Near Me Now LTD + <br>Britania House + <br>Caerphilly Business Park + <br>Caerphilly + <br>CF83 3GG + </div> + <div class="rightFooter"> + <h3>Connect with us</h3> + <p>Be the first to know about updates by joining out Community page</p> + (C) VZTA 2022<br> + Policy Terms and Conditions + </div> + <div class="centerFooter"> + <span class="footerText"> + <h3>Follow Us</h3> + <a th:href=="https://www.facebook.com/VZTAsmarttowns/" class="icon"></a><img th:src="@{images/Facebook.png}" alt="Facebook Logo" class="picture"> + <a th:href=="https://www.twitter.com/VZTAsmarttowns/" class="icon"></a><img th:src="@{images/Twitter-Logo.png}" alt="X (formally Twitter) Logo" class="picture"> + <a th:href=="https://www.instagram.com/vztasmarttowns/" class="icon"></a><img th:src="@{images/Instagram.jpg}" alt="Instagram Logo" class="picture"> + <a th:href=="https://'www.linkin.com/company/vztasmarttowns/" class="icon"></a><img th:src="@{images/LinkedIn.jpg}" alt="Linkedin Logo" class="picture"> <br> + </span> + </div> + <div class="copyright" style="text-align: left"> + + </div> + </div> + </div> +</div> +<style> + /* Header */ + .headerBar { + border-bottom: 2px rgb(230, 230, 230) solid; + margin-bottom: 20px; + display: flex; + background: blueviolet; + } + /* Navbar Links */ + .navBar { + margin-top: 50px; + margin-left: auto; + margin-right:20px; + text-align: right; + } + .work{ + color: rgb(255, 255, 255); + } + .navBar ul { + list-style: none; + display: flex; + margin-left: 100px; + } + .navBar a { + border-left: 2px rgb(185, 185, 185) solid; + padding: 10px 40px; + text-decoration: none; + color:rgb(87, 86, 86); + white-space: nowrap; + overflow: hidden; + float: right; + } + .navBar a:hover { + background-color: rgb(209, 209, 209); + } + .navBar li{ + margin-left: 10px; + margin-right: 10px; + color: rgb(255, 255, 255); + } + .navListLast { + border-right: 2px rgb(185, 185, 185) solid; + margin-right:40px; + } + + /* Navbar Logo */ + .Logo { + margin-left:10px; + padding: 20px; + width: fit-content; + } + .Logo img { + width: 120px; + margin-left:15px; + } + + /* Footer */ + footer { + margin-top:20px; + display: flex; + justify-content: center; + } + .footerBar{ + border-top: 2px rgb(230, 230, 230) solid; + text-align: left; + display: flex; + background: blueviolet; + color: rgb(255, 255, 255); + padding-left: 30px; + } + .footerBar ul { + list-style: none; + display: flex; + } + .copyright{ + text-align: left; + display: flex; + } + .containerFooter{ + display: flex; + flex-direction: row; + } + .leftFooter{ + flex:1; + color: rgb(255, 255,255); + } + .centerFooter{ + flex: 1; + color: rgb(255, 255,255); + } + .rightFooter{ + flex:1; + color: rgb(255, 255, 255); + } +</style> \ No newline at end of file diff --git a/src/main/resources/templates/towns/trailsData.html b/src/main/resources/templates/towns/trailsData.html new file mode 100644 index 0000000000000000000000000000000000000000..eed9252284628bd058083f4f1cb13878b71f5b2f --- /dev/null +++ b/src/main/resources/templates/towns/trailsData.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Trails Information</title> +</head> +<body> +<div> + <ul th:each="trail:${trails}"> + <li th:text="${trail}"></li> + </ul> +</div> +</body> +</html> \ No newline at end of file