diff --git a/build.gradle b/build.gradle index 0eed974f7a02792eda7daacc68a5c30486d188c5..af2499a7473f74081c6a13900f5c73acbb993627 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.springframework.boot:spring-boot-starter-security' testImplementation 'org.springframework.security:spring-security-test' - testImplementation 'org.mockito:mockito-core:4.0.0' // Mocking library testImplementation 'org.mockito:mockito-junit-jupiter:4.0.0' // JUnit 5 support for Mockito diff --git a/src/main/java/polish_community_group_11/polish_community/event/controllers/EventController.java b/src/main/java/polish_community_group_11/polish_community/event/controllers/EventController.java index e1bffd25295351330f5c86cb941aa87a86967ab2..92658e9952906751b11a4fdde6c54d944647fff2 100644 --- a/src/main/java/polish_community_group_11/polish_community/event/controllers/EventController.java +++ b/src/main/java/polish_community_group_11/polish_community/event/controllers/EventController.java @@ -1,20 +1,37 @@ package polish_community_group_11.polish_community.event.controllers; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.ModelAndView; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; -import polish_community_group_11.polish_community.event.services.EventRepository; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import polish_community_group_11.polish_community.event.models.viewmodel.EventForm; +import org.springframework.web.bind.annotation.*; +import polish_community_group_11.polish_community.event.models.Event; +import polish_community_group_11.polish_community.event.models.EventImpl; +import polish_community_group_11.polish_community.event.services.EventService; +import polish_community_group_11.polish_community.register.services.UserService; + +import java.security.Principal; +import java.sql.SQLException; @Controller public class EventController { - // Inject the EventRepository service to handle event-related data operations - private EventRepository eventService; + private EventService eventService; + private UserService userService; + @Autowired // Constructor-based dependency injection for EventRepository - public EventController(EventRepository eventService){ + public EventController(EventService eventService, UserService userService) { this.eventService = eventService; + this.userService = userService; } // Controller method to handle GET requests for the "/event" URL @@ -50,4 +67,65 @@ public class EventController { // Return the populated ModelAndView object, which will render the event details view return modelAndView; } + + //Controller Method to load create new event page + @GetMapping("event/add") + public ModelAndView addEvent() { + ModelAndView modelAndView = new ModelAndView("event/add-event"); + //Passing Form object for validation + modelAndView.addObject("event", new EventForm()); + + /* Form action string to check the type of action to perform and dynamically + use the same form for both create and update */ + modelAndView.addObject("formAction", "/event/add"); + return modelAndView; + } + + @PostMapping("event/add") + public ModelAndView addEvent(@Valid @ModelAttribute("event") EventForm event, + BindingResult bindingResult, Model model, + Principal principal, RedirectAttributes redirectAttributes) throws SQLException { + ModelAndView modelAndView = new ModelAndView("event/add-event"); + if(bindingResult.hasErrors()) { + modelAndView.addObject(model.asMap()); + + /* Form action string to check the type of action to perform and dynamically + use the same form for both create and update */ + modelAndView.addObject("formAction", "/event/add"); + } + else{ + if(event==null){ + throw new NullPointerException("New event is empty or null"); + } + eventService.addNewEvent(event.processEventForm(),principal.getName()); + redirectAttributes.addFlashAttribute("successMessage", "Event created successfully!"); + modelAndView.setViewName("redirect:/event"); + } + return modelAndView; + } + + //Update Events + @GetMapping("event/edit/{id}") + public ModelAndView editEvent(@PathVariable int id) { + EventForm event = eventService.getEventById(id).processEventToEventForm(); + ModelAndView modelAndView = new ModelAndView("event/add-event"); + modelAndView.addObject("event", event); + modelAndView.addObject("formAction", "/event/edit"); + return modelAndView; + } + + @PostMapping("/event/edit") + public ModelAndView editEvent(@Valid @ModelAttribute("event") EventForm event, + BindingResult bindingResult, Model model) throws SQLException { + ModelAndView modelAndView = new ModelAndView("event/add-event"); + if (bindingResult.hasErrors()) { + modelAndView.addObject(model.asMap()); + modelAndView.addObject("formAction", "/event/edit"); + } else { + eventService.getEditEvent(event.processEventForm()); + modelAndView.setViewName("redirect:/event"); + } + return modelAndView; + } + } diff --git a/src/main/java/polish_community_group_11/polish_community/event/dao/EventRepository.java b/src/main/java/polish_community_group_11/polish_community/event/dao/EventRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..2efd942099d1d78d12dd9f537ab8730323c124c0 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/event/dao/EventRepository.java @@ -0,0 +1,15 @@ +package polish_community_group_11.polish_community.event.dao; + +import polish_community_group_11.polish_community.event.models.Event; + +import java.sql.SQLException; +import java.util.List; + +public interface EventRepository { + + List<Event> getAllEvents(); + + Event getEventById(int id); + void addNewEvent(Event newEvent) throws SQLException; + Event editEvent(Event event)throws SQLException; +} diff --git a/src/main/java/polish_community_group_11/polish_community/event/services/EventRepositoryImpl.java b/src/main/java/polish_community_group_11/polish_community/event/dao/EventRepositoryImpl.java similarity index 50% rename from src/main/java/polish_community_group_11/polish_community/event/services/EventRepositoryImpl.java rename to src/main/java/polish_community_group_11/polish_community/event/dao/EventRepositoryImpl.java index 80acfe225ce1fd0b245e04c60e59c4d947773676..2197c12e66e9341ecdf4b1f3db398b38929e6cd7 100644 --- a/src/main/java/polish_community_group_11/polish_community/event/services/EventRepositoryImpl.java +++ b/src/main/java/polish_community_group_11/polish_community/event/dao/EventRepositoryImpl.java @@ -1,17 +1,20 @@ -package polish_community_group_11.polish_community.event.services; +package polish_community_group_11.polish_community.event.dao; +import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import polish_community_group_11.polish_community.event.models.Event; import polish_community_group_11.polish_community.event.models.EventImpl; +import java.sql.SQLException; + import java.util.List; @Repository // Marks this class as a Spring Data repository for database interaction public class EventRepositoryImpl implements EventRepository { // JdbcTemplate instance for executing SQL queries - private JdbcTemplate jdbc; + private final JdbcTemplate jdbc; // RowMapper to map SQL result set rows to Event objects private RowMapper<Event> eventItemMapper; @@ -74,4 +77,64 @@ public class EventRepositoryImpl implements EventRepository { return event; } + public void addNewEvent(Event newEvent) throws SQLException, NullPointerException{ + if(newEvent==null){ + throw new NullPointerException("Event cannot be null"); + } + String dbInsertSql = + "insert into event " + + "(event_title, event_description, location, " + + "event_date, event_time,user_id, event_poster_url,whyJoin,benefits)" + + " values (?,?,?,?,?,?,?,?,?)"; + try{ + jdbc.update(dbInsertSql, + newEvent.getEvent_title(), + newEvent.getDescription(), + newEvent.getDescription(), + newEvent.getEvent_date(), + newEvent.getEvent_time(), + newEvent.getUser_id(), + newEvent.getImageUrl(), + newEvent.getWhyJoin(), + newEvent.getBenefits() + ); + } + catch (DataAccessException e) { + throw new SQLException("Failed to insert new information record", e); + } + } + + // Updates selected record with new updated information + public Event editEvent(Event event)throws SQLException { + String updateSql = "UPDATE event " + + "SET event_title = ?, event_description = ?, " + + "event_date = ?, event_time = ?, " + + "location = ?, user_id = ?, event_poster_url = ?, " + + "whyJoin = ?, benefits = ? " + + "WHERE event_id = ?"; + try { + // jdbc.update() is a method that will execute the sql query + // replaces the ? with the actual values from the news object + int rowsAffected = jdbc.update(updateSql, + event.getEvent_title(), + event.getDescription(), + event.getEvent_date(), + event.getEvent_time(), + event.getLocation(), + event.getUser_id(), + event.getImageUrl(), + event.getWhyJoin(), + event.getBenefits(), + event.getEvent_id() + ); + + // error handling + if (rowsAffected == 0) { + throw new SQLException("No event item was updated. Check the ID provided."); + } + } catch (DataAccessException e) { + throw new SQLException("Error updating event with ID: " + event.getEvent_id(), e); + } + return event; + } } diff --git a/src/main/java/polish_community_group_11/polish_community/event/models/Event.java b/src/main/java/polish_community_group_11/polish_community/event/models/Event.java index e09329ed4bcb7cdcc99766913950c87a0afa0ac2..06bab520ed102dd498c48d7a4f92a1679b219a9d 100644 --- a/src/main/java/polish_community_group_11/polish_community/event/models/Event.java +++ b/src/main/java/polish_community_group_11/polish_community/event/models/Event.java @@ -1,5 +1,7 @@ package polish_community_group_11.polish_community.event.models; +import polish_community_group_11.polish_community.event.models.viewmodel.EventForm; + import java.time.LocalDate; import java.time.LocalTime; @@ -27,4 +29,5 @@ public interface Event { public String getBenefits(); public void setBenefits(String benefits); + EventForm processEventToEventForm(); } diff --git a/src/main/java/polish_community_group_11/polish_community/event/models/EventImpl.java b/src/main/java/polish_community_group_11/polish_community/event/models/EventImpl.java index d08a19da24131fc362cb05643a4d840d92dc5c30..61e14fdef04e64df50ff58b129f26755f639dc3f 100644 --- a/src/main/java/polish_community_group_11/polish_community/event/models/EventImpl.java +++ b/src/main/java/polish_community_group_11/polish_community/event/models/EventImpl.java @@ -1,7 +1,13 @@ package polish_community_group_11.polish_community.event.models; +import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; import lombok.AllArgsConstructor; import lombok.Data; +import org.hibernate.validator.constraints.URL; +import polish_community_group_11.polish_community.event.models.viewmodel.EventForm; import java.time.LocalDate; import java.time.LocalTime; @@ -20,4 +26,12 @@ public class EventImpl implements Event{ private String whyJoin; private String benefits; + public EventForm processEventToEventForm(){ + + EventForm eventForm = new EventForm( + event_id, event_title, description, event_date, event_time, + location, user_id, imageUrl, whyJoin, benefits + ); + return eventForm; + } } diff --git a/src/main/java/polish_community_group_11/polish_community/event/models/viewmodel/EventForm.java b/src/main/java/polish_community_group_11/polish_community/event/models/viewmodel/EventForm.java new file mode 100644 index 0000000000000000000000000000000000000000..a85c34e182877a2a660fab7b8dadb22b467039f2 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/event/models/viewmodel/EventForm.java @@ -0,0 +1,62 @@ +package polish_community_group_11.polish_community.event.models.viewmodel; + +import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.URL; +import polish_community_group_11.polish_community.event.models.Event; +import polish_community_group_11.polish_community.event.models.EventImpl; + +import java.time.LocalDate; +import java.time.LocalTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EventForm { + private int event_id; + @NotEmpty(message = "Please enter the event title") + private String event_title; + @NotEmpty(message = "Please enter the event description") + private String description; + @FutureOrPresent(message = "The date cannot be in the past") + @NotNull(message = "Please enter the event date") + private LocalDate event_date; + @NotNull(message = "Please enter the time of the event") + private LocalTime event_time; + @NotEmpty(message = "Please provide the location of event") + private String location; + private int user_id; + @NotEmpty(message = "Please provide event image url") + @Pattern(regexp = ".*\\.(png|jpg|jpeg|gif).*", message = "Must be a valid image (png, jpg, jpeg, gif).") + @URL(message = "Not a valid image url") + private String imageUrl; + private String whyJoin; + private String benefits; + + public Event processEventForm(){ + + if (event_title == null || event_title.isEmpty()) { + throw new IllegalArgumentException("Event title is required"); + } + if (event_date == null) { + throw new IllegalArgumentException("Event date is required"); + } + if (event_time == null) { + throw new IllegalArgumentException("Event time is required"); + } + if (location == null || location.isEmpty()) { + throw new IllegalArgumentException("Location is required"); + } + + Event event = new EventImpl( + event_id, event_title, description, event_date, event_time, + location, user_id, imageUrl, whyJoin, benefits + ); + return event; + } +} diff --git a/src/main/java/polish_community_group_11/polish_community/event/services/EventRepository.java b/src/main/java/polish_community_group_11/polish_community/event/services/EventRepository.java deleted file mode 100644 index d529da27847197c528aa76dce8076869ea35653e..0000000000000000000000000000000000000000 --- a/src/main/java/polish_community_group_11/polish_community/event/services/EventRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package polish_community_group_11.polish_community.event.services; - -import polish_community_group_11.polish_community.event.models.Event; -import java.util.List; - -public interface EventRepository { - - List<Event> getAllEvents(); - - Event getEventById(int id); -} diff --git a/src/main/java/polish_community_group_11/polish_community/event/services/EventService.java b/src/main/java/polish_community_group_11/polish_community/event/services/EventService.java new file mode 100644 index 0000000000000000000000000000000000000000..64b87f40b97d0d26dbab5db187fb67ae3e0ff471 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/event/services/EventService.java @@ -0,0 +1,13 @@ +package polish_community_group_11.polish_community.event.services; + +import polish_community_group_11.polish_community.event.models.Event; +import java.sql.SQLException; +import java.util.List; + +public interface EventService { + + List<Event> getAllEvents(); + Event getEventById(int id); + void addNewEvent(Event newEvent, String email) throws SQLException; + Event getEditEvent(Event event) throws SQLException; +} \ No newline at end of file diff --git a/src/main/java/polish_community_group_11/polish_community/event/services/EventServiceImpl.java b/src/main/java/polish_community_group_11/polish_community/event/services/EventServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..23e1de25a7040ca733f47a861a4131dd1dd1efa8 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/event/services/EventServiceImpl.java @@ -0,0 +1,38 @@ +package polish_community_group_11.polish_community.event.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import polish_community_group_11.polish_community.event.dao.EventRepository; +import polish_community_group_11.polish_community.event.models.Event; +import polish_community_group_11.polish_community.register.services.UserService; + +import java.sql.SQLException; +import java.util.List; + +@Service +public class EventServiceImpl implements EventService { + + private final EventRepository eventRepository; + private final UserService userService; + + @Autowired + public EventServiceImpl(EventRepository eventRepository, UserService userService) { + this.eventRepository = eventRepository; + this.userService=userService; + } + + public List<Event> getAllEvents(){ + return eventRepository.getAllEvents(); + } + public Event getEventById(int id){ + return eventRepository.getEventById(id); + } + public void addNewEvent(Event newEvent, String email) throws SQLException { + newEvent.setUser_id(userService.findUserIdByEmail(email)); + eventRepository.addNewEvent(newEvent); + } + + public Event getEditEvent(Event event) throws SQLException { + return eventRepository.editEvent(event); + } +} diff --git a/src/main/java/polish_community_group_11/polish_community/profile/controllers/ProfileController.java b/src/main/java/polish_community_group_11/polish_community/profile/controllers/ProfileController.java new file mode 100644 index 0000000000000000000000000000000000000000..fb82e1ed930c64a1369b15245aadde4468150270 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/profile/controllers/ProfileController.java @@ -0,0 +1,81 @@ +package polish_community_group_11.polish_community.profile.controllers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import polish_community_group_11.polish_community.profile.models.Profile; +import polish_community_group_11.polish_community.profile.services.ProfileService; +import polish_community_group_11.polish_community.register.models.User; +import polish_community_group_11.polish_community.register.services.UserService; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +@Controller +public class ProfileController { + + private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Autowired + private UserService userService; + @Autowired + private ProfileService profileService; + + @GetMapping("/profile") + public ModelAndView profile(Authentication authentication) { + + ModelAndView modelAndView = new ModelAndView("profile/profilePage"); + + Profile profile = getProfileForAuthenticatedUser(authentication); + modelAndView.addObject("profile", profile); + + return modelAndView; + } + + @PostMapping("/update") + public String updateProfile(@ModelAttribute Profile profile, Authentication authentication,@RequestParam("newPicture") MultipartFile newPicture) + throws IOException { + String username = authentication.getName(); + User user = userService.findByEmail(username); + profile.setUserId(user.getId()); + if (!newPicture.isEmpty()) { + String fileName = StringUtils.cleanPath(newPicture.getOriginalFilename()); + String uploadDir = "build/resources/main/static/assets/user-photos/" + user.getId(); + Path uploadPath = Paths.get(uploadDir); + + if (!Files.exists(uploadPath)) { + Files.createDirectories(uploadPath); + } + + try (InputStream inputStream = newPicture.getInputStream()) { + Path filePath = uploadPath.resolve(fileName); + Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING); + profile.setProfilePicture("/assets/user-photos/" + user.getId() + "/" + fileName); + } + } + profileService.updateProfile(profile); + return "redirect:/profile"; + } + private Profile getProfileForAuthenticatedUser(Authentication authentication) { + LOG.info("getting profile for {} - isAuthenticated: {}", authentication.getName(), authentication.isAuthenticated()); + String username = authentication.getName(); + + User user = userService.findByEmail(username); + Profile profile = profileService.getProfile(user.getId()); + return profile; + } +} diff --git a/src/main/java/polish_community_group_11/polish_community/profile/controllers/ProfileRestController.java b/src/main/java/polish_community_group_11/polish_community/profile/controllers/ProfileRestController.java new file mode 100644 index 0000000000000000000000000000000000000000..67aef21422016f65845afd4d2a21e4200428f177 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/profile/controllers/ProfileRestController.java @@ -0,0 +1,30 @@ +package polish_community_group_11.polish_community.profile.controllers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import polish_community_group_11.polish_community.profile.models.Profile; +import polish_community_group_11.polish_community.profile.services.ProfileService; +import polish_community_group_11.polish_community.register.models.User; +import polish_community_group_11.polish_community.register.services.UserService; + +@RestController +public class ProfileRestController { + @Autowired + private UserService userService; + @Autowired + private ProfileService profileService; + + @GetMapping("/profile-json") + public Profile getProfile(Authentication authentication) { + return getProfileForAuthenticatedUser(authentication); + } + private Profile getProfileForAuthenticatedUser(Authentication authentication) { + String username = authentication.getName(); + + User user = userService.findByEmail(username); + Profile profile = profileService.getProfile(user.getId()); + return profile; + } +} diff --git a/src/main/java/polish_community_group_11/polish_community/profile/models/Profile.java b/src/main/java/polish_community_group_11/polish_community/profile/models/Profile.java new file mode 100644 index 0000000000000000000000000000000000000000..ae0fa9b7366301cf254290dbd8b4324afffd3716 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/profile/models/Profile.java @@ -0,0 +1,23 @@ +package polish_community_group_11.polish_community.profile.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Profile { + private int userId; + private String fullName; + private String email; + private String profilePicture; + private LocalDate dob; + private String bio; + private String phoneNumber; + private String organisation; + private boolean showPhoneNumber; + private boolean showDob; +} diff --git a/src/main/java/polish_community_group_11/polish_community/profile/repositories/ProfileRepository.java b/src/main/java/polish_community_group_11/polish_community/profile/repositories/ProfileRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..3c1011095364f46e023dcf5cc2202f8c88f6221a --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/profile/repositories/ProfileRepository.java @@ -0,0 +1,13 @@ +package polish_community_group_11.polish_community.profile.repositories; + +import org.springframework.stereotype.Repository; +import polish_community_group_11.polish_community.profile.models.Profile; + +import java.util.List; + +@Repository +public interface ProfileRepository { + Profile getProfile(Integer id); + void addProfile(Profile profile); + void updateProfile(Profile profile); +} diff --git a/src/main/java/polish_community_group_11/polish_community/profile/repositories/ProfileRepositoryImpl.java b/src/main/java/polish_community_group_11/polish_community/profile/repositories/ProfileRepositoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..1c1a562776fb9e69a6d5fd7085101332ad673e2e --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/profile/repositories/ProfileRepositoryImpl.java @@ -0,0 +1,52 @@ +package polish_community_group_11.polish_community.profile.repositories; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; +import polish_community_group_11.polish_community.feed.models.FeedImpl; +import polish_community_group_11.polish_community.profile.models.Profile; + +@Repository +public class ProfileRepositoryImpl implements ProfileRepository { + private final JdbcTemplate jdbcTemplate; + private final RowMapper<Profile> rowMapper; + + public ProfileRepositoryImpl(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + this.rowMapper = (rs, rowNum) -> new Profile( + rs.getInt("user_id"), + rs.getString("fullName"), + rs.getString("email"), + rs.getString("profile_picture"), + rs.getDate("dob").toLocalDate(), + rs.getString("bio"), + rs.getString("phone_number"), + rs.getString("organization"), + rs.getBoolean("show_phone_number"), + rs.getBoolean("show_dob") + ); + } + @Override + public void addProfile(Profile profile) { + String sql = "INSERT INTO user_profile (user_id) VALUES (?)"; + jdbcTemplate.update(sql, profile.getUserId()); + + } + @Override + public Profile getProfile(Integer id) { + String sql = "SELECT u.id as user_id, " + + "u.fullname, u.email, u.dob, u.organization, " + + "up.profile_picture, up.phone_number, up.bio, up.show_phone_number, up.show_dob " + + "FROM users u LEFT JOIN user_profile up ON " + + "u.id = up.user_id WHERE u.id = ?"; + return jdbcTemplate.queryForObject(sql, rowMapper, id); + } + + + @Override + public void updateProfile(Profile profile) { + String sql = "UPDATE user_profile SET profile_picture = ?," + + " bio = ?, phone_number = ?, show_phone_number = ?, show_dob = ? WHERE user_id = ?"; + jdbcTemplate.update(sql, profile.getProfilePicture(), profile.getBio(), profile.getPhoneNumber(),profile.isShowPhoneNumber(), profile.isShowDob(), profile.getUserId()); + } +}; diff --git a/src/main/java/polish_community_group_11/polish_community/profile/services/ProfileService.java b/src/main/java/polish_community_group_11/polish_community/profile/services/ProfileService.java new file mode 100644 index 0000000000000000000000000000000000000000..1e4de2551b8e8bdd81b768d1f705715846182750 --- /dev/null +++ b/src/main/java/polish_community_group_11/polish_community/profile/services/ProfileService.java @@ -0,0 +1,22 @@ +package polish_community_group_11.polish_community.profile.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import polish_community_group_11.polish_community.profile.models.Profile; +import polish_community_group_11.polish_community.profile.repositories.ProfileRepository; + +@Service +public class ProfileService { + private ProfileRepository profileRepository; + @Autowired + public ProfileService(ProfileRepository profileRepository) { + this.profileRepository = profileRepository; + } + public void addProfile(Profile profile) {profileRepository.addProfile(profile);} + public Profile getProfile(Integer userId) { + return profileRepository.getProfile(userId); + } + public void updateProfile(Profile profile) { + profileRepository.updateProfile(profile); + } +} diff --git a/src/main/java/polish_community_group_11/polish_community/register/controllers/RegisterController.java b/src/main/java/polish_community_group_11/polish_community/register/controllers/RegisterController.java index c6aba7401b29156bfa9fa9c9bdd8d64f2f35429f..b27e416ec76b4219449021e37a37562a14bed6d4 100644 --- a/src/main/java/polish_community_group_11/polish_community/register/controllers/RegisterController.java +++ b/src/main/java/polish_community_group_11/polish_community/register/controllers/RegisterController.java @@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; +import polish_community_group_11.polish_community.profile.models.Profile; +import polish_community_group_11.polish_community.profile.services.ProfileService; import polish_community_group_11.polish_community.register.models.User; import polish_community_group_11.polish_community.register.models.Role; import polish_community_group_11.polish_community.register.services.UserService; @@ -19,6 +21,9 @@ public class RegisterController { @Autowired private RoleService roleService; + + @Autowired + private ProfileService profileService; // displaying the registration form using get request and ModelAndView @GetMapping("/register") @@ -49,6 +54,10 @@ public class RegisterController { // save user to the database userService.saveUser(user); +// Profile newProfile = new Profile(); +// newProfile.setUserId(user.getId()); +// profileService.addProfile(newProfile); + // redirect to the login page return "redirect:/login"; } diff --git a/src/main/java/polish_community_group_11/polish_community/register/dao/UserRepository.java b/src/main/java/polish_community_group_11/polish_community/register/dao/UserRepository.java index 945b72d91dfdb22bf599c5b4b185adf8513f87e6..2c6c3f1b02cc0cb6efd92df490bb8b66118689c9 100644 --- a/src/main/java/polish_community_group_11/polish_community/register/dao/UserRepository.java +++ b/src/main/java/polish_community_group_11/polish_community/register/dao/UserRepository.java @@ -8,9 +8,11 @@ public interface UserRepository { int saveUser(User user); // add user into the database User findByEmail(String email); // find user by email + int findUserIdByEmail(String email); User findById(int id); // find user by ID List<User> findAllUsers(); // get all the users + String findUserFullNameByEmail(String email); } \ No newline at end of file diff --git a/src/main/java/polish_community_group_11/polish_community/register/dao/UserRepositoryImpl.java b/src/main/java/polish_community_group_11/polish_community/register/dao/UserRepositoryImpl.java index 5d12c85607ddbfe25dda9c8281756e2d1a3c0dc4..0a3b1be269baaae808f0a2cee2849693ae839486 100644 --- a/src/main/java/polish_community_group_11/polish_community/register/dao/UserRepositoryImpl.java +++ b/src/main/java/polish_community_group_11/polish_community/register/dao/UserRepositoryImpl.java @@ -85,6 +85,22 @@ public class UserRepositoryImpl implements UserRepository { } } + public int findUserIdByEmail(String email) { + if(email.trim().equals("")|| email == null){ + throw new IllegalArgumentException("Email is null or empty"); + } + User user = findByEmail(email); + if(user == null){ + throw new NullPointerException("User not found"); + } + return user.getId(); + } + + public String findUserFullNameByEmail(String email) { + User user = findByEmail(email); + return user.getFullname(); + } + // function for finding user by id public User findById(int id) { @@ -107,6 +123,6 @@ public class UserRepositoryImpl implements UserRepository { // return null if no user is found with the id return null; } - } + } } diff --git a/src/main/java/polish_community_group_11/polish_community/register/services/UserService.java b/src/main/java/polish_community_group_11/polish_community/register/services/UserService.java index 63cf2d3a58c459ec7d14a667c5510223243a8fce..aa346e699b0dc7c54f3091995951fc8eeea7dc75 100644 --- a/src/main/java/polish_community_group_11/polish_community/register/services/UserService.java +++ b/src/main/java/polish_community_group_11/polish_community/register/services/UserService.java @@ -10,6 +10,8 @@ public interface UserService { User findById(int id); User findByEmail(String email); + int findUserIdByEmail(String email); List<User> findAllUsers(); + public String findUserFullNameByEmail(String email); } \ No newline at end of file diff --git a/src/main/java/polish_community_group_11/polish_community/register/services/UserServiceImpl.java b/src/main/java/polish_community_group_11/polish_community/register/services/UserServiceImpl.java index d2c0a3e498d415ace85964d8964c78108ed7afb5..233bdac350904a2da84dfbcefda455344ae0113b 100644 --- a/src/main/java/polish_community_group_11/polish_community/register/services/UserServiceImpl.java +++ b/src/main/java/polish_community_group_11/polish_community/register/services/UserServiceImpl.java @@ -37,4 +37,10 @@ public class UserServiceImpl implements UserService { // Calls findAll method of the UserRepository return userRepository.findAllUsers(); } + public int findUserIdByEmail(String email){ + return userRepository.findUserIdByEmail(email); + } + public String findUserFullNameByEmail(String email){ + return userRepository.findUserFullNameByEmail(email); + } } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 50fdd5a36fa3d207c55898969b61bdf30c4624e5..a142a3314181fcf50f8532f4ee34f0b01e311f11 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -15,26 +15,6 @@ VALUES delete from event; -insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url,whyJoin,benefits) -values ('Science Fair', 'Students explore through the science fair', 'Cardiff', current_date,current_time, 1, 'https://marketplace.canva.com/EAE53TNAVD8/1/0/1131w/canva-event-present-science-fair-promotion-poster-1abqT-GiCNQ.jpg','Participating in the Science Fair offers you a unique opportunity to dive deep into the world of science, innovation, and discovery. Whether you''re a student eager to showcase your scientific knowledge or an individual with a passion for learning, this event is the perfect platform to fuel your curiosity. Here’s why you should join: -Engage with Innovative Ideas: Explore cutting-edge scientific projects that challenge the status quo and inspire new ways of thinking. -Collaborate with Like-Minded Individuals: Meet fellow science enthusiasts, students, and professionals who share your interests and passion for discovery. -Boost Your Critical Thinking: Through the process of research, experimentation, and presentation, you’ll develop critical problem-solving skills that are invaluable in any field. -Expand Your Knowledge: Learn about new technologies, scientific theories, and groundbreaking advancements that will shape the future of science and innovation. -Be a Part of a Larger Community: Join a global community of science advocates and future scientists, making valuable connections that could open doors to future opportunities.', 'Hands-On Experience: Gain practical, hands-on experience in the scientific method, from research and hypothesis testing to data analysis and presentation. -Develop Presentation Skills: Sharpen your ability to communicate complex scientific concepts in an engaging and accessible way, an essential skill for any future career. -Exposure to Career Opportunities: Connect with professionals in science, education, and industry, opening up potential career pathways, internships, and scholarships. -Recognition and Prizes: Stand a chance to win awards and gain recognition for your hard work and creativity. Whether you’re awarded for your innovation, research, or presentation, your efforts will be acknowledged. -Confidence Building: Presenting your work to peers, teachers, and judges builds confidence in your abilities and boosts self-esteem, allowing you to grow as both a scientist and an individual. -Inspire Future Projects: Your participation could spark new ideas and motivate others to start their own scientific endeavors, contributing to a culture of curiosity and learning. -Stay Ahead of the Curve: By participating, you gain knowledge about the latest trends in science and technology, giving you an edge in academic and professional fields. -By joining this science fair, you are not only enriching your own learning experience but also contributing to a vibrant community of innovators and explorers.'); -insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url,whyJoin,benefits) -values ('Games Fair', 'Gamers explore through the game fair', 'Bristol', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/game-event-poster-template-c54aaeed440befaacf79e6dd35deb8f5_screen.jpg?ts=1486132851','Abc', 'Def'); -insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url,whyJoin,benefits) -values ('Bikes Fair', 'Riders explore through the Ride fair', 'Newport', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/bike-fest-poster-design-template-fb1cc1ab4b2aee783f8ee75476c4c92d_screen.jpg?ts=1637012682','Abc', 'Def'); - - -- mock data for news delete from news; INSERT INTO news (news_title, news_summary, news_source, news_link, news_image_url, user_id, news_upload_date) @@ -74,16 +54,6 @@ VALUES 5, current_date); --- mock data for events -insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url) -values ('Bikes Fair', 'Riders explore through the Ride fair', 'Newport', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/bike-fest-poster-design-template-fb1cc1ab4b2aee783f8ee75476c4c92d_screen.jpg?ts=1637012682'); -insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url) -values ('Bikes Fair', 'Riders explore through the Ride fair', 'Newport', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/bike-fest-poster-design-template-fb1cc1ab4b2aee783f8ee75476c4c92d_screen.jpg?ts=1637012682'); -insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url) -values ('Bikes Fair', 'Riders explore through the Ride fair', 'Newport', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/bike-fest-poster-design-template-fb1cc1ab4b2aee783f8ee75476c4c92d_screen.jpg?ts=1637012682'); -insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url) -values ('Bikes Fair', 'Riders explore through the Ride fair', 'Newport', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/bike-fest-poster-design-template-fb1cc1ab4b2aee783f8ee75476c4c92d_screen.jpg?ts='); - -- insert standard roles into roles table INSERT INTO roles (role_name) VALUES ('ADMIN'); @@ -105,7 +75,9 @@ INSERT INTO users (email, password, fullname, dob, role_id, organization) VALUES INSERT INTO users (email, password, fullname, dob, role_id) -VALUES ('user@email.com', 'Abcdef1!', 'Jane Doe', '200-01-01', 1 ); +VALUES ('user@email.com', 'Abcdef1!', 'Jane Doe', '2000-01-01', 1 ); +UPDATE user_profile SET bio = 'Jane''s bio' WHERE user_id = (SELECT id from users where email = 'user@email.com'); + -- insert the user and role id's of the created roles and user INSERT INTO user_roles (user_id, role_id) VALUES (1, 1); @@ -139,3 +111,29 @@ INSERT INTO post_likes (post_id, user_id) VALUES (2, 2), (2, 4); +insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url,whyJoin,benefits) +values ('Science Fair', 'Students explore through the science fair', 'Cardiff', current_date,current_time, 1, 'https://marketplace.canva.com/EAE53TNAVD8/1/0/1131w/canva-event-present-science-fair-promotion-poster-1abqT-GiCNQ.jpg','Participating in the Science Fair offers you a unique opportunity to dive deep into the world of science, innovation, and discovery. Whether you''re a student eager to showcase your scientific knowledge or an individual with a passion for learning, this event is the perfect platform to fuel your curiosity. Here’s why you should join: +Engage with Innovative Ideas: Explore cutting-edge scientific projects that challenge the status quo and inspire new ways of thinking. +Collaborate with Like-Minded Individuals: Meet fellow science enthusiasts, students, and professionals who share your interests and passion for discovery. +Boost Your Critical Thinking: Through the process of research, experimentation, and presentation, you’ll develop critical problem-solving skills that are invaluable in any field. +Expand Your Knowledge: Learn about new technologies, scientific theories, and groundbreaking advancements that will shape the future of science and innovation. +Be a Part of a Larger Community: Join a global community of science advocates and future scientists, making valuable connections that could open doors to future opportunities.', 'Hands-On Experience: Gain practical, hands-on experience in the scientific method, from research and hypothesis testing to data analysis and presentation. +Develop Presentation Skills: Sharpen your ability to communicate complex scientific concepts in an engaging and accessible way, an essential skill for any future career. +Exposure to Career Opportunities: Connect with professionals in science, education, and industry, opening up potential career pathways, internships, and scholarships. +Recognition and Prizes: Stand a chance to win awards and gain recognition for your hard work and creativity. Whether you’re awarded for your innovation, research, or presentation, your efforts will be acknowledged. +Confidence Building: Presenting your work to peers, teachers, and judges builds confidence in your abilities and boosts self-esteem, allowing you to grow as both a scientist and an individual. +Inspire Future Projects: Your participation could spark new ideas and motivate others to start their own scientific endeavors, contributing to a culture of curiosity and learning. +Stay Ahead of the Curve: By participating, you gain knowledge about the latest trends in science and technology, giving you an edge in academic and professional fields. +By joining this science fair, you are not only enriching your own learning experience but also contributing to a vibrant community of innovators and explorers.'); +insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url,whyJoin,benefits) +values ('Games Fair', 'Gamers explore through the game fair', 'Bristol', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/game-event-poster-template-c54aaeed440befaacf79e6dd35deb8f5_screen.jpg?ts=1486132851','Abc', 'Def'); +insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url,whyJoin,benefits) +values ('Bikes Fair', 'Riders explore through the Ride fair', 'Newport', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/bike-fest-poster-design-template-fb1cc1ab4b2aee783f8ee75476c4c92d_screen.jpg?ts=1637012682','Abc', 'Def'); +insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url) +values ('Bikes Fair', 'Riders explore through the Ride fair', 'Newport', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/bike-fest-poster-design-template-fb1cc1ab4b2aee783f8ee75476c4c92d_screen.jpg?ts=1637012682'); +insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url) +values ('Bikes Fair', 'Riders explore through the Ride fair', 'Newport', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/bike-fest-poster-design-template-fb1cc1ab4b2aee783f8ee75476c4c92d_screen.jpg?ts=1637012682'); +insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url) +values ('Bikes Fair', 'Riders explore through the Ride fair', 'Newport', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/bike-fest-poster-design-template-fb1cc1ab4b2aee783f8ee75476c4c92d_screen.jpg?ts=1637012682'); +insert into event (event_title, event_description, location, event_date, event_time,user_id, event_poster_url) +values ('Bikes Fair', 'Riders explore through the Ride fair', 'Newport', current_date,current_time, 1, 'https://d1csarkz8obe9u.cloudfront.net/posterpreviews/bike-fest-poster-design-template-fb1cc1ab4b2aee783f8ee75476c4c92d_screen.jpg?ts='); diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index c2d28a6e33acafa680a95fd5b5d24e0307ec4de6..2371c4449bc48ab5ee94d057edc7beb68dc6f0a1 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -9,6 +9,7 @@ DROP TABLE IF EXISTS feed_tags; DROP TABLE IF EXISTS comment; DROP TABLE IF EXISTS feed; DROP TABLE IF EXISTS tags; +DROP TABLE IF EXISTS user_profile; DROP TABLE IF EXISTS user_roles; DROP TABLE IF EXISTS users; DROP TABLE IF EXISTS roles; @@ -40,22 +41,6 @@ create table if not exists information ) engine = InnoDB; --- create schema for event - -create table if not exists event( - event_id int primary key auto_increment, - event_title varchar(255), - event_description varchar(255), - location varchar(255), - event_date date, - event_time time, - user_id int, - event_poster_url varchar(255), - whyJoin text, - benefits text - )engine = InnoDB; - - -- schema for news create table if not exists news @@ -155,3 +140,35 @@ create table if not exists translations ( ) engine = InnoDB; ALTER TABLE translations MODIFY value TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- create schema for event + +create table if not exists event( + event_id int primary key auto_increment, + event_title varchar(255), + event_description varchar(255), + location varchar(255), + event_date date, + event_time time, + user_id int, + event_poster_url varchar(255), + whyJoin text, + benefits text, + foreign key event_user_id_fk (user_id) references users(id) +) engine = InnoDB; + +CREATE TABLE IF NOT EXISTS user_profile ( + user_id INT PRIMARY KEY NOT NULL, + profile_picture TEXT, + bio TEXT, + phone_number VARCHAR(20), + show_phone_number BOOLEAN DEFAULT FALSE, + show_dob BOOLEAN DEFAULT FALSE, + FOREIGN KEY (user_id) REFERENCES users(id) + ON DELETE CASCADE + ON UPDATE CASCADE +) engine = InnoDB; + +CREATE TRIGGER UMustHaveProfile AFTER INSERT ON users FOR EACH ROW INSERT INTO user_profile (user_id) VALUES (NEW.id); + + diff --git a/src/main/resources/static/assets/default-profile.jpg b/src/main/resources/static/assets/default-profile.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7a8a84327d080b877818d9fb2f528a5e4784c790 Binary files /dev/null and b/src/main/resources/static/assets/default-profile.jpg differ diff --git a/src/main/resources/static/css/events/add-event.css b/src/main/resources/static/css/events/add-event.css new file mode 100644 index 0000000000000000000000000000000000000000..8c504a663f9d796ad5d9bcb75d1091e09598e0bd --- /dev/null +++ b/src/main/resources/static/css/events/add-event.css @@ -0,0 +1,122 @@ +section { + font-family: Arial, sans-serif; + padding: 20px; +} +h1 { + font-size: 24px; + margin-bottom: 20px; +} +fieldset { + border: none; + padding: 0; +} +label { + display: block; + margin-bottom: 8px; + margin-top: 10px; + font-weight: bold; +} + +input[type="date"], input[type="time"]{ + margin-bottom: 10px; + width: 80%; + padding: 10px; + box-sizing: border-box; + border-radius: 4px; + border: 1px solid #ccc; +} + +input[type="date"]:focus, input[type="time"]:focus { + /*border-color: 1px solid var(--primary-color);*/ + box-shadow: 0 0 5px var(--border-color); + outline: solid var(--border-color); +} + +fieldset > input[type="text"], textarea { + width: 95%; + padding: 10px; + margin-bottom: 10px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 16px; +} + +input[type="text"]:focus, textarea:focus{ + outline: solid var(--border-color); +} + +.event-date-and-location-card{ + display: flex; + justify-content: space-between; + width: 95%; +} +.event-date-card, .event-time-card{ + flex:1; +} +.event-location-card{ + flex: 2; +} +.event-location-input{ + width: 100%; + padding: 10px; + margin-bottom: 10px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 16px; +} + +textarea { + height: 100px; +} +.buttons { + display: flex; + justify-content: flex-end; + width: 97%; +} +button { + padding: 10px 20px; + margin-left: 10px; + border: none; + border-radius: 4px; + font-size: 16px; + cursor: pointer; +} + +.cancel { + background-color: var(--border-color); + color: var(--primary-color); +} +.submit { + background-color: var(--primary-color); + color: var(--alternate-text); +} +button:hover { + opacity: 0.7; +} + +a.button { + display: inline-block; + padding: 10px 20px; + margin-left: 10px; + font-size: 16px; + text-align: center; + text-decoration: none; + cursor: pointer; +} + +.backButton{ + color: black; + text-decoration: none; +} + + +a.button:hover { + opacity: 0.7; +} + +.alert{ + color: red; + display: block; + height: auto; + margin: 2px 0 10px; +} \ No newline at end of file diff --git a/src/main/resources/static/css/events/editEvent.css b/src/main/resources/static/css/events/editEvent.css new file mode 100644 index 0000000000000000000000000000000000000000..ba9e567176848d63a69f14c48529c1c5718e69a6 --- /dev/null +++ b/src/main/resources/static/css/events/editEvent.css @@ -0,0 +1,128 @@ +/* General Styles */ +/* Center the form on the screen */ +section { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + /*height: 100vh;*/ + padding: 0 20px; +} + +/* Form styling */ +form { + background-color: var(--secondary-color); + border-radius: 10px; + padding: 30px; + width: 100%; + max-width: 600px; + box-sizing: border-box; + margin: auto; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Add some shadow to give the form a floating effect */ +} + +/* Header */ +h1 { + text-align: left; + margin-bottom: 20px; + font-size: 24px; + color: #333; +} + +/* Label styling */ +label { + display: block; + font-weight: bold; + margin-bottom: 10px; + color: #333; + font-size: 16px; +} + +/* Input and Textarea Styling */ +input[type="text"], input[type="date"], input[type="time"], input[type="url"], textarea { + width: 100%; + padding: 12px; + margin-bottom: 20px; + border: 1px solid var(--border-color); + border-radius: 5px; + font-size: 16px; + color: #333; + background-color: var(--background-color); /* Light background for inputs */ +} + +textarea { + resize: vertical; + min-height: 120px; +} + +/* Alert message styling for errors */ +.alert { + color: var(--secondary-color); + background-color: var(--border-color); + padding: 5px; + border-radius: 5px; + font-size: 14px; +} + +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} + +/* Button styling */ +.buttons { + display: flex; + justify-content: space-between; + gap: 15px; +} + +/* Submit and Cancel button styling */ +button.submit { + background-color: #5bc0de; + color: white; + border: none; + padding: 12px 20px; + font-size: 16px; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s; +} + +button.submit:hover { + background-color: #31b0d5; +} + +#cancelButton { + background-color: #f44336; + color: white; + border: none; + padding: 12px 20px; + font-size: 16px; + border-radius: 5px; + cursor: pointer; + text-decoration: none; /* Remove underline */ + display: inline-block; + text-align: center; + transition: background-color 0.3s; +} + +#cancelButton:hover { + background-color: #e53935; +} + +/* Responsive Styles */ +@media (max-width: 768px) { + section { + padding: 20px; + } + + form { + width: 100%; + max-width: 90%; + padding: 20px; + } + + h1 { + font-size: 20px; + } +} diff --git a/src/main/resources/static/css/events/event-detail.css b/src/main/resources/static/css/events/event-detail.css index 142d7f9a263a06a7ec9c37d33dc24c4a30c08bbc..266d18d6a1c5fb7563668be4af1409e667b8388b 100644 --- a/src/main/resources/static/css/events/event-detail.css +++ b/src/main/resources/static/css/events/event-detail.css @@ -1,30 +1,12 @@ -/* Basic reset to ensure consistent styling across browsers */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: Arial, sans-serif; - background-color: #E3E2DF; - display: flex; - justify-content: center; - align-items: flex-start; - height: 100vh; /* Full height of the viewport */ - margin: 0; - padding-top: 10px; -} - #event-detail-container { - width: 80%; - max-width: 90%; - background-color: #fff; + width: 100%; + background-color: var(--secondary-color); padding: 30px; - border-radius: 10px; - box-shadow: 0 0 15px 2px #E3AFBC; - margin-top: 2rem; - background-image: linear-gradient(to right, #e3e2df,white); +} + +.backButton{ + color: var(--text-color); + text-decoration: none; } .event-detail-card { @@ -34,7 +16,7 @@ body { .event-detail-card h1 { font-size: 2rem; - color: #5D001E; + color: var(--text-color); margin-bottom: 20px; } @@ -48,14 +30,14 @@ body { .event-image { width: 80%; /* Resizes the image to 80% of the container */ height: auto; - border-radius: 8px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + border-radius: 10px; + box-shadow: 0 4px 6px var(--border-color); } /* Style for event description, date, and location */ p { font-size: 1rem; - color: #555; + color: var(--secondary-text-color); line-height: 1.6; margin-bottom: 15px; } @@ -72,15 +54,15 @@ strong { .event-benefits { margin: 20px 0; /* Adds vertical spacing between sections */ padding: 15px; /* Adds padding inside the sections */ - background-color: #E3E2DF; /* White background for each section */ - border: 1px solid #E3AFBC; /* Light border to separate sections */ + background-color: var(--border-color); /* White background for each section */ + border: 1px solid var(--primary-color); /* Light border to separate sections */ border-radius: 8px; /* Rounded corners for the section */ } /* Styling for the headings inside the event-benefits sections */ .event-benefits h2 { font-size: 24px; /* Sets a larger font size for the headings */ - color: #EE4C7C; /* Blue color for the headings */ + color: var(--text-color); /* Blue color for the headings */ margin-bottom: 10px; /* Adds space below the heading */ } @@ -106,8 +88,8 @@ strong { .register-button { - background-color: #EE4C7C; - color: white; + background-color: var(--primary-color); + color: var(--secondary-color); border: none; padding: 10px 20px; font-size: 1rem; @@ -117,5 +99,6 @@ strong { } .register-button:hover { - background-color: #9A1750; + background-color: var(--border-color); + color: var(--text-color); } diff --git a/src/main/resources/static/css/events/event.css b/src/main/resources/static/css/events/event.css index 5127df5ebfbfc3c6342d61c938679865adfd118a..33f983dd2c3a4d5c64d7db19d583893a87581f75 100644 --- a/src/main/resources/static/css/events/event.css +++ b/src/main/resources/static/css/events/event.css @@ -1,17 +1,46 @@ /* General Styles */ -body { + +section { font-family: Arial, sans-serif; - margin: 0; - padding: 0; - background-color: #E3E2DF; + /*padding: 20px;*/ +} + +.general-headings-layout h1{ + font-weight: bolder; + font-size:xx-large; +} + +.general-headings-layout button{ + padding: 10px 15px; + border: none; + font-size: 14px; + cursor: pointer; + border-radius: 5px; + background-color: var(--primary-color); + color: var(--alternate-text); +} + +.general-headings-layout button:hover { + background-color: var(--border-color); + color: var(--text-color); + border: 2px solid var(--primary-color); } /* Main container for event details */ #event-details { - padding: 20px; - background-color: #E3E2DF; + /*padding: 20px;*/ + margin: 0 5% 2% 5%; + background-color: var(--background-color); } +.general-headings-layout{ + display: flex; + align-items: center; + justify-content: space-between; + margin: 0 3%; +} + + /* Event grid container for dynamically generated cards */ .event-grid { display: grid; @@ -23,26 +52,24 @@ body { /* Styling each event card */ .event-card { - background: #fff; - border: 1px solid #ddd; + background: var(--secondary-color); + border: 2px solid var(--border-color); border-radius: 10px; - overflow: hidden; + /*overflow: hidden;*/ transition: transform 0.3s ease, box-shadow 0.3s ease; - width: 100%; - max-width: 300px; /* Ensures cards do not get too wide */ - box-shadow: 0 0 15px 2px #E3AFBC; + min-width: 60%; + max-width: 80%; /* Ensures cards do not get too wide */ + box-shadow: 0 0 15px 2px var(--primary-color); } /* Hover effect for the card */ .event-card:hover { transform: translateY(-10px); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); + box-shadow: 0 8px 16px var(--border-color); } /* Event poster styling */ .event-poster { - position: relative; - overflow: hidden; height: 200px; justify-content: center; align-items: center; @@ -51,14 +78,15 @@ body { .event-poster img { width: 100%; height: 100%; - object-fit: cover; /* Ensures the image covers the container */ + object-fit: fill; + /* Ensures the image covers the container */ } /* Title styling */ .event-card h2, .event-card h3 { font-size: 1.5em; - color: #5D001E; + color: var(--primary-color); margin: 10px 0; text-align: center; } @@ -70,41 +98,52 @@ body { margin: 10px; } -/* Remove underline from links with the class 'event-link' */ -.event-link { - text-decoration: none; /* Removes the underline */ +/* Event buttons container */ +.event-card-button { + display: flex; + /*flex-direction: column;*/ + align-items: center; /* Center buttons horizontally */ + /*gap: 10px; !* Adds space between buttons *!*/ } -/* Register button styling */ -.register-button { - display: block; - width: 50%; /* Ensures the button spans the full width of its container */ - max-width: 300px; /* You can set a max-width to control the button size */ + +/* Common button styling for both buttons */ +.event-card-button button { + max-width: 250px; /* Controls the button size */ padding: 10px; margin: 10px auto; /* Horizontally centers the button and adds vertical spacing */ - background-color: #EE4C7C; - color: #fff; + background-color: var(--primary-color); + color: var(--secondary-color); border: none; border-radius: 5px; cursor: pointer; font-size: 1em; + transition: background-color 0.3s ease, transform 0.3s ease; /* Add transition effects */ } .register-button:hover { - background-color: #9A1750; + background-color: var(--border-color); + color: var(--primary-color); } -/* Responsive adjustments */ +/* Hover effect for the buttons */ +.event-card-button button:hover { + background-color: var(--border-color); + transform: scale(1.05); /* Slight scaling effect on hover */ +} -/* On medium screens, switch to a 2-column layout */ -@media (max-width: 768px) { - .event-grid { - grid-template-columns: repeat(2, 1fr); - } +/* Remove underline from links with the class 'event-link' */ +.event-link { + text-decoration: none; /* Removes the underline */ + margin:auto; } -/* On smaller screens, switch to a single column layout */ -@media (max-width: 480px) { - .event-grid { - grid-template-columns: 1fr; /* Single column for very small screens */ - } +.event-link button { + max-width: 250px; /* Controls the button size */ + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 1em; +} +.event-link button:hover{ + color: var(--text-color); } diff --git a/src/main/resources/static/css/news/editNewsStyle.css b/src/main/resources/static/css/news/editNewsStyle.css index 789ca2f6a311df6145159d66a7af1319505ae0ed..0b575d42276f870de0cd3951de929305e89ca890 100644 --- a/src/main/resources/static/css/news/editNewsStyle.css +++ b/src/main/resources/static/css/news/editNewsStyle.css @@ -1,9 +1,3 @@ -body{ - font-family: Arial, sans-serif; - margin: 0; - padding: 0; -} - .form-container { display: flex; justify-content: center; diff --git a/src/main/resources/static/css/news/newsStyles.css b/src/main/resources/static/css/news/newsStyles.css index 97d67379050ab7b6e04020c1c14411bc1de7df95..d9abc1ac9ae2764b2f1b0800e9f9278761406bc3 100644 --- a/src/main/resources/static/css/news/newsStyles.css +++ b/src/main/resources/static/css/news/newsStyles.css @@ -1,19 +1,14 @@ -body { +.content{ font-family: Arial, sans-serif; - margin: 0; - padding: 0; - background-color: white; - color: var(--text-color); + padding: 20px; } - .general-headings-layout{ display: flex; /*margin-top: 5%;*/ align-items: center; justify-content: space-between; - /*margin-bottom: 20px;*/ - margin: 5% 8% 0 8%; + margin: 0 8% 0 8%; } h1 { diff --git a/src/main/resources/static/css/profile/profile.css b/src/main/resources/static/css/profile/profile.css new file mode 100644 index 0000000000000000000000000000000000000000..fed39bcd6e966147b770fc5ad97cf89c71312965 --- /dev/null +++ b/src/main/resources/static/css/profile/profile.css @@ -0,0 +1,105 @@ +.profile-container { + display: flex; + justify-content: center; + align-items: center; + min-height: calc(100vh - 64px - 100px); /* Adjust for header and footer */ + padding: 20px; +} + +.profile-card { + background-color: var(--secondary-color); + border-radius: 15px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + padding: 30px; + width: 100%; + max-width: 400px; + text-align: center; +} +.profile-picture { + width: 150px; + height: 150px; + border-radius: 50%; + object-fit: cover; + margin-bottom: 20px; + border: 3px solid var(--border-color); +} + +.full-name { + font-size: 24px; + margin-bottom: 10px; + color: var(--text-color); +} +.email, #edit-email { + font-size: 14px; + color: var(--text-color); + margin-bottom: 15px; +} +.organisation { + font-size: 16px; + color: var(--secondary-text-color); + margin-bottom: 15px; +} +/*#edit-organisation {*/ +/* color: var(--text-color);*/ +/*}*/ +.optional-field, #edit-bio, #edit-phone-number, #edit-phone-number { + font-size: 14px; + color: var(--text-color); + margin-bottom: 10px; +} + +.edit-profile-btn { + background-color: var(--secondary-text-color); + color: var(--text-color); + border: none; + padding: 10px 20px; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s ease; +} + +#edit-profile-form input[type="text"], +#edit-profile-form input[type="tel"], +#edit-profile-form textarea { + width: 100%; + padding: 8px; + margin-bottom: 10px; + border: 1px solid var(--secondary-text-color); + border-radius: 5px; + background-color: var(--background-color); + color: var(--text-color); +} + +.checkbox-container { + display: flex; + justify-content: space-between; + margin-bottom: 10px; +} +.not-editable { + cursor: not-allowed; + background-color: var(--secondary-color) !important; +} + +@media (max-width: 768px) { + .profile-card { + padding: 20px; + } + + .profile-picture { + width: 120px; + height: 120px; + } + + .full-name{ + font-size: 20px; + } + + .email, #edit-email { + font-size: 14px; + } + + .optional-field, #edit-bio, #edit-phone-number{ + font-size: 12px; + } +} + diff --git a/src/main/resources/static/images/logo.png b/src/main/resources/static/images/logo.png deleted file mode 100644 index b7aa35736858285f7f535c77181c595e2018765a..0000000000000000000000000000000000000000 Binary files a/src/main/resources/static/images/logo.png and /dev/null differ diff --git a/src/main/resources/static/js/profile/profile.js b/src/main/resources/static/js/profile/profile.js new file mode 100644 index 0000000000000000000000000000000000000000..7a8ccc5f9d18336d9543fd06bba0168ede8a0d40 --- /dev/null +++ b/src/main/resources/static/js/profile/profile.js @@ -0,0 +1,83 @@ +document.addEventListener('DOMContentLoaded', function () { + const profileCard = document.getElementById('profile-card'); + const editForm = document.getElementById('edit-profile-form'); + const editBtn = document.getElementById('edit-profile-btn'); + const cancelBtn = document.getElementById('cancel-edit'); + const profilePictureInput = document.getElementById('profile-picture-input'); + const changeProfilePictureBtn = document.getElementById('change-profile-picture-btn'); + const editProfilePicture = document.getElementById('edit-profile-picture'); + fetch('/profile-json') + .then(response => response.json()) + .then(profile => { + document.getElementById('profile-picture').src = profile.profilePicture || '/assets/default-profile.jpg'; + /*document.getElementById('profile-heading').innerHTML = `${profile.fullName}'s bio`;*/ + document.getElementById('full-name').innerHTML = profile.fullName; + document.getElementById('email').innerHTML = profile.email; + if (profile.organisation) { + document.getElementById('organisation').innerHTML = profile.organisation; + } + if (profile.bio) { + document.getElementById('bio').innerHTML = profile.bio; + } + if (profile.showDob && profile.dob) { + document.getElementById('date-of-birth').innerHTML = profile.dob; + } + if (profile.showPhoneNumber && profile.phoneNumber) { + document.getElementById('phone-number').innerHTML = `Phone: ${profile.phoneNumber}`; + } + populateEditForm(profile); + updateProfileCard(profile); + + }) + .catch(error => { + console.error('Error:', error); + console.error('Error stack:', error.stack); + document.getElementById('profile-container').innerHTML = 'Error loading profile.'; + }); + editBtn.addEventListener('click', () => { + profileCard.style.display = 'none'; + editForm.style.display = 'block'; + }); + + cancelBtn.addEventListener('click', () => { + editForm.style.display = 'none'; + profileCard.style.display = 'block'; + }); + changeProfilePictureBtn.addEventListener('click', () => { + profilePictureInput.click(); + }); + + profilePictureInput.addEventListener('change', (event) => { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + editProfilePicture.src = e.target.result; + }; + reader.readAsDataURL(file); + } + }); + + function updateProfileCard(profile) { + document.getElementById('profile-picture').src = profile.profilePicture || '/assets/default-profile.jpg'; + document.getElementById('full-name').textContent = profile.fullName; + document.getElementById('email').textContent = profile.email; + document.getElementById('organisation').textContent = profile.organisation || ''; + document.getElementById('bio').textContent = profile.bio || ''; + document.getElementById('date-of-birth').textContent = profile.showDob && profile.dob ? profile.dob : ''; + document.getElementById('phone-number').textContent = profile.showPhoneNumber && profile.phoneNumber ? `Phone: ${profile.phoneNumber}` : ''; + } + + function populateEditForm(profile) { + document.getElementById('edit-profile-picture').src = profile.profilePicture || '/assets/default-profile.jpg'; + document.getElementById('original-profile-picture').value = profile.profilePicture; + document.getElementById('edit-full-name').value = profile.fullName; + document.getElementById('edit-email').value = profile.email; + document.getElementById('edit-organisation').value = profile.organisation || ''; + document.getElementById('edit-bio').value = profile.bio || ''; + document.getElementById('edit-date-of-birth').value = profile.dob || ''; + document.getElementById('edit-phone-number').value = profile.phoneNumber || ''; + document.getElementById('show-dob').checked = profile.showDob; + document.getElementById('show-number').checked = profile.showPhoneNumber; + } +}); \ No newline at end of file diff --git a/src/main/resources/templates/event/add-event.html b/src/main/resources/templates/event/add-event.html new file mode 100644 index 0000000000000000000000000000000000000000..cfed5e8d9a6f6043520df66dc7709e302a791b22 --- /dev/null +++ b/src/main/resources/templates/event/add-event.html @@ -0,0 +1,77 @@ +<!DOCTYPE html> +<html 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>Add Event</title> + <link rel="stylesheet" href="/css/events/add-event.css"> +</head> +<section layout:fragment="content"> + <a th:href="@{/event}" id="backToHeadings" class="backButton"> + <p><i class="bi bi-chevron-left"></i> Back to Events</p> + </a> + + <h1 th:text="${formAction.contains('edit')} ? 'Edit Event' : 'Add New Event'">Add New Event</h1> + + <form th:action="@{${formAction}}" th:method="post" th:object="${event}" id="addEventForm"> + <fieldset> + <!-- Hidden Event ID --> + <input th:field="*{event_id}" type="hidden" /> + <input th:field="*{user_id}" type="hidden" /> + + <!-- Event Title --> + <label th:for="*{event_title}">Event Title</label> + <input type="text" th:field="*{event_title}" placeholder="Enter the title for the event..."> + <div class="alert" th:errors="*{event_title}" th:if="${#fields.hasErrors('event_title')}"></div> + + <!-- Event Description --> + <label th:for="*{description}">Event Description</label> + <textarea th:field="*{description}" placeholder="Enter the event description..."></textarea> + <div class="alert" th:errors="*{description}" th:if="${#fields.hasErrors('description')}">Description Error</div> + + <div class="event-date-and-location-card"> + <!-- Event Date --> + <div class="event-date-card"> + <label th:for="*{event_date}">Event Date</label> + <input class="event-date" type="date" th:field="*{event_date}"> + <div class="alert" th:errors="*{event_date}" th:if="${#fields.hasErrors('event_date')}">Date Error</div> + </div> + <!-- Event Time --> + <div class="event-time-card"> + <label th:for="*{event_time}">Event Time</label> + <input type="time" th:field="*{event_time}"> + <div class="alert" th:errors="*{event_time}" th:if="${#fields.hasErrors('event_time')}">Time Error</div> + </div> + <!-- Location --> + <div class="event-location-card"> + <label th:for="*{location}">Location</label> + <input class="event-location-input" type="text" th:field="*{location}" autocomplete="off" placeholder="Enter the event location..."> + <div class="alert" th:errors="*{location}" th:if="${#fields.hasErrors('location')}">Location Error</div> + </div> + </div> + + <!-- Image URL --> + <label th:for="*{imageUrl}">Image URL</label> + <input type="text" th:field="*{imageUrl}" placeholder="Enter the image URL..."> + <div class="alert" th:errors="*{imageUrl}" th:if="${#fields.hasErrors('imageUrl')}">Image URL Error</div> + + <!-- Why Join --> + <label th:for="*{whyJoin}">Why Join</label> + <textarea th:field="*{whyJoin}" placeholder="Explain why someone should join..."></textarea> + <div class="alert" th:errors="*{whyJoin}" th:if="${#fields.hasErrors('whyJoin')}">Why Join Error</div> + + <!-- Benefits --> + <label th:for="*{benefits}">Benefits</label> + <textarea th:field="*{benefits}" placeholder="List the benefits..."></textarea> + <div class="alert" th:errors="*{benefits}" th:if="${#fields.hasErrors('benefits')}">Benefits Error</div> + </fieldset> + + <!-- Buttons --> + <div class="buttons"> + <a th:href="@{/event}" id="cancelButton" class="button cancel">Cancel</a> + <button th:text="${formAction.contains('edit')}?'Update Event':'Add Event'" type="submit" class="submit">Add Event</button> + </div> + </form> +</section> +</html> diff --git a/src/main/resources/templates/event/editEvent.html b/src/main/resources/templates/event/editEvent.html new file mode 100644 index 0000000000000000000000000000000000000000..48b921ca87ec76198db8873e577342ae25465554 --- /dev/null +++ b/src/main/resources/templates/event/editEvent.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org" + xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" + layout:decorate="~{layout/layout}"> +<head> + <meta charset="UTF-8"> + <title>Edit Event</title> + <link rel="stylesheet" href="/css/events/editEvent.css"></link> +</head> +<section layout:fragment="content"> + <h1>Add New Information</h1> + <form th:action="@{/event/editEvent}" th:method="post" th:object="${editEvent}" id="addEventForm"> + <!-- Hidden Event ID --> + <input th:field="*{event_id}" type="hidden" /> + + <!-- Event Title --> + <label th:for="*{event_title}">Event Title</label> + <input type="text" th:field="*{event_title}" placeholder="Enter the title for the event..."> + <div class="alert alert-warning" th:errors="*{event_title}" th:if="${#fields.hasErrors('event_title')}">Title Error</div> + + <!-- Event Description --> + <label th:for="*{description}">Event Description</label> + <textarea th:field="*{description}" placeholder="Enter the event description..."></textarea> + <div class="alert alert-warning" th:errors="*{description}" th:if="${#fields.hasErrors('description')}">Description Error</div> + + <!-- Event Date --> + <label th:for="*{event_date}">Event Date</label> + <input type="date" th:field="*{event_date}"> + <div class="alert alert-warning" th:errors="*{event_date}" th:if="${#fields.hasErrors('event_date')}">Date Error</div> + + <!-- Event Time --> + <label th:for="*{event_time}">Event Time</label> + <input type="time" th:field="*{event_time}"> + <div class="alert alert-warning" th:errors="*{event_time}" th:if="${#fields.hasErrors('event_time')}">Time Error</div> + + <!-- Location --> + <label th:for="*{location}">Location</label> + <input type="text" th:field="*{location}" placeholder="Enter the event location..."> + <div class="alert alert-warning" th:errors="*{location}" th:if="${#fields.hasErrors('location')}">Location Error</div> + + <!-- Image URL --> + <label th:for="*{imageUrl}">Image URL</label> + <input type="text" th:field="*{imageUrl}" placeholder="Enter the image URL..."> + <div class="alert alert-warning" th:errors="*{imageUrl}" th:if="${#fields.hasErrors('imageUrl')}">Image URL Error</div> + + <!-- Why Join --> + <label th:for="*{whyJoin}">Why Join</label> + <textarea th:field="*{whyJoin}" placeholder="Explain why someone should join..."></textarea> + <div class="alert alert-warning" th:errors="*{whyJoin}" th:if="${#fields.hasErrors('whyJoin')}">Why Join Error</div> + + <!-- Benefits --> + <label th:for="*{benefits}">Benefits</label> + <textarea th:field="*{benefits}" placeholder="List the benefits..."></textarea> + <div class="alert alert-warning" th:errors="*{benefits}" th:if="${#fields.hasErrors('benefits')}">Benefits Error</div> + + <!-- Buttons --> + <div class="buttons"> + <a href="#" id="cancelButton" class="button cancel">Cancel</a> + <button type="submit" class="submit">Add Event</button> + </div> + </form> +</section> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/event/event-detail.html b/src/main/resources/templates/event/event-detail.html index fc6eb0d7fd8ecdfa2757dbe55b4d289deea90390..a2dbb8f04bb7dc590782b2af91606539ce116b82 100644 --- a/src/main/resources/templates/event/event-detail.html +++ b/src/main/resources/templates/event/event-detail.html @@ -9,45 +9,47 @@ <title>Event Details</title> <link rel="stylesheet" href="/css/events/event-detail.css"> </head> - -<body> -<main id="event-detail-container" layout:fragment="content"> - <section class="event-detail-card"> - <article class="image-container"> - <img th:src="${event.getImageUrl()}" alt="Event Image" class="event-image"> - </article> - - <header> - <h1 th:text="${event.getEvent_title()}">Event Name</h1> - </header> - - <section class="text-details"> - <p><strong>Description:</strong> <span th:text="${event.getDescription()}">Event description</span></p> - <p><strong>Date and Time:</strong> <span th:text="${event.getEvent_date()}">Event date</span>, <span - th:text="${event.getEvent_time()}">Event time</span></p> - <p><strong>Location:</strong> <span th:text="${event.getLocation()}">Event location</span></p> - </section> - - <!-- Additional Event Information --> - <section class="event-benefits"> - <h2>Why Should You Join?</h2> - <p th:text="${event.getWhyJoin()}">Reasons to join the event</p> - </section> - - <section class="event-benefits"> - <h2>Benefits of Joining</h2> - <ul> - <li th:each="benefit : ${event.getBenefits()}"> - <span th:text="${benefit}">Benefit 1</span> - </li> - </ul> +<section layout:fragment="content"> + <main id="event-detail-container"> + <a th:href="@{/event}" id="backToHeadings" class="backButton"> + <p><i class="bi bi-chevron-left"></i> <strong>Back to Events</strong></p> + </a> + <section class="event-detail-card"> + <article class="image-container"> + <img th:src="${event.getImageUrl()}" alt="Event Image" class="event-image"> + </article> + + <header> + <h1 th:text="${event.getEvent_title()}">Event Name</h1> + </header> + + <section class="text-details"> + <p><strong>Description:</strong> <span th:text="${event.getDescription()}">Event description</span></p> + <p><strong>Date and Time:</strong> <span th:text="${event.getEvent_date()}">Event date</span>, <span + th:text="${event.getEvent_time()}">Event time</span></p> + <p><strong>Location:</strong> <span th:text="${event.getLocation()}">Event location</span></p> + </section> + + <!-- Additional Event Information --> + <section class="event-benefits"> + <h2>Why Should You Join?</h2> + <p th:text="${event.getWhyJoin()}">Reasons to join the event</p> + </section> + + <section class="event-benefits"> + <h2>Benefits of Joining</h2> + <ul> + <li th:each="benefit : ${event.getBenefits()}"> + <span th:text="${benefit}">Benefit 1</span> + </li> + </ul> + </section> + + <footer> + <button class="register-button">Register Now</button> + </footer> </section> - - <footer> - <button class="register-button">Register Now</button> - </footer> - </section> -</main> -</body> + </main> +</section> </html> diff --git a/src/main/resources/templates/event/event.html b/src/main/resources/templates/event/event.html index 6d75e5ae5be7669e90c89de2419a49d163fdb2ae..c4e3731ac6202bb777f5f59c9b499864661a5657 100644 --- a/src/main/resources/templates/event/event.html +++ b/src/main/resources/templates/event/event.html @@ -8,16 +8,19 @@ <title>Event Details</title> <link rel="stylesheet" href="/css/events/event.css"> </head> - - <body layout:fragment="content"> + <section layout:fragment="content"> <div id="event-details" class="event-details"> - <div class="event-grid"> + <div class="general-headings-layout"> + <h1>Community Events</h1> + <a th:href="@{event/add}"><button class="add-event-button">Add Event</button></a> + </div> + <div class="event-grid"> <!-- Loop through each event and create a clickable link --> <div class="event-card" th:each="event : ${events}"> <div class="event-card-content"> <a th:href="@{/event/{id}(id=${event.getEvent_id()})}" class="event-link"> <div class="event-poster"> - <img th:src="${event.getImageUrl()} " alt="Image description" id="myImage"> + <img th:src="${event.getImageUrl()} " class=".event-poster-img" alt="Image description" id="myImage"> </div> <h2 th:text="${event.getEvent_title()}">Sample Event</h2> <p><strong>Description:</strong> <span th:text="${event.getDescription()}">Event description</span></p> @@ -25,10 +28,16 @@ <p><strong>Location:</strong> <span th:text="${event.getLocation()}">Event location</span></p> </a> </div> - <button class="register-button">Register Now</button> + <div class ="event-card-button"> + <button class="register-button">Register Now</button> + <a th:href="@{/event/edit/{id}(id=${event.getEvent_id()})}" class="event-link"><button>Edit Event</button></a> + </div> </div> </div> </div> <script src="/js/event/event.js"></script> - </body> + <script th:if="${successMessage}"> + alert("Event created successfully"); + </script> + </section> </html> diff --git a/src/main/resources/templates/news/newsList.html b/src/main/resources/templates/news/newsList.html index 835352cd6f0341e4a4d2a5016e760558707b870a..2e2a97ce2b01f99b9bfcedce8662a2fcab204ff6 100644 --- a/src/main/resources/templates/news/newsList.html +++ b/src/main/resources/templates/news/newsList.html @@ -10,14 +10,10 @@ <script src="/js/news/addNews.js" defer></script> <script src="/js/news/newsScripts.js" defer></script> </head> - <section layout:fragment="content" > + <section layout:fragment="content" class="content"> <div class="general-headings-layout"> - <h1>Community News</h1> - - <button onclick="openNewsForm()" id="openModalBtn" class="openModalBtn">Add News</button> - </div> <main class="news-container"> <!-- Main news card --> diff --git a/src/main/resources/templates/profile/profilePage.html b/src/main/resources/templates/profile/profilePage.html new file mode 100644 index 0000000000000000000000000000000000000000..bba95b8f6c911dd2e9f482ed246f1ed13c3d9fe8 --- /dev/null +++ b/src/main/resources/templates/profile/profilePage.html @@ -0,0 +1,47 @@ +<!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>Profile</title> + <link rel="stylesheet" href="/css/profile/profile.css"> + <script src="/js/profile/profile.js" defer></script> + +</head> +<section class="profile-container" id="profile-container" layout:fragment="content"> + <div class="profile-card" id="profile-card"> + <!--<h3 id="profile-heading">Profile Page</h3>--> + <img id="profile-picture" src="/assets/default-profile.jpg" alt="Profile Picture" class="profile-picture"> + <div class="profile-info"> + <h2 class="full-name" id="full-name">Name</h2> + <p id="organisation" class="organisation"></p> + <p class="email" id="email"></p> + <p id="bio" class="optional-field"></p> + <p id="date-of-birth" class="optional-field"></p> + <p id="phone-number" class="optional-field"></p> + </div> + <button class="edit-profile-btn" id="edit-profile-btn">Edit Profile</button> + </div> + <form id="edit-profile-form" class="profile-card" style="display: none" th:action="@{/update}" method="post" enctype="multipart/form-data"> + <img id="edit-profile-picture" src="/assets/default-profile.jpg" alt="Profile Picture" class="profile-picture"> + <input type="file" id="profile-picture-input" name="newPicture" accept="image/*" style="display: none;"> + <br> + <button type="button" id="change-profile-picture-btn">Change Profile Picture</button> + <input type="hidden" id="original-profile-picture" name="profilePicture" readonly> + <input type="text" class="not-editable" id="edit-full-name" readonly> + <input type="text" class="not-editable" id="edit-email" readonly> + <input type="text" class="not-editable" id="edit-organisation" readonly placeholder="Organisation"> + <textarea id="edit-bio" name="bio" placeholder="Bio" class="optional-field"></textarea> + <input type="text" class="not-editable" id="edit-date-of-birth" readonly> + <input type="tel" id="edit-phone-number" name="phoneNumber" placeholder="Phone Number" class="optional-field"> + <div class="checkbox-container"> + <label><input type="checkbox" id="show-dob" name="showDob"> Show Date of Birth</label> + <label><input type="checkbox" id="show-number" name="showPhoneNumber"> Show Phone Number</label> + </div> + <button type="submit" class="edit-profile-btn">Save Changes</button> + <button type="button" id="cancel-edit" class="edit-profile-btn">Cancel</button> + </form> +</section> +</html> \ No newline at end of file diff --git a/src/test/java/polish_community_group_11/polish_community/event/TestEventPage.java b/src/test/java/polish_community_group_11/polish_community/event/TestEventPage.java index 91b12c44f0d9e64f0b256bd70ee9bad20fcefc3c..3587f3c9311467df7cf2d9a683fcf5565443e129 100644 --- a/src/test/java/polish_community_group_11/polish_community/event/TestEventPage.java +++ b/src/test/java/polish_community_group_11/polish_community/event/TestEventPage.java @@ -7,7 +7,7 @@ //import org.springframework.boot.test.context.SpringBootTest; //import org.springframework.boot.test.mock.mockito.MockBean; //import org.springframework.test.web.servlet.MockMvc; -//import polish_community_group_11.polish_community.event.services.EventRepository; +//import polish_community_group_11.polish_community.event.dao.EventRepository; // //import static org.junit.jupiter.api.Assertions.assertEquals; // diff --git a/src/test/java/polish_community_group_11/polish_community/profile/controllers/ProfileControllerTest.java b/src/test/java/polish_community_group_11/polish_community/profile/controllers/ProfileControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..571bc90b2183b9c621cfbd440420d3ea9340b969 --- /dev/null +++ b/src/test/java/polish_community_group_11/polish_community/profile/controllers/ProfileControllerTest.java @@ -0,0 +1,36 @@ +package polish_community_group_11.polish_community.profile.controllers; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithUserDetails; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import polish_community_group_11.polish_community.register.models.User; +import polish_community_group_11.polish_community.register.services.UserService; + +import static org.junit.jupiter.api.Assertions.*; +@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment. RANDOM_PORT) +@AutoConfigureMockMvc +class ProfileControllerTest { + @Autowired + MockMvc mockMvc; + @Test + @WithUserDetails("user@email.com") + void whenUserCallsProfileEndpointThenTheirProfileIsReturned() throws Exception { + + mockMvc.perform(MockMvcRequestBuilders.get("/profile-json") + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()) + .andExpect(MockMvcResultMatchers.content().json("{\"email\": \"user@email.com\"}")) + .andExpect(MockMvcResultMatchers.content().json("{\"bio\": \"Jane's bio\"}")); + } +} + + + +