diff --git a/src/main/java/polish_community_group_11/polish_community/contact/Controllers/Contact.java b/src/main/java/polish_community_group_11/polish_community/contact/Controllers/Contact.java new file mode 100644 index 0000000000000000000000000000000000000000..baa02a3ac3dde79550028dc7ae734c236df789a7 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/contact/Controllers/Contact.java @@ -0,0 +1,14 @@ +package polish_community_group_11.polish_community.contact.Controllers; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.stereotype.Controller; + +@Controller +public class Contact { + @GetMapping("/contactus") + public ModelAndView home(){ + ModelAndView modelAndView = new ModelAndView("contact/contact"); + return modelAndView; + } +} diff --git a/src/main/resources/static/css/contact/contact.css b/src/main/resources/static/css/contact/contact.css new file mode 100644 index 0000000000000000000000000000000000000000..8ee14877241316282d20e2150c5065e9892c3bf2 --- /dev/null +++ b/src/main/resources/static/css/contact/contact.css @@ -0,0 +1,178 @@ +/* General Styles */ +body { + margin: 0; + padding: 0; + box-sizing: border-box; + background-color: #f4f4f9; + font-family: Arial, sans-serif; + padding-top: 80px; +} + +#title{ + + color: black; + text-align: center; + font-size: 35px; + margin-bottom: 30px; + font-weight: bold; + +} + +#contact-form { + margin: 20px auto; + max-width: 900px; + border-radius: 10px; + background-color: transparent; +} + +/* FAQ Section */ +#faq { + padding: 20px; +} + +.faq-question { + cursor: pointer; + font-weight: bold; + color: #333; +} + +.faq-question:hover { + color: #4CAF50; +} + +.faq-answer { + display: none; + color: #666; + padding-left: 20px; +} + +#map { + display: none; +} + +#contact-form { + display: flex; + justify-content: space-between; + align-items: flex-start; + text-align: left; + margin-top: 20px; + gap: 30px; +} + +#contact-form-container { + width: 50%; + padding: 30px; + background-color: transparent; + border: 1px solid #ddd; + border-radius: 10px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +#contact-form-container h3 { + margin-bottom: 20px; + color: #333; +} + +#contact-info-container { + width: 50%; + padding: 30px; + background-color: transparent; + border: 1px solid #ddd; + border-radius: 10px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +#contact-info-container h3 { + margin-bottom: 20px; + color: #333; +} + +#contactForm label { + display: block; + margin-bottom: 8px; + font-weight: bold; + color: #333; +} + +#contactForm input, #contactForm textarea { + width: 100%; + padding: 10px; + margin-bottom: 15px; + border: 1px solid #ccc; + border-radius: 5px; + box-sizing: border-box; + font-size: 16px; + color: #333; +} + +#contactForm input:focus, #contactForm textarea:focus { + border-color: #007BFF; + outline: none; + box-shadow: 0 0 5px rgba(0, 123, 255, 0.5); +} + +#contactForm button { + display: inline-block; + padding: 10px 20px; + font-size: 16px; + color: #fff; + background-color: black; + border: none; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s; +} + +#contactForm button:hover { + background-color: #333; +} + +#formFeedback { + margin: 10px; + font-size: 14px; + color: grey; +} + +body, .faq-answer, #contactForm input, #contactForm textarea { + font-size: 14px; + color: #666; +} + +h2, h3, .faq-question { + color: #333; +} + +/* Media Queries for Mobile Optimization */ +@media (max-width: 768px) { + /* Stack Contact Form and Contact Info Vertically on Smaller Screens */ + #contact-form { + flex-direction: column; + } + + #contact-form-container, #contact-info-container { + width: 100%; + margin-bottom: 20px; + } +} + +@media (max-width: 480px) { + #faq h2 { + font-size: 20px; + } + + #contact-form h2 { + font-size: 18px; + } + + .faq-question { + font-size: 16px; + } + + #contact-form input, #contact-form textarea { + font-size: 14px; + } + + #contact-form button { + font-size: 14px; + } +} diff --git a/src/main/resources/static/js/contact/contact.js b/src/main/resources/static/js/contact/contact.js new file mode 100644 index 0000000000000000000000000000000000000000..5294bbc947e215731ca73758fbb6f95016ef2e69 --- /dev/null +++ b/src/main/resources/static/js/contact/contact.js @@ -0,0 +1,174 @@ +// initalise the chart when the page loads, could also initialize map but didn't work like the chart did +// also sort out the animations of the contact form +document.addEventListener('DOMContentLoaded', () => { + + // call the chart initialization method + initUserChart(); + + // input focus and blur animation for the text + document.querySelectorAll('input, textarea').forEach(field => { + field.addEventListener('focus', () => { + anime({ + targets: field, + scale: 1.05, // enlarge teh box slightly on focus + duration: 300, + easing: 'easeInOutQuad' + }); + }); + + field.addEventListener('blur', () => { + anime({ + targets: field, + scale: 1, // return the box to the original size + duration: 300, + easing: 'easeInOutQuad' + }); + }); + }); + + // submit button hover bounce effect + const submitButton = document.getElementById('submitButton'); + submitButton.addEventListener('mouseover', () => { + anime({ + targets: submitButton, + translateY: [-10, 0], + duration: 500, + easing: 'easeOutBounce' + }); + }); + + // FAQ functionality that includes the map initialization + document.querySelectorAll('.faq-question').forEach(question => { + question.addEventListener('click', function () { + // answer of the questions + const answer = this.nextElementSibling; + // the map element + const mapContainer = this.nextElementSibling.nextElementSibling; + + // toggle visibility of the answer (block = visible, none = hidden) + const isHidden = answer.style.display === 'none' || answer.style.display === ''; + answer.style.display = isHidden ? 'block' : 'none'; + + // if the map container is hidden, initialize the map + if (mapContainer.style.display === 'none' || mapContainer.style.display === '') { + // show the map + mapContainer.style.display = 'block'; + // call the map initialization method + initMap(mapContainer); + } else { + // hide the map + mapContainer.style.display = 'none'; + } + + }); + }); + + // form submission that displays a feedback message + document.getElementById('contactForm').addEventListener('submit', function (event) { + event.preventDefault(); + + // get all the inputs from the fields + const name = document.getElementById('name').value.trim(); + const email = document.getElementById('email').value.trim(); + const message = document.getElementById('message').value.trim(); + + // get the feedback element and clear the previous feedback + const feedback = document.getElementById('formFeedback'); + feedback.textContent = ''; + feedback.style.color = ''; + + let isValid = true; + + // check for empty fields + if (!name || !email || !message) { + feedback.textContent = 'Please fill out all fields.'; + feedback.style.color = 'red'; + isValid = false; + } + + // check for valid email + if (email) { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + feedback.textContent = 'Please enter a valid email address.'; + feedback.style.color = 'red'; + isValid = false; + } + } + + // if everything is valid + if (isValid) { + feedback.textContent = 'Thank you for your message! We will get back to you within 2-3 days.'; + feedback.style.color = 'green'; + + // reset the form fields + this.reset(); + } + }); + +}); + +// funtion for initialising the map +function initMap(container) { + // using Cardiff location coordinates + const location = { lat: 51.4778, lng: -3.1776 }; + + // create the map that centers at Cardiff + const map = new google.maps.Map(container, { + zoom: 12, + center: location + }); + + // add the marker that is placed at Cardiff + const marker = new google.maps.Marker({ + position: location, + map: map, + title: "Our Location", + }); +} + +// funtion for initialising the chart +function initUserChart() { + // get the html chart + const ctx = document.getElementById('userChart').getContext('2d'); + + // mock data for years and the corresponding number of users + // in future versions, this would collect data from the users database + const years = [2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024]; + const numberOfUsers = [500, 700, 900, 1200, 1500, 2000, 2500, 3000, 4000, 5000, 6000]; + + // set up the chart + new Chart("userChart", { + type: "line", // line chart + data: { + labels: years, + datasets: [{ + label: "Number of Users", + backgroundColor: "rgba(0,0,255,0.2)", + borderColor: "rgba(0,0,255,1.0)", + data: numberOfUsers, + fill: true, // fill the area below the line + }] + }, + // styling options for the chart + options: { + scales: { + x: { + title: { + display: true, + text: 'Number of Users' + }, + beginAtZero: true, + }, + y: { + title: { + display: true, + text: 'Year' + } + } + }, + responsive: true, + + } + }); +} diff --git a/src/main/resources/templates/contact/contact.html b/src/main/resources/templates/contact/contact.html new file mode 100644 index 0000000000000000000000000000000000000000..f8d51304093cd2b96a1305686dc2b91d346497a0 --- /dev/null +++ b/src/main/resources/templates/contact/contact.html @@ -0,0 +1,96 @@ +<!DOCTYPE html> +<html lang="en" xmlns:th="http://www.thymeleaf.org" + xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" + layout:decorate="~{layout/layout}"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Contact Us</title> + <link rel="stylesheet" href="/css/contact/contact.css"> + + <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script> + <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAcnCewJfgcEPXtBPkokQwJXcOwUy-9iwI&callback=initMap" async defer></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script> + +</head> +<body layout:fragment="content"> + + <h1 id="title">Contact Us</h1> + <!-- Contact Form Section --> + <section id="contact-form"> + + <!-- Contact Form Container --> + <div id="contact-form-container"> + <h2>Get in Touch</h2> + <form id="contactForm" action="/submit" method="POST" novalidate> + <label for="name">Name:</label> + <input type="text" id="name" name="name"> + + <label for="email">Email:</label> + <input type="email" id="email" name="email"> + + <label for="message">Message:</label> + <textarea id="message" name="message"></textarea> + + <div id="formFeedback"></div> + + <button type="submit" id="submitButton">Submit</button> + </form> + </div> + + <!-- Contact Information Container --> + <div id="contact-info-container"> + <h3>Contact Information</h3> + <p><strong>Phone:</strong> +123456789</p> + <p><strong>Email:</strong> contact@domain.com</p> + <p><strong>Address:</strong> 123 Street, City, Country</p> + + <a href="https://www.facebook.com/LudekPCG" class="social-icon"><img src="/assets/navbarImages/facebook.png" alt="Facebook"></a> + <a href="#" class="social-icon"><img src="/assets/navbarImages/twitter.png" alt="Twitter"></a> + <a href="#" class="social-icon"><img src="/assets/navbarImages/instagram.png" alt="Instagram"></a> + </div> + + </section> + + + + <!-- FAQ Section --> + <section id="faq"> + + <h2>Frequently Asked Questions</h2> + + <div class="faq-item"> + <h3 class="faq-question">What is the community?</h3> + <p class="faq-answer">Our community is a group of people who support and connect with each other.</p> + </div> + + <div class="faq-item"> + <h3 class="faq-question">How can I join the community?</h3> + <p class="faq-answer">You can join by signing up through the main page or by contacting us directly.</p> + </div> + + <div class="faq-item"> + <h3 class="faq-question">How do I contact support?</h3> + <p class="faq-answer">You can reach out to us through the contact form above.</p> + </div> + + <div class="faq-item"> + <h3 class="faq-question">How many users are currently on the platform?</h3> + <div class="faq-answer"> + <canvas id="userChart" width="400" height="200"></canvas> <!-- The canvas for the chart --> + </div> + </div> + + <div class="faq-item"> + <h3 class="faq-question">Where are you located?</h3> + <p class="faq-answer">We are located in the heart of the city. You can find us on the map below!</p> + + <!-- Interactive Map --> + <div id="map" style="height: 400px;"></div> <!-- Map container --> + </div> + </section> + + <script src="/js/contact/contact.js"></script> +</body> +</html> + diff --git a/src/test/java/polish_community_group_11/polish_community/register/RegisterTest.java b/src/test/java/polish_community_group_11/polish_community/register/RegisterTest.java index 135cad41d80e0c79de7d4b9dfabdd7f80f3a2158..f2079deb2e615797b8a18652b15866592f48ead7 100644 --- a/src/test/java/polish_community_group_11/polish_community/register/RegisterTest.java +++ b/src/test/java/polish_community_group_11/polish_community/register/RegisterTest.java @@ -1,59 +1,130 @@ package polish_community_group_11.polish_community.register; import polish_community_group_11.polish_community.register.dao.UserRepository; -import polish_community_group_11.polish_community.register.services.UserServiceImpl; // Assuming UserServiceImpl is the correct implementation import polish_community_group_11.polish_community.register.models.User; - +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import static org.junit.jupiter.api.Assertions.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.time.LocalDate; -import static org.mockito.Mockito.*; -import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; +import java.util.List; +import java.util.Arrays; -@ExtendWith(MockitoExtension.class) + +@SpringBootTest +@Transactional public class RegisterTest { - @Mock - private UserRepository userRepository; // Mocking the UserRepository + @Autowired + private UserRepository userRepository; + + @BeforeEach + public void setUp() { + // Create and save 3 users to the database before each test + User user1 = new User(); + user1.setEmail("user1@example.com"); + user1.setPassword("password1"); + user1.setFullname("User One"); + user1.setDateOfBirth(LocalDate.of(2003, 1, 1)); + user1.setRoleId(1); + userRepository.saveUser(user1); - @InjectMocks - private UserServiceImpl userService; // Injecting mock into the service + User user2 = new User(); + user2.setEmail("user2@example.com"); + user2.setPassword("password2"); + user2.setFullname("User Two"); + user2.setDateOfBirth(LocalDate.of(2003, 2, 2)); + user2.setRoleId(2); + userRepository.saveUser(user2); + + User user3 = new User(); + user3.setEmail("user3@example.com"); + user3.setPassword("password3"); + user3.setFullname("User Three"); + user3.setDateOfBirth(LocalDate.of(2003, 3, 3)); + user3.setRoleId(1); + userRepository.saveUser(user3); + + System.out.println("3 users have been saved to the database."); + } @Test - public void testFindAllUsers() { - System.out.println("Starting testFindAllUsers..."); - // Arrange: Create a mock user using setters - List<User> mockUsers = new ArrayList<>(); - User user = new User(); - user.setId(1); - user.setEmail("test@example.com"); - user.setPassword("password"); - user.setFullname("John Doe"); - user.setDateOfBirth(LocalDate.of(1990, 1, 1)); - user.setRoleId(1); + public void testSaveUser() { + // create a new user + User newUser = new User(); + newUser.setEmail("newuser@example.com"); + newUser.setFullname("New User"); + newUser.setPassword("password4"); + newUser.setDateOfBirth(LocalDate.of(2000, 1, 1)); + newUser.setRoleId(1); + + // call the saveUser method from the repository + int userId = userRepository.saveUser(newUser); + + // print out the saved user's ID + System.out.println("Saved user ID: " + userId); + + // verify that the returned user ID is greater than 0, meaning the user was saved + assertTrue(userId > 0, "User ID is greater than 0 therefore it was a successful insert"); - mockUsers.add(user); - - // Mock the findAllUsers method to return the mock users - when(userRepository.findAllUsers()).thenReturn(mockUsers); - - // Act: Call the service method - List<User> users = userService.findAllUsers(); - System.out.println("Returned users: " + users); - - // Assert: Verify that the result is not null, contains the expected users, and is not empty + } + + + @Test + public void testFindAllUsers() { + // call the repository method to retrieve all users + List<User> users = userRepository.findAllUsers(); + + // print out all users + System.out.println("Users list:"); + for (User user : users) { + System.out.println(user.getFullname() + " - " + user.getEmail()); + } + + // verify that the result is not null and contains the expected number of users assertNotNull(users); - assertFalse(users.isEmpty()); - assertEquals(1, users.size()); // Expecting 1 user + assertEquals(4, users.size()); + + // verify that the users have the correct details + for (User user : users) { + assertNotNull(user.getEmail()); + assertNotNull(user.getFullname()); + } + + System.out.println("Test completed: Found " + users.size() + " users."); + } + + + @Test + public void testFindByEmail() { + + // intialiaze the testing emails + List<String> emails = Arrays.asList("user1@example.com", "user2@example.com", "user3@example.com"); + + // loop through each email + for (String email : emails) { + // call the repository method to retrieve user by email + User userByEmail = userRepository.findByEmail(email); + + if (userByEmail != null) { + System.out.println("User found by email: " + userByEmail.getFullname() + " - " + userByEmail.getEmail()); + } else { + System.out.println("No user found with the email " + email); + } + + // verify that the user is returned + assertNotNull(userByEmail); + assertEquals(email, userByEmail.getEmail()); + } + + System.out.println("Test completed: Found 3 users"); + + User wrongUser = userRepository.findByEmail("nonexistent@example.com"); - System.out.println("Test completed successfully. Found " + users.size() + " users."); + // ensure that no user is found when + assertNull(wrongUser); } }