소스 검색

admin panel, captcha, navbar update, bill class for future invoice (PDF), token update

mateuszsudra 2 년 전
부모
커밋
ff9df979a2
58개의 변경된 파일1440개의 추가작업 그리고 301개의 파일을 삭제
  1. 4 4
      boat-reservation-logic/pom.xml
  2. 32 0
      boat-reservation-logic/src/main/java/pl/sudra/configuration/EmailManagerConfiguration.java
  3. 1 1
      boat-reservation-logic/src/main/java/pl/sudra/configuration/HibernatePersistenceConfiguration.java
  4. 34 18
      boat-reservation-logic/src/main/java/pl/sudra/configuration/SpringConfiguration.java
  5. 1 0
      boat-reservation-logic/src/main/java/pl/sudra/configuration/SpringInit.java
  6. 40 1
      boat-reservation-logic/src/main/java/pl/sudra/controller/ReservationController.java
  7. 40 1
      boat-reservation-logic/src/main/java/pl/sudra/controller/UserController.java
  8. 84 0
      boat-reservation-logic/src/main/java/pl/sudra/domain/Bill.java
  9. 46 23
      boat-reservation-logic/src/main/java/pl/sudra/domain/Reservation.java
  10. 38 0
      boat-reservation-logic/src/main/java/pl/sudra/domain/Role.java
  11. 54 13
      boat-reservation-logic/src/main/java/pl/sudra/domain/User.java
  12. 12 0
      boat-reservation-logic/src/main/java/pl/sudra/repository/BillRepository.java
  13. 5 0
      boat-reservation-logic/src/main/java/pl/sudra/repository/ReservationRepository.java
  14. 11 0
      boat-reservation-logic/src/main/java/pl/sudra/repository/RoleRepository.java
  15. 15 15
      boat-reservation-logic/src/main/java/pl/sudra/securityController/CustomInterceptor.java
  16. 2 1
      boat-reservation-logic/src/main/java/pl/sudra/securityController/JwtTokenUtil.java
  17. 31 7
      boat-reservation-logic/src/main/java/pl/sudra/securityController/SecurityController.java
  18. 16 0
      boat-reservation-logic/src/main/java/pl/sudra/service/BillService.java
  19. 70 0
      boat-reservation-logic/src/main/java/pl/sudra/service/BillServiceImpl.java
  20. 5 0
      boat-reservation-logic/src/main/java/pl/sudra/service/EmailService.java
  21. 22 0
      boat-reservation-logic/src/main/java/pl/sudra/service/EmailServiceImpl.java
  22. 1 0
      boat-reservation-logic/src/main/java/pl/sudra/service/ReservationService.java
  23. 35 24
      boat-reservation-logic/src/main/java/pl/sudra/service/ReservationServiceImpl.java
  24. 15 0
      boat-reservation-logic/src/main/java/pl/sudra/service/RoleService.java
  25. 46 0
      boat-reservation-logic/src/main/java/pl/sudra/service/RoleServiceImpl.java
  26. 6 0
      boat-reservation-logic/src/main/java/pl/sudra/service/UserService.java
  27. 22 0
      boat-reservation-logic/src/main/java/pl/sudra/service/UserServiceImpl.java
  28. 28 0
      boat-reservation-logic/src/main/java/pl/sudra/utils/RoleConverter.java
  29. 30 0
      boat-reservation-logic/src/main/java/pl/sudra/utils/RoleListConverter.java
  30. 18 0
      boat-reservation-view/package-lock.json
  31. 1 0
      boat-reservation-view/package.json
  32. 1 1
      boat-reservation-view/src/app/add-boat-component/add-boat.component.ts
  33. 45 0
      boat-reservation-view/src/app/admin-panel/admin-panel.component.css
  34. 30 0
      boat-reservation-view/src/app/admin-panel/admin-panel.component.html
  35. 47 0
      boat-reservation-view/src/app/admin-panel/admin-panel.component.ts
  36. 16 15
      boat-reservation-view/src/app/app.module.ts
  37. 0 70
      boat-reservation-view/src/app/auth-service/auth.service.ts
  38. 1 1
      boat-reservation-view/src/app/boats-component/boats-view.component.ts
  39. 24 0
      boat-reservation-view/src/app/domain/user.ts
  40. 18 4
      boat-reservation-view/src/app/login-view/login-view.component.css
  41. 23 11
      boat-reservation-view/src/app/login-view/login-view.component.html
  42. 45 5
      boat-reservation-view/src/app/login-view/login-view.component.ts
  43. 0 0
      boat-reservation-view/src/app/manager-panel/manager-panel.component.css
  44. 1 0
      boat-reservation-view/src/app/manager-panel/manager-panel.component.html
  45. 10 0
      boat-reservation-view/src/app/manager-panel/manager-panel.component.ts
  46. 49 17
      boat-reservation-view/src/app/navbar/navbar.component.css
  47. 91 31
      boat-reservation-view/src/app/navbar/navbar.component.html
  48. 16 0
      boat-reservation-view/src/app/navbar/navbar.component.ts
  49. 30 5
      boat-reservation-view/src/app/register-view/register-view.component.css
  50. 74 14
      boat-reservation-view/src/app/register-view/register-view.component.html
  51. 39 7
      boat-reservation-view/src/app/register-view/register-view.component.ts
  52. 4 4
      boat-reservation-view/src/app/reservation-view/reservation-view.component.css
  53. 22 5
      boat-reservation-view/src/app/reservation-view/reservation-view.component.ts
  54. 44 0
      boat-reservation-view/src/app/services/auth.service.ts
  55. 2 1
      boat-reservation-view/src/app/services/boats.service.ts
  56. 0 0
      boat-reservation-view/src/app/services/reservation.service.ts
  57. 43 0
      boat-reservation-view/src/app/services/user.service.ts
  58. 0 2
      boat-reservation-view/src/styles.css

+ 4 - 4
boat-reservation-logic/pom.xml

@@ -160,10 +160,10 @@
 <!--            <artifactId>spring-security-web</artifactId>-->
 <!--            <version>6.1.0</version>-->
 <!--        </dependency>-->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-security</artifactId>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.boot</groupId>-->
+<!--            <artifactId>spring-boot-starter-security</artifactId>-->
+<!--        </dependency>-->
 
     </dependencies>
 

+ 32 - 0
boat-reservation-logic/src/main/java/pl/sudra/configuration/EmailManagerConfiguration.java

@@ -0,0 +1,32 @@
+//package pl.sudra.configuration;
+//
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.mail.javamail.JavaMailSender;
+//import org.springframework.mail.javamail.JavaMailSenderImpl;
+//
+//import java.util.Properties;
+//
+//@Configuration
+//public class EmailManagerConfiguration {
+//
+//    @Bean
+//    public JavaMailSender JavaMailSender() {
+//        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
+//        mailSender.setHost("smtp.gmail.com");
+//        mailSender.setPort(587); //SSL config
+//
+//        mailSender.setUsername("mateusz.sudra.21@gmail.com");
+//        mailSender.setPassword("jupjlcrapzgpxwah");
+//
+//        Properties props = mailSender.getJavaMailProperties();
+//        props.put("mail.transport.protocol", "smtp");
+//        props.put("mail.smtp.auth", "true");
+//        props.put("mail.smtp.starttls.enable", "true");
+//        props.put("mail.debug", "true");
+//        props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
+//
+//        return mailSender;
+//    }
+//}
+//

+ 1 - 1
boat-reservation-logic/src/main/java/pl/sudra/configuration/HibernatePersistenceConfiguration.java

@@ -25,7 +25,7 @@ public class HibernatePersistenceConfiguration {
     public DataSource getDataSource() {
         BasicDataSource dataSource = new BasicDataSource();
         dataSource.setDriverClassName("org.postgresql.Driver");
-        dataSource.setUrl("jdbc:postgresql://localhost:5432/oslo?characterEncoding=utf-8");
+        dataSource.setUrl("jdbc:postgresql://localhost:5432/oslo_boats?characterEncoding=utf-8");
         dataSource.setUsername("postgres");
         dataSource.setPassword("postgres");
         return dataSource;

+ 34 - 18
boat-reservation-logic/src/main/java/pl/sudra/configuration/SpringConfiguration.java

@@ -1,13 +1,19 @@
 package pl.sudra.configuration;
 
+import jakarta.annotation.Resource;
+import jakarta.persistence.EntityManagerFactory;
 import org.springframework.context.MessageSource;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.support.ReloadableResourceBundleMessageSource;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.orm.jpa.JpaTransactionManager;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
 import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
 import org.springframework.web.servlet.LocaleResolver;
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@@ -18,6 +24,9 @@ import org.springframework.web.servlet.i18n.CookieLocaleResolver;
 import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
 import org.springframework.web.servlet.view.InternalResourceViewResolver;
 import pl.sudra.securityController.CustomInterceptor;
+import pl.sudra.service.RoleService;
+import pl.sudra.utils.RoleConverter;
+import pl.sudra.utils.RoleListConverter;
 
 import java.time.Duration;
 import java.util.Locale;
@@ -26,6 +35,7 @@ import java.util.Locale;
 @Configuration
 @EnableWebMvc
 @EnableScheduling
+@EnableTransactionManagement
 @ComponentScan("pl.sudra")
 //@Import({SecurityConfiguration.class})
 public class SpringConfiguration implements WebMvcConfigurer {
@@ -84,28 +94,34 @@ public class SpringConfiguration implements WebMvcConfigurer {
 //    @Resource(name = "addressService")
 //    private AddressService addressService;
 //
-//    @Resource(name = "appUserRoleService")
-//    private AppUserRoleService appUserRoleService;
-//
-//    @Override
-//    public void addFormatters(FormatterRegistry formatterRegistry) {
-//        formatterRegistry.addConverter(getMyAddressConverter());
-//        formatterRegistry.addConverter(getMyUserRoleConverter());
-//        formatterRegistry.addConverter(getMyUserRoleListConverter());
-//    }
+    @Resource(name = "roleService")
+    private RoleService roleService;
+
+    @Override
+    public void addFormatters(FormatterRegistry formatterRegistry) {
+        formatterRegistry.addConverter(getRoleConverter());
+        formatterRegistry.addConverter(getRoleListConverter());
+    }
 
 //    @Bean
 //    public AddressConverter getMyAddressConverter() {
 //        return new AddressConverter(addressService);
 //    }
 //
-//    @Bean
-//    public AppUserRoleConverter getMyUserRoleConverter() {
-//        return new AppUserRoleConverter(appUserRoleService);
-//    }
-//
-//    @Bean
-//    public AppUserRoleListConverter getMyUserRoleListConverter() {
-//        return new AppUserRoleListConverter(appUserRoleService);
-//    }
+    @Bean
+    public RoleConverter getRoleConverter() {
+        return new RoleConverter(roleService);
+    }
+
+    @Bean
+    public RoleListConverter getRoleListConverter() {
+        return new RoleListConverter(roleService);
+    }
+
+    @Bean
+    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
+        JpaTransactionManager transactionManager = new JpaTransactionManager();
+        transactionManager.setEntityManagerFactory(entityManagerFactory);
+        return transactionManager;
+    }
 }

+ 1 - 0
boat-reservation-logic/src/main/java/pl/sudra/configuration/SpringInit.java

@@ -14,6 +14,7 @@ public class SpringInit extends AbstractAnnotationConfigDispatcherServletInitial
                 SpringConfiguration.class,
                 HibernatePersistenceConfiguration.class,
                 CorsConfig.class
+//                EmailManagerConfiguration.class
         };
     }
 

+ 40 - 1
boat-reservation-logic/src/main/java/pl/sudra/controller/ReservationController.java

@@ -2,7 +2,12 @@ package pl.sudra.controller;
 
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
+//import pl.sudra.domain.Bill;
+//import pl.sudra.domain.Reservation;
+import pl.sudra.domain.Bill;
 import pl.sudra.domain.Reservation;
+import pl.sudra.service.BillService;
+import pl.sudra.service.BoatService;
 import pl.sudra.service.ReservationService;
 
 import java.sql.Date;
@@ -12,9 +17,14 @@ import java.util.List;
 @CrossOrigin(origins = "http://localhost:1410")
 public class ReservationController {
     private final ReservationService reservationService;
+    private final BillService billService;
+    private final BoatService boatService;
 
-    public ReservationController(ReservationService reservationService) {
+    public ReservationController(ReservationService reservationService,
+                                 BillService billService, BoatService boatService) {
         this.reservationService = reservationService;
+        this.billService = billService;
+        this.boatService = boatService;
     }
 
     @RequestMapping(
@@ -32,6 +42,26 @@ public class ReservationController {
             produces = MediaType.APPLICATION_JSON_VALUE)
     public void createReservation(@RequestBody Reservation reservation) {
         System.out.println("Creating reservation");
+//        Bill bill = new Bill();
+
+//        bill.setReservation(reservation);
+//        System.out.println(reservation.getBoatId());
+//        bill.setTotalCost(
+//                this.boatService.getBoat(reservation.getBoatId()).getCost()
+//                        * (reservation.getEndHour() - reservation.getStartHour()));
+//        bill.setIssueDate(new java.sql.Date());
+//        bill.setStatus(false);
+        Bill bill = new Bill(
+                reservation,
+                "WAITING_FOR_PAYMENT",
+                new java.sql.Date(System.currentTimeMillis()),
+                this.boatService.getBoat(reservation.getBoatId()).getCost()
+                        * (reservation.getEndHour() - reservation.getStartHour()));
+
+        reservation.setBill(bill);
+        System.out.println(bill);
+        System.out.println(reservation);
+
         this.reservationService.addReservation(reservation);
     }
 
@@ -44,4 +74,13 @@ public class ReservationController {
         System.out.println("Looking for reservations");
         return this.reservationService.findReservations(boat_id, date);
     }
+
+//    @RequestMapping(
+//            value = "/getReservation",
+//            method = RequestMethod.GET,
+//            produces = MediaType.APPLICATION_JSON_VALUE)
+//    public Reservation findReservations(@RequestParam("id") Long id) {
+//        System.out.println("Looking for reservations");
+//        return this.reservationService.findById(id);
+//    }
 }

+ 40 - 1
boat-reservation-logic/src/main/java/pl/sudra/controller/UserController.java

@@ -1,6 +1,45 @@
 package pl.sudra.controller;
 
-import pl.sudra.service.ReservationService;
+import org.springframework.data.repository.query.Param;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+import pl.sudra.domain.User;
+import pl.sudra.service.UserService;
 
+import java.util.List;
+
+@RestController
+@CrossOrigin(origins = "http://localhost:1410")
 public class UserController {
+    private final UserService userService;
+
+    public UserController(UserService userService) {
+        this.userService = userService;
+    }
+
+    @RequestMapping(
+            value = "/getAllUsers",
+            method = RequestMethod.GET,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    public List<User> generateReservations() {
+        return this.userService.getAll();
+    }
+
+    @RequestMapping(
+            value = "/updateUser",
+            method = RequestMethod.GET,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    public void updateUser(@Param("id") Long id, @Param("role") String role) {
+        this.userService.updateUserRole(id, role);
+    }
+    @RequestMapping(
+            value = "/deleteUser",
+            method = RequestMethod.DELETE,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    public void deleteUser(@Param("id") Long id) {
+        this.userService.deleteUser(id);
+    }
 }

+ 84 - 0
boat-reservation-logic/src/main/java/pl/sudra/domain/Bill.java

@@ -0,0 +1,84 @@
+package pl.sudra.domain;
+
+import jakarta.persistence.*;
+import jakarta.validation.constraints.NotNull;
+
+import java.sql.Date;
+
+@Entity
+public class Bill {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    long id;
+
+    @OneToOne(mappedBy = "bill")
+    private Reservation reservation;
+
+    @NotNull
+    String status; // WAITING_FOR_PAYMENT, PAYMENT_MADE, CLOSED, CANCELLED
+    @NotNull
+    Date issueDate;
+    @NotNull
+    Float totalCost;
+
+    @Override
+    public String toString() {
+        return "Bill{" +
+                "id=" + id +
+                ", reservation=" + reservation.getId() +
+                ", isPaid=" + status +
+                ", issueDate=" + issueDate +
+                ", totalCost=" + totalCost +
+                '}';
+    }
+
+    public Bill(Reservation reservation, String status, Date issueDate, Float totalCost) {
+        this.reservation = reservation;
+        this.status = status;
+        this.issueDate = issueDate;
+        this.totalCost = totalCost;
+    }
+
+    public Bill() {
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public Reservation getReservation() {
+        return reservation;
+    }
+
+    public void setReservation(Reservation reservation) {
+        this.reservation = reservation;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public Date getIssueDate() {
+        return issueDate;
+    }
+
+    public void setIssueDate(Date issueDate) {
+        this.issueDate = issueDate;
+    }
+
+    public Float getTotalCost() {
+        return totalCost;
+    }
+
+    public void setTotalCost(Float totalCost) {
+        this.totalCost = totalCost;
+    }
+}

+ 46 - 23
boat-reservation-logic/src/main/java/pl/sudra/domain/Reservation.java

@@ -1,21 +1,23 @@
 package pl.sudra.domain;
 
-import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
+import com.fasterxml.jackson.annotation.JsonBackReference;
+import jakarta.persistence.*;
 import jakarta.validation.constraints.NotNull;
 
 import java.sql.Date;
 
 
 @Entity
+@Table(name = "reservation")
 public class Reservation {
     @Id
     @GeneratedValue(strategy = GenerationType.AUTO)
     private Long id;
+    //    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
+//    @JoinColumn(name = "user_id")
+//    @JsonBackReference
     @NotNull
-    private Long userId;
+    private Long user_id;
     @NotNull
     private Long boatId;
     @NotNull
@@ -25,14 +27,31 @@ public class Reservation {
     @NotNull
     private byte endHour;
 
-    public Reservation(Long userId, Long boatId, Date date, byte startHour, byte endHour) {
-        this.userId = userId;
-        this.boatId = boatId;
-        this.date = date;
-        this.startHour = startHour;
-        this.endHour = endHour;
+    @OneToOne(cascade = CascadeType.ALL)
+    @JoinColumn(name = "bill_id")
+    private Bill bill;
+
+    @Override
+    public String toString() {
+        return "Reservation{" +
+                "id=" + id +
+                ", user=" + user_id +
+                ", boatId=" + boatId +
+                ", date=" + date +
+                ", startHour=" + startHour +
+                ", endHour=" + endHour +
+                ", bill=" + bill.getId() +
+                '}';
     }
 
+    //    public Reservation(Long userId, Long boatId, Date date, byte startHour, byte endHour) {
+//        this.userId = userId;
+//        this.boatId = boatId;
+//        this.date = date;
+//        this.startHour = startHour;
+//        this.endHour = endHour;
+//    }
+
     public Reservation() {
     }
 
@@ -45,11 +64,11 @@ public class Reservation {
     }
 
     public Long getUserId() {
-        return userId;
+        return user_id;
     }
 
     public void setUserId(Long user_id) {
-        this.userId = user_id;
+        this.user_id = user_id;
     }
 
     public Long getBoatId() {
@@ -83,16 +102,20 @@ public class Reservation {
     public void setEndHour(byte end_hour) {
         this.endHour = end_hour;
     }
+//
+//    public Long getUserId() {
+//        return user;
+//    }
+//
+//    public void setUser(User user) {
+//        this.user = user;
+//    }
+
+    public Bill getBill() {
+        return bill;
+    }
 
-    @Override
-    public String toString() {
-        return "Reservation{" +
-                "id=" + id +
-                ", userId=" + userId +
-                ", boatId=" + boatId +
-                ", date=" + date +
-                ", startHour=" + startHour +
-                ", endHour=" + endHour +
-                '}';
+    public void setBill(Bill bill) {
+        this.bill = bill;
     }
 }

+ 38 - 0
boat-reservation-logic/src/main/java/pl/sudra/domain/Role.java

@@ -0,0 +1,38 @@
+package pl.sudra.domain;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+
+@Entity
+public class Role {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private Long id;
+    private String role;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+
+    @Override
+    public String toString() {
+        return "Role{" +
+                "id=" + id +
+                ", role='" + role + '\'' +
+                '}';
+    }
+}

+ 54 - 13
boat-reservation-logic/src/main/java/pl/sudra/domain/User.java

@@ -1,14 +1,19 @@
 package pl.sudra.domain;
 
-import com.fasterxml.jackson.annotation.JsonIgnore;
 import jakarta.persistence.*;
-import jakarta.validation.Constraint;
 import jakarta.validation.constraints.Email;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
 @Entity
+@Table(name = "user")
 public class User {
     @Id
     @GeneratedValue(strategy = GenerationType.AUTO)
@@ -28,20 +33,48 @@ public class User {
     @NotBlank(message = "Email is required")
     @Email(regexp = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$", message = "Invalid email address")
     private String email;
+    //    @NotNull
+//    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
+    private String role;
 
-    private String role = "ADMIN";
+    public void setRole(String role) {
+        this.role = role;
+    }
 
-    public User() {
+//    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
+//    private List<Reservation> reservations;
+
+//    public void setReservations(Collection<Reservation> reservations) {
+//        this.reservations = (List<Reservation>) reservations;
+//    }
+
+    @Override
+    public String toString() {
+        return "User{" +
+                "id=" + id +
+                ", username='" + username + '\'' +
+                ", password='" + password + '\'' +
+                ", email='" + email + '\'' +
+                ", role=" + role +
+//                ", reservations=" + reservations +
+                '}';
     }
 
-    public User(Long id, String username, String password, String email, String role) {
-        this.id = id;
-        this.username = username;
-        this.password = password;
-        this.email = email;
-        this.role = role;
+    public String getRolesForToken() {
+        return role;
     }
 
+    public User() {
+    }
+
+//    public User(Long id, String username, String password, String email, String role) {
+//        this.id = id;
+//        this.username = username;
+//        this.password = password;
+//        this.email = email;
+//        this.role = role;
+//    }
+
     public Long getId() {
         return id;
     }
@@ -70,9 +103,9 @@ public class User {
         return role;
     }
 
-    public void setRole(String role) {
-        this.role = role;
-    }
+//    public void setRole(String role) {
+//        this.role = role;
+//    }
 
     public String getPassword() {
         return password;
@@ -81,4 +114,12 @@ public class User {
     public void setPassword(String password) {
         this.password = password;
     }
+
+//    public List<Reservation> getReservations() {
+//        return reservations;
+//    }
+
+//    public void setReservations(List<Reservation> reservations) {
+//        this.reservations = reservations;
+//    }
 }

+ 12 - 0
boat-reservation-logic/src/main/java/pl/sudra/repository/BillRepository.java

@@ -0,0 +1,12 @@
+package pl.sudra.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import pl.sudra.domain.Bill;
+
+import java.util.List;
+
+public interface BillRepository  extends JpaRepository<Bill, Long> {
+    Bill findBoatById(long id);
+
+    List<Bill> findAllByStatus(String waitingForPayment);
+}

+ 5 - 0
boat-reservation-logic/src/main/java/pl/sudra/repository/ReservationRepository.java

@@ -2,6 +2,8 @@ package pl.sudra.repository;
 
 import jakarta.transaction.Transactional;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
 import org.springframework.stereotype.Repository;
 import pl.sudra.domain.Reservation;
 
@@ -14,4 +16,7 @@ public interface ReservationRepository extends JpaRepository<Reservation, Long>
     Reservation findReservationById(long id);
 
     List<Reservation> findAllByBoatIdAndDate(long id, Date date);
+    @Query(value = "SELECT * FROM reservation r WHERE r.boatid = :boatId AND r.date = :date", nativeQuery = true)
+    List<Reservation> findReservationsByBoatIdAndDate(@Param("boatId") Long boatId, @Param("date") Date date);
+
 }

+ 11 - 0
boat-reservation-logic/src/main/java/pl/sudra/repository/RoleRepository.java

@@ -0,0 +1,11 @@
+package pl.sudra.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import pl.sudra.domain.Role;
+
+import java.util.Optional;
+
+public interface RoleRepository extends JpaRepository<Role, Long> {
+    Optional<Role> findByRole(String role);
+    Optional<Role> findById(Long id);
+}

+ 15 - 15
boat-reservation-logic/src/main/java/pl/sudra/securityController/CustomInterceptor.java

@@ -6,8 +6,6 @@ import org.springframework.http.HttpStatus;
 import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;
 
-import java.util.ArrayList;
-import java.util.Enumeration;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
@@ -33,7 +31,10 @@ public class CustomInterceptor implements HandlerInterceptor {
 
     List<String> admin = Stream.concat(manager_and_above.stream(),
                     List.of(
-                            "/generateReservations"
+                            "/generateReservations",
+                            "/getAllUsers",
+                            "/updateUser",
+                            "/deleteUser"
                     ).stream())
             .collect(Collectors.toList());
 
@@ -46,17 +47,17 @@ public class CustomInterceptor implements HandlerInterceptor {
             response.setHeader("Access-Control-Max-Age", "3600");
             return true;
         }
-        System.out.println("test: " + request.getHeader("access-control-request-headers"));
-        System.out.println("test here: " + request.getHeader("access-control-request-headers.authorization"));
+//        System.out.println("test: " + request.getHeader("access-control-request-headers"));
+//        System.out.println("test here: " + request.getHeader("access-control-request-headers.authorization"));
 
         String endpoint = request.getRequestURI();
 //        System.out.println(request.getHeaderNames().toString());
-        Enumeration<String> headerNames = request.getHeaderNames();
-        while (headerNames.hasMoreElements()) {
-            String headerName = headerNames.nextElement();
-            String headerValue = request.getHeader(headerName);
-            System.out.println(headerName + ": " + headerValue);
-        }
+//        Enumeration<String> headerNames = request.getHeaderNames();
+//        while (headerNames.hasMoreElements()) {
+//            String headerName = headerNames.nextElement();
+//            String headerValue = request.getHeader(headerName);
+//            System.out.println(headerName + ": " + headerValue);
+//        }
         // if endpoint require no authorization
         if (noAuth.contains(endpoint)) {
             return HandlerInterceptor.super.preHandle(request, response, handler);
@@ -69,14 +70,13 @@ public class CustomInterceptor implements HandlerInterceptor {
                 // is Token valid and not expired?
                 if (JwtTokenUtil.validateToken(token)) {
                     if (customer_and_above.contains(endpoint) &&
-                            Objects.equals(JwtTokenUtil.getRoleFromToken(token), "CLIENT")) {
-                        System.out.println("customer_and_above");
+                            JwtTokenUtil.getRoleFromToken(token).contains("CLIENT")) {
                         return HandlerInterceptor.super.preHandle(request, response, handler);
                     } else if (manager_and_above.contains(endpoint) &&
-                            Objects.equals(JwtTokenUtil.getRoleFromToken(token), "MANAGER")) {
+                            JwtTokenUtil.getRoleFromToken(token).contains("MANAGER")) {
                         return HandlerInterceptor.super.preHandle(request, response, handler);
                     } else if (admin.contains(endpoint) &&
-                            Objects.equals(JwtTokenUtil.getRoleFromToken(token), "ADMIN")) {
+                            JwtTokenUtil.getRoleFromToken(token).contains("ADMIN")) {
                         return HandlerInterceptor.super.preHandle(request, response, handler);
                     } else {
                         response.setStatus(HttpStatus.FORBIDDEN.value());

+ 2 - 1
boat-reservation-logic/src/main/java/pl/sudra/securityController/JwtTokenUtil.java

@@ -17,7 +17,8 @@ public class JwtTokenUtil {
     private static final String SECRET_KEY = "SuperSecureKey2023".repeat(5); // Replace with your own secret key
     //    private static final long EXPIRATION_TIME = 86400000; // 24 hours in milliseconds
 //    private static final long EXPIRATION_TIME = 1000 * 30; // 30 sec
-    private static final long EXPIRATION_TIME = 1000 * 60 * 30; // 30 min
+//    private static final long EXPIRATION_TIME = 1000 * 60 * 30; // 30 min
+    private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 24; // 1 day
 
     public static String generateToken(String username, Long id, String role) {
         Date now = new Date();

+ 31 - 7
boat-reservation-logic/src/main/java/pl/sudra/securityController/SecurityController.java

@@ -10,12 +10,13 @@ import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.validation.BindingResult;
 import org.springframework.web.bind.annotation.*;
 import pl.sudra.domain.LoginDto;
+import pl.sudra.domain.Role;
 import pl.sudra.domain.User;
+import pl.sudra.service.EmailService;
+import pl.sudra.service.RoleService;
 import pl.sudra.service.UserService;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import org.springframework.context.support.DefaultMessageSourceResolvable;
@@ -23,12 +24,16 @@ import org.springframework.context.support.DefaultMessageSourceResolvable;
 @RestController
 @CrossOrigin(origins = "http://localhost:1410")
 public class SecurityController {
-    private UserService userService;
-    @Autowired
+    private final UserService userService;
+    private final RoleService roleService;
+//    private final EmailService emailService;
+
     private PasswordEncoder passwordEncoder;
 
-    public SecurityController(UserService userService) {
+    public SecurityController(UserService userService, RoleService roleService) {
         this.userService = userService;
+        this.roleService = roleService;
+//        this.emailService = emailService;
     }
 
     @RequestMapping(
@@ -37,6 +42,7 @@ public class SecurityController {
             produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<?> Register(@Valid @RequestBody User user, BindingResult bindingResult) {
         // validation check
+        System.out.println("register - validation check");
         if (bindingResult.hasErrors()) {
             List<String> errors = bindingResult.getAllErrors()
                     .stream()
@@ -50,6 +56,8 @@ public class SecurityController {
             return ResponseEntity.badRequest().body(errorResponse);
         }
         // duplication check
+
+        System.out.println("register - duplication check");
         boolean isUsernameNotUnique = this.userService.isUsernameNotUnique(user.getUsername());
         boolean isEmailNotUnique = this.userService.isEmailNotUnique(user.getEmail());
 
@@ -64,11 +72,27 @@ public class SecurityController {
             return ResponseEntity.badRequest().body(errorResponse);
         }
 
+        System.out.println("register - encoding password");
         String hashedPassword = passwordEncoder.encode(user.getPassword());
         user.setPassword(hashedPassword);
 
+        System.out.println("register - setting client role");
+        Role client_role = roleService.getRole(0L);
+        System.out.println("CLIENT ROLE" + client_role.toString());
+        String roles = client_role.getRole();
+//        roles.add(client_role);
+
+        user.setRole(roles);
+
+        System.out.println("hej" + user.toString());
+
         this.userService.registerUser(user);
 
+//        this.emailService.sendMail(
+//                user.getEmail(),
+//                "Welcome in our app!",
+//                "Account Created");
+
         return ResponseEntity
                 .status(HttpStatus.OK)
                 .body("{\"message\": \"Request is valid\"}");
@@ -94,7 +118,7 @@ public class SecurityController {
                 String jwtToken = JwtTokenUtil.generateToken(
                         user.get().getUsername(),
                         user.get().getId(),
-                        user.get().getRole()
+                        user.get().getRolesForToken()
                 );
                 System.out.println(jwtToken);
 

+ 16 - 0
boat-reservation-logic/src/main/java/pl/sudra/service/BillService.java

@@ -0,0 +1,16 @@
+package pl.sudra.service;
+
+import pl.sudra.domain.Bill;
+
+import java.util.List;
+
+public interface BillService {
+
+	void addBill(Bill bill);
+	void editBill(Bill bill);
+	List<Bill> getBills();
+	void removeBill(long id);
+	Bill getBill(long id);
+	void cancelUnpaidReservations();
+	
+}

+ 70 - 0
boat-reservation-logic/src/main/java/pl/sudra/service/BillServiceImpl.java

@@ -0,0 +1,70 @@
+package pl.sudra.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import pl.sudra.domain.Bill;
+import pl.sudra.repository.BillRepository;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+
+@Service("billService")
+@Transactional
+public class BillServiceImpl implements BillService {
+    private final BillRepository billRepository;
+
+    @Autowired
+    public BillServiceImpl(BillRepository billRepository) {
+        this.billRepository = billRepository;
+    }
+
+    @Transactional
+    public void addBill(Bill bill) {
+        billRepository.save(bill);
+    }
+
+    @Transactional
+    public void editBill(Bill bill) {
+        billRepository.save(bill);
+    }
+
+    @Transactional
+    public List<Bill> getBills() {
+        return billRepository.findAll();
+    }
+
+    @Transactional
+    public void removeBill(long id) {
+        billRepository.deleteById(id);
+    }
+
+    @Override
+    public Bill getBill(long id) {
+        return billRepository.findBoatById(id);
+    }
+
+    // every day at 6 AM
+    @Scheduled(cron = "0 0 6 * * *")
+    public void cancelUnpaidReservations() {
+        System.out.println("It's 6AM. Time for cancelling unpaid reservations after 30 days from creation: ");
+        List<Bill> bills = billRepository.findAllByStatus("WAITING_FOR_PAYMENT");
+        bills.forEach(bill -> {
+            LocalDate date1 = LocalDate.parse(bill.getIssueDate().toString());
+            LocalDate date2 = LocalDate.parse(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")).toString());
+            System.out.println(date1.toString() + ">" + date2.toString());
+            if (ChronoUnit.DAYS.between(
+                    LocalDate.parse(bill.getIssueDate().toString()),
+                    LocalDate.parse(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")).toString())) > 30
+                    && date1.isBefore(date2)) {
+                bill.setStatus("CANCELLED");
+                billRepository.saveAndFlush(bill);
+                System.out.println(bill.getId());
+            }
+        });
+    }
+}
+

+ 5 - 0
boat-reservation-logic/src/main/java/pl/sudra/service/EmailService.java

@@ -0,0 +1,5 @@
+package pl.sudra.service;
+
+public interface EmailService {
+    void sendMail(String receiver, String content, String subject);
+}

+ 22 - 0
boat-reservation-logic/src/main/java/pl/sudra/service/EmailServiceImpl.java

@@ -0,0 +1,22 @@
+//package pl.sudra.service;
+//
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.mail.MailException;
+//import org.springframework.mail.SimpleMailMessage;
+//import org.springframework.mail.javamail.JavaMailSender;
+//import org.springframework.stereotype.Service;
+//
+//@Service
+//public class EmailServiceImpl implements EmailService {
+//    @Autowired
+//    private JavaMailSender javaMailSender;
+//
+//    public void sendMail(String receiver, String content, String subject) throws MailException {
+//        SimpleMailMessage mail = new SimpleMailMessage();
+//        mail.setFrom("EmailAuthor-SpringApplication");
+//        mail.setTo(receiver);
+//        mail.setSubject(subject);
+//        mail.setText(content);
+//        javaMailSender.send(mail);
+//    }
+//}

+ 1 - 0
boat-reservation-logic/src/main/java/pl/sudra/service/ReservationService.java

@@ -20,4 +20,5 @@ public interface ReservationService {
     void generateReservations(int n);
 
     List<Reservation> findReservations(long boat_id, Date date);
+    Reservation findById(long id);
 }

+ 35 - 24
boat-reservation-logic/src/main/java/pl/sudra/service/ReservationServiceImpl.java

@@ -49,36 +49,47 @@ public class ReservationServiceImpl implements ReservationService {
 
     @Override
     public void generateReservations(int n) {
-//        Date today = new Date();
-        LocalDate today = LocalDate.now();
-        System.out.println("n: " + n);
 
-        Random random = new Random();
-
-        Calendar calendar = Calendar.getInstance();
-
-        for (int i = 0; i < n; i++) {
-            byte start_hour = (byte) (random.nextInt(23 - 7) + 7);
-            byte end_hour = (byte) (random.nextInt(24 - start_hour + 1) + start_hour + 1);
+    }
 
-//            calendar.setTime(today);
-//            calendar.add(Calendar.DAY_OF_YEAR, -1 + i);
-//            System.out.println(calendar.getTime());
+//    @Override
+//    public void generateReservations(int n) {
+////        Date today = new Date();
+//        LocalDate today = LocalDate.now();
+//        System.out.println("n: " + n);
+//
+//        Random random = new Random();
+//
+//        Calendar calendar = Calendar.getInstance();
+//
+//        for (int i = 0; i < n; i++) {
+//            byte start_hour = (byte) (random.nextInt(23 - 7) + 7);
+//            byte end_hour = (byte) (random.nextInt(24 - start_hour + 1) + start_hour + 1);
+//
+////            calendar.setTime(today);
+////            calendar.add(Calendar.DAY_OF_YEAR, -1 + i);
+////            System.out.println(calendar.getTime());
+//
+////            this.reservationRepository.save(
+////                    new Reservation(
+////                            (long) i,
+////                            (long) (i % 2),
+////                            (java.sql.Date) Date.valueOf(today.plusDays(-1 + i)),
+////                            start_hour, end_hour
+////                    ).setDate;
+////            );
+//        }
+//    }
 
-            this.reservationRepository.save(
-                    new Reservation(
-                            (long) i,
-                            (long) (i % 2),
-                            (java.sql.Date) Date.valueOf(today.plusDays(-1 + i)),
-                            start_hour, end_hour
-                    )
-            );
-        }
+    @Override
+    @Transactional
+    public List<Reservation> findReservations(long boat_id, Date date) {
+        return this.reservationRepository.findReservationsByBoatIdAndDate(boat_id, date);
     }
 
     @Override
-    public List<Reservation> findReservations(long boat_id, Date date) {
-        return this.reservationRepository.findAllByBoatIdAndDate(boat_id, date);
+    public Reservation findById(long id) {
+        return this.reservationRepository.findById(id).get();
     }
 }
 

+ 15 - 0
boat-reservation-logic/src/main/java/pl/sudra/service/RoleService.java

@@ -0,0 +1,15 @@
+package pl.sudra.service;
+
+import pl.sudra.domain.Role;
+
+import java.util.List;
+
+public interface RoleService {
+
+	void addRole(Role role);
+	void editRole(Role role);
+	List<Role> getRoles();
+	void removeRole(long id);
+	Role getRole(long id);
+	
+}

+ 46 - 0
boat-reservation-logic/src/main/java/pl/sudra/service/RoleServiceImpl.java

@@ -0,0 +1,46 @@
+package pl.sudra.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import pl.sudra.domain.Role;
+import pl.sudra.repository.RoleRepository;
+
+import java.util.List;
+
+@Service("roleService")
+@Transactional
+public class RoleServiceImpl implements RoleService {
+	private final RoleRepository roleRepository;
+
+	@Autowired
+	public RoleServiceImpl(RoleRepository roleRepository) {
+		this.roleRepository = roleRepository;
+	}
+
+	@Transactional
+	public void addRole(Role role) {
+		roleRepository.save(role);
+	}
+
+	@Transactional
+	public void editRole(Role role) {
+		roleRepository.save(role);
+	}
+
+	@Transactional
+	public List<Role> getRoles() {
+		return roleRepository.findAll();
+	}
+
+	@Transactional
+	public void removeRole(long id) {
+		roleRepository.deleteById(id);
+	}
+
+	@Transactional
+	public Role getRole(long id) {
+		return roleRepository.findById(id).get();
+	}
+}
+

+ 6 - 0
boat-reservation-logic/src/main/java/pl/sudra/service/UserService.java

@@ -2,6 +2,7 @@ package pl.sudra.service;
 
 import pl.sudra.domain.User;
 
+import java.util.List;
 import java.util.Optional;
 
 public interface UserService {
@@ -12,4 +13,9 @@ public interface UserService {
     boolean isUsernameNotUnique(String username);
 
     Optional<User> findByUsername(String username);
+
+    List<User> getAll();
+    void updateUserRole(Long id, String roles);
+
+    void deleteUser(Long id);
 }

+ 22 - 0
boat-reservation-logic/src/main/java/pl/sudra/service/UserServiceImpl.java

@@ -5,6 +5,7 @@ import org.springframework.transaction.annotation.Transactional;
 import pl.sudra.domain.User;
 import pl.sudra.repository.UserRepository;
 
+import java.util.List;
 import java.util.Optional;
 
 @Service("userService")
@@ -35,4 +36,25 @@ public class UserServiceImpl implements UserService {
     public Optional<User> findByUsername(String username) {
         return this.userRepository.findByUsername(username);
     }
+
+    @Override
+    public List<User> getAll() {
+        return this.userRepository.findAll();
+    }
+
+    @Override
+    public void updateUserRole(Long id, String role) {
+        User user = this.userRepository.findById(id).get();
+        if (user.getRole().contains(role)) {
+            user.setRole(user.getRole().replace(";" + role, ""));
+        } else {
+            user.setRole(user.getRole() + ";" + role);
+        }
+        this.userRepository.save(user);
+    }
+
+    @Override
+    public void deleteUser(Long id) {
+        this.userRepository.deleteById(id);
+    }
 }

+ 28 - 0
boat-reservation-logic/src/main/java/pl/sudra/utils/RoleConverter.java

@@ -0,0 +1,28 @@
+package pl.sudra.utils;
+
+import pl.sudra.domain.Role;
+import pl.sudra.service.RoleService;
+import org.springframework.core.convert.converter.Converter;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class RoleConverter implements Converter<String, Set<Role>> {
+
+    private RoleService roleService;
+
+    //    @Autowired
+    public RoleConverter(RoleService roleService) {
+        this.roleService = roleService;
+    }
+
+    @Override
+    public Set<Role> convert(String source) {
+
+        Set<Role> userRoleList = new HashSet<Role>(0);
+
+        userRoleList.add(roleService.getRole(Integer.parseInt(source)));
+
+        return userRoleList;
+    }
+}

+ 30 - 0
boat-reservation-logic/src/main/java/pl/sudra/utils/RoleListConverter.java

@@ -0,0 +1,30 @@
+package pl.sudra.utils;
+
+import org.springframework.core.convert.converter.Converter;
+import pl.sudra.domain.Role;
+import pl.sudra.service.RoleService;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class RoleListConverter implements Converter<String[], Set<Role>> {
+
+    private RoleService roleService;
+
+    //    @Autowired
+    public RoleListConverter(RoleService roleService) {
+        this.roleService = roleService;
+    }
+
+    @Override
+    public Set<Role> convert(String[] source) {
+
+        Set<Role> userRoleList = new HashSet<Role>(0);
+
+        for (int i = 0; i < source.length; i++) {
+            userRoleList.add(roleService.getRole(Integer.parseInt(source[i])));
+        }
+
+        return userRoleList;
+    }
+}

+ 18 - 0
boat-reservation-view/package-lock.json

@@ -18,6 +18,7 @@
         "@angular/router": "^16.0.0",
         "@auth0/angular-jwt": "^5.1.2",
         "jwt-decode": "^3.1.2",
+        "ng-recaptcha": "^12.0.1",
         "node": "^20.2.0",
         "rxjs": "~7.8.0",
         "tslib": "^2.3.0",
@@ -3257,6 +3258,11 @@
         "@types/send": "*"
       }
     },
+    "node_modules/@types/grecaptcha": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@types/grecaptcha/-/grecaptcha-3.0.4.tgz",
+      "integrity": "sha512-7l1Y8DTGXkx/r4pwU1nMVAR+yD/QC+MCHKXAyEX/7JZhwcN1IED09aZ9vCjjkcGdhSQiu/eJqcXInpl6eEEEwg=="
+    },
     "node_modules/@types/http-proxy": {
       "version": "1.17.11",
       "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz",
@@ -8308,6 +8314,18 @@
       "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
       "dev": true
     },
+    "node_modules/ng-recaptcha": {
+      "version": "12.0.1",
+      "resolved": "https://registry.npmjs.org/ng-recaptcha/-/ng-recaptcha-12.0.1.tgz",
+      "integrity": "sha512-EX7IKqEn2SF8a2Enw3ggZVEjBIW2LQ52YiT2cb0/z4InikjlFoXGyM7PVb+jVvZfr5sfrxfRd/OOX29sjtWexA==",
+      "dependencies": {
+        "@types/grecaptcha": "^3.0.3",
+        "tslib": "^2.2.0"
+      },
+      "peerDependencies": {
+        "@angular/core": "^16.0.0"
+      }
+    },
     "node_modules/nice-napi": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",

+ 1 - 0
boat-reservation-view/package.json

@@ -22,6 +22,7 @@
     "@angular/router": "^16.0.0",
     "@auth0/angular-jwt": "^5.1.2",
     "jwt-decode": "^3.1.2",
+    "ng-recaptcha": "^12.0.1",
     "node": "^20.2.0",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0",

+ 1 - 1
boat-reservation-view/src/app/add-boat-component/add-boat.component.ts

@@ -1,5 +1,5 @@
 import {Component} from '@angular/core';
-import {BoatsService} from "../boats-service/boats.service";
+import {BoatsService} from "../services/boats.service";
 
 @Component({
   selector: 'app-add-boat-component',

+ 45 - 0
boat-reservation-view/src/app/admin-panel/admin-panel.component.css

@@ -0,0 +1,45 @@
+table {
+  border-collapse: collapse;
+  width: 100%;
+  background-color: white;
+}
+
+th, td {
+  border: 1px solid black;
+  padding: 8px;
+}
+
+div {
+  text-align: center;
+  font-family: Courier New, monospace;
+}
+
+.action-cell {
+  display: flex;
+  justify-content: space-evenly;
+}
+
+.button {
+  padding: 6px 10px;
+  text-decoration: none;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-weight: bold;
+  transition: background-color 0.3s ease;
+}
+
+.button.change-role {
+  background-color: #4CAF50;
+  color: white;
+}
+
+.button.delete {
+  background-color: #f44336;
+  color: white;
+}
+
+.button:hover {
+  background-color: #555;
+}
+

+ 30 - 0
boat-reservation-view/src/app/admin-panel/admin-panel.component.html

@@ -0,0 +1,30 @@
+<div>
+  <br>
+  <table>
+    <tr>
+      <th>ID</th>
+      <th>Username</th>
+      <th>Email</th>
+      <th>A</th>
+      <th>M</th>
+      <th>Actions</th>
+    </tr>
+    <tr *ngFor="let user of users">
+      <td>{{user.id}}</td>
+      <td>{{user.username}}</td>
+      <td>{{user.email}}</td>
+      <td *ngIf="user.isAdmin">✅</td>
+      <td *ngIf="!user.isAdmin">❌</td>
+      <td *ngIf="user.isManager">✅</td>
+      <td *ngIf="!user.isManager">❌</td>
+      <td>
+        <div class="action-cell">
+          <button class="button change-role" (click)="updateAdminRole(user.id)">Switch Admin</button>
+          <button class="button change-role" (click)="updateManagerRole(user.id)">Switch Manager</button>
+          <button class="button delete"  (click)="deleteUser(user.id)">Delete</button>
+        </div>
+      </td>
+    </tr>
+  </table>
+  <br>
+</div>

+ 47 - 0
boat-reservation-view/src/app/admin-panel/admin-panel.component.ts

@@ -0,0 +1,47 @@
+import {Component} from '@angular/core';
+import {BoatsService} from "../services/boats.service";
+import {UserService} from "../services/user.service";
+import {User} from "../domain/user";
+
+@Component({
+  selector: 'app-admin-panel',
+  templateUrl: './admin-panel.component.html',
+  styleUrls: ['./admin-panel.component.css']
+})
+export class AdminPanelComponent {
+  users: User[] = []
+
+  constructor(private userService: UserService) {
+    this.userService.getAllUsers().subscribe(
+      (users) => {
+        console.log(users)
+        users.forEach(user => {
+          this.users.push(
+            new User(
+              user.id,
+              user.username,
+              user.email,
+              user.role.includes("ADMIN"),
+              user.role.includes("MANAGER")))
+        })
+        this.users.sort((a, b) => a.id - b.id)
+        console.log(this.users)
+      }
+    )
+  }
+
+  updateAdminRole(id: number) {
+    this.users.find(u => u.id == id)?.switchAdmin()
+    this.userService.updateUserRole(id, "ADMIN")
+  }
+
+  updateManagerRole(id: number) {
+    this.users.find(u => u.id == id)?.switchManager()
+    this.userService.updateUserRole(id, "MANAGER")
+  }
+
+  deleteUser(id: number) {
+    this.users = this.users.filter(u => u.id !== id)
+    this.userService.deleteUser(id)
+  }
+}

+ 16 - 15
boat-reservation-view/src/app/app.module.ts

@@ -17,6 +17,9 @@ import {MapViewComponent} from './map-view/map-view.component';
 import {AddBoatComponent} from './add-boat-component/add-boat.component';
 import {FormsModule, ReactiveFormsModule} from "@angular/forms";
 import {NotFoundComponent} from './not-found/not-found.component';
+import {RecaptchaModule} from "ng-recaptcha";
+import { AdminPanelComponent } from './admin-panel/admin-panel.component';
+import { ManagerPanelComponent } from './manager-panel/manager-panel.component';
 
 export function setupTranslateServiceFactory(
   service: TranslateService): Function {
@@ -30,29 +33,24 @@ export function setRoutes() {
     {path: 'login', component: LoginViewComponent},
     {path: 'register', component: RegisterViewComponent}
   ];
-
-  const role = sessionStorage.getItem("role");
-
-  if (role) {
-    if (["CLIENT", "MANAGER", "ADMIN"].includes(role.toUpperCase())) {
+  const roles = sessionStorage.getItem("role")?.split(";");
+  if (roles) {
+    if (roles.includes("CLIENT")) {
       routes.push(
         {path: 'reservation', component: ReservationViewComponent}
       );
     }
-
-    if (["MANAGER", "ADMIN"].includes(role.toUpperCase())) {
+    if (roles.includes("MANAGER")) {
       routes.push(
-        {path: 'add-boat', component: AddBoatComponent})
+        {path: 'add-boat', component: AddBoatComponent},
+        {path: 'manager', component: ManagerPanelComponent})
     }
-
-    if (["ADMIN"].includes(role.toUpperCase())) {
+    if (roles.includes("ADMIN")) {
       routes.push(
-        // just for now to differentiate roles
-        {path: 'map', component: MapViewComponent}
+        {path: 'admin', component: AdminPanelComponent}
       );
     }
   }
-
   routes.push(
     {path: '**', component: NotFoundComponent}
   );
@@ -73,14 +71,17 @@ export function setRoutes() {
     ReservationViewComponent,
     MapViewComponent,
     AddBoatComponent,
-    NotFoundComponent
+    NotFoundComponent,
+    AdminPanelComponent,
+    ManagerPanelComponent
   ],
   imports: [
     BrowserModule,
     RouterModule.forRoot(setRoutes()),
     ReactiveFormsModule,
     FormsModule,
-    HttpClientModule
+    HttpClientModule,
+    RecaptchaModule
   ],
   providers: [TranslateService,
     {

+ 0 - 70
boat-reservation-view/src/app/auth-service/auth.service.ts

@@ -1,70 +0,0 @@
-import {Injectable} from '@angular/core';
-import {HttpClient, HttpHeaders} from "@angular/common/http";
-// import {JwtHelperService, JWT_OPTIONS} from "@auth0/angular-jwt";
-import jwt_decode from 'jwt-decode';
-import {Router} from "@angular/router";
-
-@Injectable({
-  providedIn: 'root'
-})
-export class AuthService {
-  constructor(private http: HttpClient,
-              private router: Router
-  ) {
-  }
-
-  login(username: string, password: string) {
-    console.log("login")
-    const url = 'http://localhost:2137/login';
-    let loginDto = {
-      "username": username,
-      "password": password
-    }
-    const headers = new HttpHeaders()
-      .set('Content-Type', 'application/json');
-
-    interface Token {
-      "exp": number,
-      "iat": number,
-      "sub": string,
-      "sub_id": string,
-      "sub_role": string
-    }
-
-    this.http.post<any>(url, loginDto, {headers}).subscribe(
-      (response) => {
-        sessionStorage.setItem('jwtToken', response.Authorization)
-
-        let decoded = jwt_decode<Token>(response.Authorization)
-
-        sessionStorage.setItem('username', decoded.sub)
-        sessionStorage.setItem('username_id', decoded.sub_id)
-        sessionStorage.setItem('role', decoded.sub_role)
-
-        window.location.href = "http://localhost:1410/"
-      },
-      (error) => {
-        console.error('Error making POST request:', error);
-      });
-  }
-
-  register(username: string, password: string, email: string) {
-    console.log("register")
-    const url = 'http://localhost:2137/register';
-    let registerDto = {
-      "username": username,
-      "password": password,
-      "email": email
-    }
-    const headers = new HttpHeaders()
-      .set('Content-Type', 'application/json');
-
-    this.http.post(url, registerDto, {headers}).subscribe(
-      (response) => {
-        console.log('POST request successful', response);
-      },
-      (error) => {
-        console.error('Error making POST request:', error);
-      });
-  }
-}

+ 1 - 1
boat-reservation-view/src/app/boats-component/boats-view.component.ts

@@ -1,5 +1,5 @@
 import {Component} from '@angular/core';
-import {BoatsService} from "../boats-service/boats.service";
+import {BoatsService} from "../services/boats.service";
 import {Boat} from "../domain/boat";
 
 @Component({

+ 24 - 0
boat-reservation-view/src/app/domain/user.ts

@@ -0,0 +1,24 @@
+export class User {
+  id = 0
+  username = ''
+  email = ''
+  isAdmin = false
+  isManager = false
+  role: string = '';
+
+  constructor(id: number, username: string, email: string, isAdmin: boolean, isManager: boolean) {
+    this.id = id;
+    this.username = username;
+    this.email = email;
+    this.isAdmin = isAdmin;
+    this.isManager = isManager;
+  }
+
+  switchAdmin(){
+    this.isAdmin = !this.isAdmin
+  }
+  switchManager(){
+    this.isManager = !this.isManager
+  }
+
+}

+ 18 - 4
boat-reservation-view/src/app/login-view/login-view.component.css

@@ -18,15 +18,29 @@ textarea {
   border-radius: 4px;
 }
 
-input[type="submit"] {
+input[type="submit"].enabled {
   background-color: #4caf50;
+}
+
+input[type="submit"].disabled {
+  background-color: darkgrey;
+}
+
+input[type="submit"].enabled:hover {
+  background-color: #45a049;
+}
+
+input[type="submit"].disabled:hover {
+  cursor: not-allowed;
+}
+
+input[type="submit"] {
   color: #fff;
   padding: 10px 16px;
   border: none;
   border-radius: 4px;
   cursor: pointer;
+  width: 70%;
+  align-self: center;
 }
 
-input[type="submit"]:hover {
-  background-color: #45a049;
-}

+ 23 - 11
boat-reservation-view/src/app/login-view/login-view.component.html

@@ -2,21 +2,33 @@
   <div style="text-align: center;">
     <h2>LOGIN</h2>
   </div>
-  <form (submit)="login()" #myForm="ngForm">
-    <label for="username" style="text-align: left;">Username:</label>
-    <input type="text"
-           id="username"
-           name="name" [(ngModel)]="formData.username" required>
+  <form (submit)="login()" [formGroup]="formData">
+    <div class="form-group">
+      <label for="username">Username:</label>
+      <input type="text"
+             id="username"
+             name="name" formControlName="username"
+             minlength="5">
+    </div>
 
-    <label for="password" style="text-align: left;">Password:</label>
-    <input type="text"
-           id="password"
-           name="name" [(ngModel)]="formData.password" required>
+    <div class="form-group">
+      <label for="password">Password:</label>
+      <input type="text"
+             id="password"
+             name="name" formControlName="password"
+             minlength="5">
+    </div>
 
-    <input type="submit" value="Login">
+    <input type="submit" value="Login"
+           [ngClass]="{'disabled': formData.invalid, 'enabled': !(formData.invalid)}"
+           [disabled]="formData.invalid">
+    <br>
+    <div style="color: #b70b0b">
+      <b>{{errors}}</b><br>
+    </div>
   </form>
   <br>
   <p>Don't have an account?</p>
-  <input type="submit" value="REGISTER HERE" routerLink="/register" routerLinkActive="activebutton">
+  <input class="enabled" type="submit" value="REGISTER HERE" routerLink="/register" routerLinkActive="activebutton">
   <br><br>
 </div>

+ 45 - 5
boat-reservation-view/src/app/login-view/login-view.component.ts

@@ -1,6 +1,8 @@
 import {Component} from '@angular/core';
-import {AuthService} from "../auth-service/auth.service";
+import {AuthService} from "../services/auth.service";
 import {Router} from "@angular/router";
+import {FormBuilder, FormGroup, Validators} from "@angular/forms";
+import jwt_decode from "jwt-decode";
 
 @Component({
   selector: 'app-login-view',
@@ -8,17 +10,55 @@ import {Router} from "@angular/router";
   styleUrls: ['./login-view.component.css']
 })
 export class LoginViewComponent {
-  formData: any = {};
+  formData: FormGroup;
+  errors = ''
 
   constructor(private authService: AuthService,
-              private router: Router) {
+              private router: Router,
+              private formBuilder: FormBuilder) {
+    this.formData = this.formBuilder.group(
+      {
+        username: ['', Validators.required],
+        password: ['', Validators.required]
+      }
+    )
   }
 
   login() {
+    interface Token {
+      "exp": number,
+      "iat": number,
+      "sub": string,
+      "sub_id": string,
+      "sub_role": string
+    }
+
     console.log("login attempt")
     console.log(this.formData)
     this.authService.login(
-      this.formData.username,
-      this.formData.password)
+      this.formData.get('username')?.value,
+      this.formData.get('password')?.value).subscribe(
+      (response) => {
+        sessionStorage.setItem('jwtToken', response.Authorization)
+
+        let decoded = jwt_decode<Token>(response.Authorization)
+
+        sessionStorage.setItem('username', decoded.sub)
+        sessionStorage.setItem('username_id', decoded.sub_id)
+        sessionStorage.setItem('role', decoded.sub_role)
+
+        window.location.href = "http://localhost:1410/"
+      },
+      (error) => {
+        console.error('Error making POST request:', error);
+        this.printErrors(error.error.errors.join(' '));
+      });
+  }
+
+  printErrors(text: string): void {
+    this.errors = text;
+    setTimeout(() => {
+      this.errors = '';
+    }, 10000); // 10 seconds
   }
 }

+ 0 - 0
boat-reservation-view/src/app/manager-panel/manager-panel.component.css


+ 1 - 0
boat-reservation-view/src/app/manager-panel/manager-panel.component.html

@@ -0,0 +1 @@
+<p>manager-panel works!</p>

+ 10 - 0
boat-reservation-view/src/app/manager-panel/manager-panel.component.ts

@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'app-manager-panel',
+  templateUrl: './manager-panel.component.html',
+  styleUrls: ['./manager-panel.component.css']
+})
+export class ManagerPanelComponent {
+
+}

+ 49 - 17
boat-reservation-view/src/app/navbar/navbar.component.css

@@ -3,29 +3,61 @@ body {
   height: 100%;
 }
 
-table {
-  border-collapse: collapse;
-  width: 100%;
+td:not(:first-child) {
+  border: 1px solid rgba(0, 0, 0, 0);
+  padding: 4px;
+  text-align: center;
+  width: fit-content;
+}
+
+
+.navbar {
+  display: flex;
+  justify-content: flex-end;
+  /*background-color: #f0f0f0;*/
+  padding: 0;
   height: 100%;
 }
 
-th, td {
-  border: 1px solid rgba(0, 0, 0, 0);
-  padding-left: 8px;
-  text-align: left;
+.navbar-button {
+  padding-left: 20px;
+  padding-right: 20px;
+  color: #000;
+  font-weight: bold;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  background-color: transparent;
+  transition: background-color 0.3s ease;
 }
 
-td {
-  width: fit-content;
+.navbar-button :hover {
+  background-color: rgba(255, 255, 255, 0.2);
+  text-decoration: underline;
+  cursor: pointer;
 }
 
-td:not(:first-child) {
-  border: 1px solid rgba(0, 0, 0, 0);
-  padding: 4px;
-  text-align: center;
-  width: fit-content;
+.logo :hover{
+  cursor: pointer;
 }
 
-a:hover {
-   cursor: pointer;
- }
+.navbar a:not(:first-child) {
+  margin-left: 10px;
+  justify-content: flex-start;
+}
+
+.navbar-left {
+  padding-left: 20px;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  display: flex;
+  align-items: center;
+}
+
+.navbar-right {
+  flex-grow: 1;
+  text-align: right;
+  display: flex;
+  justify-content: flex-end;
+  padding-right: 20px;
+}

+ 91 - 31
boat-reservation-view/src/app/navbar/navbar.component.html

@@ -1,48 +1,108 @@
 <body>
-<table>
-  <tr>
-    <td>
-      <a class="logo"
-         routerLink="/"
-         routerLinkActive="activebutton">
-        Oslo Boating Service
+<div class="navbar">
+  <div class="navbar-left">
+    <a class="logo"
+       routerLink="/"
+       routerLinkActive="activebutton">
+      Oslo Boating Service
+    </a>
+  </div>
+  <div class="navbar-right" style="width:100%">
+    <div class="navbar-button"
+         routerLink="/admin"
+         routerLinkActive="activebutton"
+         *ngIf="isAdmin">
+      <a>
+        ADMIN PANEL
+      </a>
+    </div>
+    <div class="navbar-button"
+         routerLink="/manager"
+         routerLinkActive="activebutton"
+         *ngIf="isManager">
+      <a>
+        MANAGER PANEL
       </a>
-    </td>
-    <td>
-      <a class="navbar-button"
+    </div>
+    <div class="navbar-button"
          routerLink="/boats"
          routerLinkActive="activebutton">
+      <a>
         BOATS
       </a>
-    </td>
-    <td>
-      <a class="navbar-button"
-         routerLink="/map"
-         routerLinkActive="activebutton">
-        MAP
-      </a>
-    </td>
-    <td>
-      <a class="navbar-button"
+    </div>
+    <div class="navbar-button"
          routerLink="/reservation"
          routerLinkActive="activebutton">
+      <a>
         RESERVATION
       </a>
-    </td>
-    <td>
-      <a class="navbar-button"
+    </div>
+    <div class="navbar-button"
          routerLink="/login"
-         routerLinkActive="activebutton">
+         routerLinkActive="activebutton"
+         *ngIf="!isLogged">
+      <a>
         LOGIN
       </a>
-    </td>
-    <td>
-      <a class="navbar-button"
+    </div>
+    <div class="navbar-button"
          (click)="logout()"
-         routerLinkActive="activebutton">
+         routerLinkActive="activebutton"
+         *ngIf="isLogged">
+      <a>
         LOGOUT
       </a>
-    </td>
-  </tr>
-</table>
+    </div>
+  </div>
+</div>
 </body>
+
+<!--<table>-->
+<!--  <tr>-->
+<!--    <td>-->
+<!--      <a class="logo"-->
+<!--         routerLink="/"-->
+<!--         routerLinkActive="activebutton">-->
+<!--        Oslo Boating Service-->
+<!--      </a>-->
+<!--    </td>-->
+<!--    <td>-->
+<!--      <a class="navbar-button"-->
+<!--         routerLink="/boats"-->
+<!--         routerLinkActive="activebutton">-->
+<!--        BOATS-->
+<!--      </a>-->
+<!--    </td>-->
+<!--    &lt;!&ndash;    <td>&ndash;&gt;-->
+<!--    &lt;!&ndash;      <a class="navbar-button"&ndash;&gt;-->
+<!--    &lt;!&ndash;         routerLink="/map"&ndash;&gt;-->
+<!--    &lt;!&ndash;         routerLinkActive="activebutton">&ndash;&gt;-->
+<!--    &lt;!&ndash;        MAP&ndash;&gt;-->
+<!--    &lt;!&ndash;      </a>&ndash;&gt;-->
+<!--    &lt;!&ndash;    </td>&ndash;&gt;-->
+<!--    <td>-->
+<!--      <a class="navbar-button"-->
+<!--         routerLink="/reservation"-->
+<!--         routerLinkActive="activebutton">-->
+<!--        RESERVATION-->
+<!--      </a>-->
+<!--    </td>-->
+<!--    <td>-->
+<!--      <a class="navbar-button"-->
+<!--         routerLink="/login"-->
+<!--         routerLinkActive="activebutton"-->
+<!--         *ngIf="!isLogged">-->
+<!--        LOGIN-->
+<!--      </a>-->
+<!--    </td>-->
+<!--    <td>-->
+<!--      <a class="navbar-button"-->
+<!--         (click)="logout()"-->
+<!--         routerLinkActive="activebutton"-->
+<!--         *ngIf="isLogged">-->
+<!--        LOGOUT-->
+<!--      </a>-->
+<!--    </td>-->
+<!--  </tr>-->
+<!--</table>-->

+ 16 - 0
boat-reservation-view/src/app/navbar/navbar.component.ts

@@ -7,7 +7,23 @@ import {Router} from "@angular/router";
   styleUrls: ['./navbar.component.css']
 })
 export class NavbarComponent {
+  isLogged: boolean = false;
+  isAdmin: boolean = false;
+  isManager: boolean = false;
+
   constructor(private router: Router) {
+    if (sessionStorage.getItem('jwtToken') !== null) {
+      this.isLogged = true
+      let roles = sessionStorage.getItem('role')
+      if(roles?.includes("ADMIN")){
+        this.isAdmin = true
+      }
+      if(roles?.includes("MANAGER")){
+        this.isManager = true
+      }
+    } else {
+      this.isLogged = false
+    }
   }
 
   logout() {

+ 30 - 5
boat-reservation-view/src/app/register-view/register-view.component.css

@@ -3,30 +3,55 @@ form {
   margin: 0 auto;
 }
 
+re-captcha{
+  text-align: center;
+}
+
 label {
   display: block;
   margin-bottom: 8px;
+  text-align: left;
 }
 
 input[type="text"],
 input[type="number"],
 textarea {
   width: 100%;
-  padding: 8px;
+  padding-top: 8px;
+  padding-bottom: 8px;
   margin-bottom: 16px;
   border: 1px solid #ccc;
   border-radius: 4px;
+
+  display:block;
+  margin-top: 0;
+  margin-left: 0;
+  margin-rigt: 0;
 }
 
-input[type="submit"] {
+input[type="submit"].enabled {
   background-color: #4caf50;
+}
+
+input[type="submit"].disabled {
+  background-color: darkgrey;
+}
+
+input[type="submit"].enabled:hover {
+  background-color: #45a049;
+}
+
+input[type="submit"].disabled:hover {
+  cursor: not-allowed;
+}
+
+input[type="submit"] {
   color: #fff;
   padding: 10px 16px;
   border: none;
   border-radius: 4px;
   cursor: pointer;
+  width: 70%;
+  align-self: center;
 }
 
-input[type="submit"]:hover {
-  background-color: #45a049;
-}

+ 74 - 14
boat-reservation-view/src/app/register-view/register-view.component.html

@@ -1,22 +1,82 @@
 <div style="text-align: center;">
   <h2>REGISTER NEW USER</h2>
 </div>
-<form (submit)="registerUser()" #myForm="ngForm" style="font-family: Courier New, monospace;">
-  <label for="username">Username:</label>
-  <input type="text"
-         id="username"
-         name="name" [(ngModel)]="formData.username" required>
+<form (submit)="registerUser()" [formGroup]="formData"
+      style="text-align: center; font-family: Courier New, monospace;">
+  <div class="form-group">
+    <label for="username">Username:</label>
+    <input type="text"
+           id="username"
+           name="name" formControlName="username"
+           minlength="5"
+           maxlength="20">
+  </div>
+  <div style="color: #b70b0b" *ngIf="formData.controls['username'].hasError('required') &&
+  (formData.controls['username'].dirty || formData.controls['username'].touched)">
+    Username is required!<br><br>
+  </div>
+  <div style="color: #b70b0b" *ngIf="(formData.controls['username'].hasError('minlength') ||
+  formData.controls['username'].hasError('maxlength')) &&
+  (formData.controls['username'].dirty || formData.controls['username'].touched)">
+    Password length has to be between 5 and 20!<br><br>
+  </div>
 
-  <label for="password">Password:</label>
-  <input type="text"
-         id="password"
-         name="name" [(ngModel)]="formData.password" required>
+  <div class="form-group">
+    <label for="password">Password:</label>
+    <input type="text"
+           id="password"
+           name="name" formControlName="password"
+           minlength="5"
+           maxlength="20">
+  </div>
+  <div style="color: #b70b0b" *ngIf="formData.controls['password'].hasError('required') &&
+  (formData.controls['password'].dirty || formData.controls['password'].touched)">
+    Password is required!<br><br>
+  </div>
+  <div style="color: #b70b0b" *ngIf="(formData.controls['password'].hasError('minlength') ||
+  formData.controls['password'].hasError('maxlength')) &&
+  (formData.controls['password'].dirty || formData.controls['password'].touched)">
+    Password length has to be between 5 and 20!<br><br>
+  </div>
 
-  <label for="email">Email:</label>
-  <input type="text"
-         id="email"
-         name="name" [(ngModel)]="formData.email" required>
+  <div class="form-group">
+    <label for="email">Email:</label>
+    <input type="text"
+           id="email"
+           name="name" formControlName="email">
+  </div>
+  <div style="color: #b70b0b" *ngIf="formData.controls['email'].hasError('required') &&
+  (formData.controls['email'].dirty || formData.controls['email'].touched)">
+    Email is required!<br><br>
+  </div>
+  <div style="color: #b70b0b" *ngIf="formData.controls['email'].hasError('email') &&
+  (formData.controls['email'].dirty || formData.controls['email'].touched)">
+    Invalid email format<br><br>
+  </div>
 
-  <input type="submit" value="Register">
+  <br>
+  <label>Captcha:</label>
+  <re-captcha
+    (resolved)="resolved($event)"
+    siteKey="6LcqrYQlAAAAAHEGNg4O9yPxxwgBTFYqAndYs6Js"
+  ></re-captcha>
+
+  <br>
+  <div style="color: #b70b0b" *ngIf="(formData.invalid) &&
+      ((formData.controls['username'].dirty || formData.controls['username'].touched)||
+      (formData.controls['password'].dirty || formData.controls['password'].touched)||
+      (formData.controls['email'].dirty || formData.controls['email'].touched))">
+    <b>Fix the errors above to proceed.</b><br>
+  </div>
+  <br>
+  <div style="color: #b70b0b">
+    <b>{{errors}}</b><br>
+  </div>
+  <br>
+  <input type="submit" value="Register"
+         [ngClass]="{
+         'disabled': formData.invalid || !isCaptchaResolved,
+         'enabled': !(formData.invalid || !isCaptchaResolved)}"
+         [disabled]="formData.invalid || !isCaptchaResolved">
 </form>
 

+ 39 - 7
boat-reservation-view/src/app/register-view/register-view.component.ts

@@ -1,6 +1,8 @@
 import {Component} from '@angular/core';
-import {BoatsService} from "../boats-service/boats.service";
-import {AuthService} from "../auth-service/auth.service";
+import {BoatsService} from "../services/boats.service";
+import {AuthService} from "../services/auth.service";
+import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
+import {delay} from "rxjs";
 
 @Component({
   selector: 'app-register-view',
@@ -8,17 +10,47 @@ import {AuthService} from "../auth-service/auth.service";
   styleUrls: ['./register-view.component.css']
 })
 export class RegisterViewComponent {
-  formData: any = {};
+  formData: FormGroup;
+  isCaptchaResolved: boolean = false;
+  errors = ""
 
-  constructor(private authService: AuthService) {
+  constructor(private authService: AuthService,
+              private formBuilder: FormBuilder) {
+    this.formData = this.formBuilder.group(
+      {
+        username: ['', Validators.required],
+        password: ['', Validators.required],
+        email: ['', [Validators.required, Validators.email]]
+      }
+    )
+  }
+
+  resolved(captchaResponse: string) {
+    console.log(`Resolved captcha with response: ${captchaResponse}`);
+    this.isCaptchaResolved = true;
   }
 
   registerUser() {
     console.log("register attempt")
     console.log(this.formData)
     this.authService.register(
-      this.formData.username,
-      this.formData.password,
-      this.formData.email)
+      this.formData.get('username')?.value,
+      this.formData.get('password')?.value,
+      this.formData.get('email')?.value).subscribe(
+      (response) => {
+        console.log('POST request successful', response);
+
+        window.location.href = "http://localhost:1410/"
+      },
+      async (error) => {
+        console.error('Error making POST request:', error);
+        this.printErrors(error.error.errors.join(' '));
+      });
+  }
+  printErrors(text: string): void {
+    this.errors = text;
+    setTimeout(() => {
+      this.errors = '';
+    }, 10000); // 10 seconds
   }
 }

+ 4 - 4
boat-reservation-view/src/app/reservation-view/reservation-view.component.css

@@ -26,6 +26,10 @@ input[type="submit"].disabled {
   background-color: darkgrey;
 }
 
+input[type="submit"].enabled:hover {
+  background-color: #45a049;
+}
+
 input[type="submit"].disabled:hover {
   cursor: not-allowed;
 }
@@ -40,10 +44,6 @@ input[type="submit"] {
   align-self: center;
 }
 
-input[type="submit"].enabled:hover {
-  background-color: #45a049;
-}
-
 #boat_label:after {
   content: " (name | capacity | cost/hour)";
   color: darkslategray;

+ 22 - 5
boat-reservation-view/src/app/reservation-view/reservation-view.component.ts

@@ -1,6 +1,6 @@
 import {Component} from '@angular/core';
-import {BoatsService} from "../boats-service/boats.service";
-import {ReservationService} from "../reservation-service/reservation.service";
+import {BoatsService} from "../services/boats.service";
+import {ReservationService} from "../services/reservation.service";
 
 @Component({
   selector: 'app-reservation-view',
@@ -53,16 +53,31 @@ export class ReservationViewComponent {
   }
 
   isItTooLateToStart() {
-    return this.fromHour <= new Date().getHours()
+    let now = new Date(new Date().setHours(0, 0, 0))
+    let selected = new Date(
+      this.date.split("-")[0],
+      this.date.split("-")[1] - 1,
+      this.date.split("-")[2],
+      1
+    )
+
+    if (now > selected) {
+      return true
+    }
+    else if (now.getDate() == selected.getDate()){
+      return this.fromHour <= new Date().getHours()
+    }
+    return false
+
   }
 
-  isFormNotValid(){
+  isFormNotValid() {
     return this.areHoursOrderWrong() || this.areHoursConflicting() || this.isDateFromPast() || this.isItTooLateToStart()
   }
 
   makeReservation() {
     let reservation_info = {
-      "userId": 0,
+      "userId": sessionStorage.getItem("username_id"),
       "boatId": this.boat,
       "date": this.date,
       "startHour": this.fromHour,
@@ -135,6 +150,7 @@ export class ReservationViewComponent {
   updateSquares() {
     this.reservationService.findReservations(parseInt(this.boat), this.date).subscribe(
       response => {
+        console.log(response)
         this.reservedHours = response.map(reservation =>
           [reservation.startHour, reservation.endHour]
         )
@@ -159,6 +175,7 @@ export class ReservationViewComponent {
 
   setHourSquaresBySelectedHours() {
     let currentHour;
+
     for (let i = this.fromHour; i < this.toHour; i++) {
       currentHour = this.hours.find(h => h.label == i)
       if (currentHour!.class == "taken") {

+ 44 - 0
boat-reservation-view/src/app/services/auth.service.ts

@@ -0,0 +1,44 @@
+import {Injectable} from '@angular/core';
+import {HttpClient, HttpHeaders} from "@angular/common/http";
+// import {JwtHelperService, JWT_OPTIONS} from "@auth0/angular-jwt";
+import jwt_decode from 'jwt-decode';
+import {Router} from "@angular/router";
+import {Observable} from "rxjs";
+
+@Injectable({
+  providedIn: 'root'
+})
+export class AuthService {
+  constructor(private http: HttpClient,
+              private router: Router
+  ) {
+  }
+
+  login(username: string, password: string): Observable<any> {
+    console.log("login")
+    const url = 'http://localhost:2137/login';
+    let loginDto = {
+      "username": username,
+      "password": password
+    }
+    const headers = new HttpHeaders()
+      .set('Content-Type', 'application/json');
+
+
+    return this.http.post<any>(url, loginDto, {headers})
+  }
+
+  register(username: string, password: string, email: string): Observable<any> {
+    console.log("register")
+    const url = 'http://localhost:2137/register';
+    let registerDto = {
+      "username": username,
+      "password": password,
+      "email": email
+    }
+    const headers = new HttpHeaders()
+      .set('Content-Type', 'application/json');
+
+    return this.http.post(url, registerDto, {headers})
+  }
+}

+ 2 - 1
boat-reservation-view/src/app/boats-service/boats.service.ts → boat-reservation-view/src/app/services/boats.service.ts

@@ -21,7 +21,8 @@ export class BoatsService {
     const url = 'http://localhost:2137/addBoat';
 
     const headers = new HttpHeaders()
-      .set('Content-Type', 'application/json');
+      .set('Content-Type', 'application/json')
+      .set('Authorization', sessionStorage.getItem("jwtToken")!);
 
     this.http.post(url, boat_info, {headers}).subscribe(
       (response) => {

+ 0 - 0
boat-reservation-view/src/app/reservation-service/reservation.service.ts → boat-reservation-view/src/app/services/reservation.service.ts


+ 43 - 0
boat-reservation-view/src/app/services/user.service.ts

@@ -0,0 +1,43 @@
+import {Injectable} from '@angular/core';
+import {HttpClient, HttpHeaders} from "@angular/common/http";
+import {Observable} from "rxjs";
+import {User} from "../domain/user";
+
+@Injectable({
+  providedIn: 'root'
+})
+export class UserService {
+
+  constructor(private http: HttpClient) {
+  }
+
+  getAllUsers(): Observable<User[]> {
+    const url = 'http://localhost:2137/getAllUsers';
+
+    const headers = new HttpHeaders()
+      .set('Content-Type', 'application/json')
+      .set('Authorization', sessionStorage.getItem("jwtToken")!);
+
+    return this.http.get<User[]>(url, {headers})
+  }
+
+  updateUserRole(id: number, role: string): void {
+    const url = 'http://localhost:2137/updateUser?id=' + id + '&role=' + role;
+
+    const headers = new HttpHeaders()
+      .set('Content-Type', 'application/json')
+      .set('Authorization', sessionStorage.getItem("jwtToken")!);
+
+    this.http.get<User[]>(url, {headers}).subscribe()
+  }
+
+  deleteUser(id: number): void {
+    const url = 'http://localhost:2137/deleteUser?id=' + id
+
+    const headers = new HttpHeaders()
+      .set('Content-Type', 'application/json')
+      .set('Authorization', sessionStorage.getItem("jwtToken")!);
+
+    this.http.delete(url, {headers}).subscribe()
+  }
+}

+ 0 - 2
boat-reservation-view/src/styles.css

@@ -26,7 +26,6 @@ html {
   font-weight: bold;
   text-decoration: none;
   color: #d1572d;
-  cursor: inherit;
   text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
 }
 
@@ -52,7 +51,6 @@ html {
 a {
   text-decoration: none;
   color: #1a0b06;
-  cursor: inherit;
 }
 
 .navbar-button {