Repository: bruch-alex/voebb-final-project
Pull request #82: User profile page
Author: login: bruch-alex
Created: 2025-05-21T14:42:30Z | Merged: 2025-05-23T09:32:53Z

## Changes

- Add rule to security config so only clients can access profile page
- Add 4 default users into `data.sql` (2 users and 2 admins)

## Screenshots

<details>
  <summary>User profile edit info page (other pages are empty now)</summary>
  
![image](https://github.com/user-attachments/assets/a78763c5-096f-4fcd-b797-660b1680d6e7)
</details>

## Updates (22.05.25):

- Replace login modal with nice dropdown
- Add user info to profile page. (Should think how to display it better, looks little bit ugly xd)
- Refactor data.sql
- Add codestyles to the project

## New screenshots

<details>
  <summary>Navbar login element if NOT logged in</summary>
  
![image](https://github.com/user-attachments/assets/47d4a8ec-0a62-4835-9ecc-1dfc273c1f80)
</details>

<details>
  <summary>Content of dropdown if NOT logged in</summary>
  
![image](https://github.com/user-attachments/assets/88492b7b-cfb5-451f-8cf9-c8b0b5ac5038)
</details>

<details>
  <summary>Navbar login element for logged in users</summary>
  
![image](https://github.com/user-attachments/assets/415bc91d-ad81-4539-9b67-e396fc5c2989)
</details>

<details>
  <summary>Content of dropdown for logged in users (Should be improved later)</summary>
  
![image](https://github.com/user-attachments/assets/cd204e93-cf6e-493b-a084-1a024e46d34e)
</details>

<details>
  <summary>User info in user profile</summary>
  
![image](https://github.com/user-attachments/assets/fba844a2-24ea-455a-9ba0-80dba6c892e4)
</details>



Diff:
diff --git a/.gitignore b/.gitignore
index 9c5fe11..2e4e939 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,7 +14,9 @@ target/
 .sts4-cache
 
 ### IntelliJ IDEA ###
-.idea
+.idea/*
+!.idea/codeStyles/
+!.idea/codeStyles/**
 *.iws
 *.iml
 *.ipr
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..a4b6b5e
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,26 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <PostgresCodeStyleSettings version="7">
+      <option name="USE_GENERAL_STYLE" value="false" />
+      <option name="KEYWORD_CASE" value="2" />
+      <option name="IDENTIFIER_CASE" value="1" />
+      <option name="TYPE_CASE" value="2" />
+      <option name="ALIAS_CASE" value="4" />
+      <option name="BUILT_IN_CASE" value="2" />
+      <option name="QUOTED_IDENTIFIER_CASE" value="1" />
+      <option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
+      <option name="INSERT_INTO_NL" value="2" />
+      <option name="INSERT_TABLE_EL_LINE" value="1" />
+      <option name="INSERT_VALUES_EL_LINE" value="1" />
+      <option name="INSERT_EL_WRAP" value="0" />
+    </PostgresCodeStyleSettings>
+    <codeStyleSettings language="HTML">
+      <indentOptions>
+        <option name="INDENT_SIZE" value="2" />
+        <option name="CONTINUATION_INDENT_SIZE" value="2" />
+        <option name="TAB_SIZE" value="2" />
+        <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
+      </indentOptions>
+    </codeStyleSettings>
+  </code_scheme>
+</component>
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </state>
+</component>
\ No newline at end of file
diff --git a/src/main/java/com/example/voebb/config/EncryptionConfig.java b/src/main/java/com/example/voebb/config/EncryptionConfig.java
index 015f92b..a6fdb0b 100644
--- a/src/main/java/com/example/voebb/config/EncryptionConfig.java
+++ b/src/main/java/com/example/voebb/config/EncryptionConfig.java
@@ -12,4 +12,5 @@ public class EncryptionConfig {
     public PasswordEncoder passwordEncoder(){
         return new BCryptPasswordEncoder();
     }
+
 }
diff --git a/src/main/java/com/example/voebb/config/SecurityConfig.java b/src/main/java/com/example/voebb/config/SecurityConfig.java
index 1a913b1..234c8c5 100644
--- a/src/main/java/com/example/voebb/config/SecurityConfig.java
+++ b/src/main/java/com/example/voebb/config/SecurityConfig.java
@@ -24,6 +24,7 @@ public SecurityConfig(PasswordEncoder passwordEncoder, CustomUserDetailsService
     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
         return http.authorizeHttpRequests(auth -> auth
                         //.requestMatchers("/admin/**").hasRole("ADMIN")
+                        .requestMatchers("/profile").hasRole("CLIENT")
                         .anyRequest().permitAll())
                 .formLogin(form -> form
                         .defaultSuccessUrl("/")
diff --git a/src/main/java/com/example/voebb/controller/web/LoginController.java b/src/main/java/com/example/voebb/controller/web/LoginController.java
index 00bd5da..8952de1 100644
--- a/src/main/java/com/example/voebb/controller/web/LoginController.java
+++ b/src/main/java/com/example/voebb/controller/web/LoginController.java
@@ -20,7 +20,7 @@ public LoginController(CustomUserDetailsService customUserDetailsService) {
 
     @GetMapping("/register")
     public String getRegisterPage(Model model) {
-        model.addAttribute("userRegistrationDTO", new UserRegistrationDTO("", "", "", "", ""));
+        model.addAttribute("userRegistrationDTO", new UserRegistrationDTO());
         return "public/login-register/register-page";
     }
 
diff --git a/src/main/java/com/example/voebb/controller/web/ProfileController.java b/src/main/java/com/example/voebb/controller/web/ProfileController.java
new file mode 100644
index 0000000..0268727
--- /dev/null
+++ b/src/main/java/com/example/voebb/controller/web/ProfileController.java
@@ -0,0 +1,41 @@
+package com.example.voebb.controller.web;
+
+import com.example.voebb.model.dto.user.UserUpdateDTO;
+import com.example.voebb.service.impl.CustomUserDetailsService;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+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.RequestMapping;
+
+import java.security.Principal;
+
+@Controller
+@RequiredArgsConstructor
+@RequestMapping("/profile")
+public class ProfileController {
+
+    private final CustomUserDetailsService userDetailsService;
+
+    @GetMapping
+    public String getProfilePage(Model model, Principal principal) {
+        model.addAttribute("userUpdateDTO", userDetailsService.getUserDTOByUsername(principal.getName()));
+        return "public/user/profile";
+    }
+
+    @PostMapping("/edit")
+    public String postEditUser(@ModelAttribute(name = "userDTO")
+                               @Valid
+                               UserUpdateDTO userUpdateDTO,
+                               Principal principal,
+                               HttpServletRequest request,
+                               HttpServletResponse response) {
+        userDetailsService.updateUserInfo(userUpdateDTO, principal.getName(), request, response);
+        return "redirect:/profile";
+    }
+}
diff --git a/src/main/java/com/example/voebb/model/dto/user/UserRegistrationDTO.java b/src/main/java/com/example/voebb/model/dto/user/UserRegistrationDTO.java
index 21694f4..80d52b4 100644
--- a/src/main/java/com/example/voebb/model/dto/user/UserRegistrationDTO.java
+++ b/src/main/java/com/example/voebb/model/dto/user/UserRegistrationDTO.java
@@ -4,24 +4,34 @@
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Pattern;
 import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
 
-public record UserRegistrationDTO(
-
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class UserRegistrationDTO{
         @NotNull(message = "Email must be not null")
         @Email(message = "Email should match pattern")
-        String email,
+        String email;
 
         @NotNull(message = "Phone Number must be not null")
         @Pattern(regexp = "^\\+[0-9]{10,15}$", message = "Invalid phone number format")
-        String phoneNumber,
+        String phoneNumber;
 
         @NotNull(message = "Password must be not null")
         @Size(min = 8, message = "Password must be at least 8 chars long")
-        String password,
+        String password;
 
         @NotNull(message = "First name must be not null")
-        String firstName,
+        String firstName;
 
         @NotNull(message = "Last name must be not null")
-        String lastName) {
+        String lastName;
 }
+
+
+
diff --git a/src/main/java/com/example/voebb/model/dto/user/UserUpdateDTO.java b/src/main/java/com/example/voebb/model/dto/user/UserUpdateDTO.java
new file mode 100644
index 0000000..d31d00d
--- /dev/null
+++ b/src/main/java/com/example/voebb/model/dto/user/UserUpdateDTO.java
@@ -0,0 +1,35 @@
+package com.example.voebb.model.dto.user;
+
+import jakarta.validation.constraints.*;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class UserUpdateDTO {
+
+    @Email(message = "Email should match pattern")
+    String email;
+
+    @Pattern(regexp = "^\\+[0-9]{10,15}$", message = "Invalid phone number format")
+    String phoneNumber;
+
+    String firstName;
+
+    String lastName;
+
+    String newPassword;
+
+    @AssertTrue(message = "New password must be at least 8 characters if provided")
+    public boolean isPasswordValid() {
+        return newPassword == null  || newPassword.isBlank() || newPassword.length() >= 8;
+    }
+
+    @NotNull(message = "Confirmation password must be not null")
+    @Size(min = 8, message = "Password must be at least 8 chars long")
+    String oldPassword;
+}
diff --git a/src/main/java/com/example/voebb/model/entity/Creator.java b/src/main/java/com/example/voebb/model/entity/Creator.java
index 205e095..a75bfbf 100644
--- a/src/main/java/com/example/voebb/model/entity/Creator.java
+++ b/src/main/java/com/example/voebb/model/entity/Creator.java
@@ -13,7 +13,6 @@
 @Entity
 @Table(name = "creators")
 public class Creator {
-
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     @Column(name = "creator_id")
diff --git a/src/main/java/com/example/voebb/model/entity/Reservation.java b/src/main/java/com/example/voebb/model/entity/Reservation.java
index 00d3641..7b6ccc5 100644
--- a/src/main/java/com/example/voebb/model/entity/Reservation.java
+++ b/src/main/java/com/example/voebb/model/entity/Reservation.java
@@ -13,7 +13,6 @@
 @Entity
 @Table(name = "reservations")
 public class Reservation {
-
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     @Column(name = "reservation_id")
diff --git a/src/main/java/com/example/voebb/service/CustomUserService.java b/src/main/java/com/example/voebb/service/CustomUserService.java
index 51c6da6..2dff881 100644
--- a/src/main/java/com/example/voebb/service/CustomUserService.java
+++ b/src/main/java/com/example/voebb/service/CustomUserService.java
@@ -1,11 +1,23 @@
 package com.example.voebb.service;
 
 import com.example.voebb.model.dto.user.UserDTO;
+import com.example.voebb.model.dto.user.UserRegistrationDTO;
+import com.example.voebb.model.dto.user.UserUpdateDTO;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.transaction.Transactional;
 
 import java.util.List;
 
 public interface CustomUserService {
     List<UserDTO> getAllUsers();
+    UserUpdateDTO getUserDTOByUsername(String username);
+
+    void updateUserInfo(UserUpdateDTO userDto,
+                        String oldEmail,
+                        HttpServletRequest request,
+                        HttpServletResponse response);
+
     UserDTO getUserById(Long id);
     UserDTO createUser(UserDTO userDto);
     UserDTO updateUser(Long id, UserDTO userDto);
diff --git a/src/main/java/com/example/voebb/service/impl/CustomUserDetailsService.java b/src/main/java/com/example/voebb/service/impl/CustomUserDetailsService.java
index cfee11e..4f2cdf2 100644
--- a/src/main/java/com/example/voebb/service/impl/CustomUserDetailsService.java
+++ b/src/main/java/com/example/voebb/service/impl/CustomUserDetailsService.java
@@ -3,19 +3,24 @@
 import com.example.voebb.exception.UserNotFoundException;
 import com.example.voebb.model.dto.user.UserDTO;
 import com.example.voebb.model.dto.user.UserRegistrationDTO;
+import com.example.voebb.model.dto.user.UserUpdateDTO;
 import com.example.voebb.model.entity.CustomUser;
 import com.example.voebb.model.entity.CustomUserRole;
 import com.example.voebb.repository.CustomUserRepo;
 import com.example.voebb.repository.CustomUserRoleRepo;
 import com.example.voebb.service.CustomUserService;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import jakarta.transaction.Transactional;
 import lombok.RequiredArgsConstructor;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
@@ -30,6 +35,7 @@ public class CustomUserDetailsService implements UserDetailsService, CustomUserS
     private final CustomUserRoleRepo customUserRoleRepo;
     private final PasswordEncoder passwordEncoder;
     private final CustomUserRepo customUserRepo;
+    private final PasswordEncoder encoder;
 
 
     @Override
@@ -75,11 +81,11 @@ public void registerUser(UserRegistrationDTO userRegistrationDTO) {
 
         CustomUser customUser = new CustomUser();
 
-        customUser.setEmail(userRegistrationDTO.email());
-        customUser.setPhoneNumber(userRegistrationDTO.phoneNumber());
-        customUser.setPassword(passwordEncoder.encode(userRegistrationDTO.password()));
-        customUser.setFirstName(userRegistrationDTO.firstName());
-        customUser.setLastName(userRegistrationDTO.lastName());
+        customUser.setEmail(userRegistrationDTO.getEmail());
+        customUser.setPhoneNumber(userRegistrationDTO.getPhoneNumber());
+        customUser.setPassword(passwordEncoder.encode(userRegistrationDTO.getPassword()));
+        customUser.setFirstName(userRegistrationDTO.getFirstName());
+        customUser.setLastName(userRegistrationDTO.getLastName());
         customUser.setEnabled(true);
         customUser.setRoles(Set.of(role));
 
@@ -93,6 +99,50 @@ public List<UserDTO> getAllUsers() {
                 .collect(Collectors.toList());
     }
 
+    @Override
+    public UserUpdateDTO getUserDTOByUsername(String username) {
+        CustomUser user = userRepo.findByEmail(username)
+                .orElseThrow(() -> new UserNotFoundException("User with username " + username + " not found"));
+        return toUpdateDto(user);
+    }
+
+    @Override
+    public void updateUserInfo(UserUpdateDTO userDto,
+                               String oldEmail,
+                               HttpServletRequest request,
+                               HttpServletResponse response){
+        boolean loginCredentialsChange = false;
+        CustomUser existingUser = userRepo.findByEmail(oldEmail)
+                .orElseThrow(() -> new UserNotFoundException("User with email " + oldEmail + " not found"));
+
+        if(!encoder.matches(userDto.getOldPassword(), existingUser.getPassword())){
+            throw new RuntimeException("Passwords are not matching");
+        }
+
+        existingUser.setFirstName(userDto.getFirstName());
+        existingUser.setLastName(userDto.getLastName());
+
+        if (!userDto.getEmail().equals(oldEmail)) {
+            existingUser.setEmail(userDto.getEmail());
+            loginCredentialsChange = true;
+        }
+
+        if (!userDto.getPhoneNumber().equals(existingUser.getPhoneNumber())) {
+            existingUser.setPhoneNumber(userDto.getPhoneNumber());
+            loginCredentialsChange = true;
+        }
+
+        if (userDto.getNewPassword() != null && !userDto.getNewPassword().isBlank()) {
+            existingUser.setPassword(encoder.encode(userDto.getNewPassword()));
+        }
+
+        userRepo.save(existingUser);
+
+        if(loginCredentialsChange){
+            new SecurityContextLogoutHandler().logout(request, response, SecurityContextHolder.getContext().getAuthentication());
+        }
+    }
+
     @Override
     public UserDTO getUserById(Long id) {
         CustomUser user = userRepo.findById(id)
@@ -195,4 +245,13 @@ private CustomUser toEntity(UserDTO dto) {
         return user;
     }
 
+
+    private UserUpdateDTO toUpdateDto(CustomUser user) {
+        UserUpdateDTO dto = new UserUpdateDTO();
+        dto.setEmail(user.getEmail());
+        dto.setPhoneNumber(user.getPhoneNumber());
+        dto.setFirstName(user.getFirstName());
+        dto.setLastName(user.getLastName());
+        return dto;
+    }
 }
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 8d76033..1c8f006 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,7 +1,3 @@
-
-## Always execute data.sql from resources when app is starting
-spring.sql.init.mode=always
-
 # Importing .env file (tests need that)
 spring.config.import=optional:file:.env[.properties]
 
@@ -68,6 +64,10 @@ spring.datasource.driver-class-name=org.postgresql.Driver
 spring.jpa.hibernate.ddl-auto=create
 spring.jpa.defer-datasource-initialization=true
 
+## Always execute data.sql from resources when app is starting
+## Does not delete old data
+spring.sql.init.mode=always
+
 # =============================================
 # ACTUATOR TODO: TBD (to be discussed)
 # =============================================
diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql
index e02c720..7d003ec 100644
--- a/src/main/resources/data.sql
+++ b/src/main/resources/data.sql
@@ -3,14 +3,17 @@ INSERT INTO creator_roles (creator_role_id, creator_role)
 VALUES (1, 'author'),
        (2, 'co-author'),
        (3, 'editor'),
-       (4, 'director');
-SELECT setval('creator_roles_creator_role_id_seq', (SELECT MAX(creator_role_id) FROM creator_roles));
+       (4, 'director')
+ON CONFLICT (creator_role_id) DO NOTHING;
+SELECT SETVAL('creator_roles_creator_role_id_seq', (SELECT MAX(creator_role_id) FROM creator_roles));
 
 -- Product types
-INSERT INTO product_types (product_type_id, name, main_creator_role_id) VALUES (1, 'book', 1) ON CONFLICT (product_type_id) DO NOTHING;
-INSERT INTO product_types (product_type_id, name, main_creator_role_id) VALUES (2, 'ebook', 1) ON CONFLICT (product_type_id) DO NOTHING;
-INSERT INTO product_types (product_type_id, name, main_creator_role_id) VALUES (3, 'DVD', 4) ON CONFLICT (product_type_id) DO NOTHING;
-SELECT setval('product_types_product_type_id_seq', (SELECT MAX(product_type_id) FROM product_types));
+INSERT INTO product_types (product_type_id, name, main_creator_role_id)
+VALUES (1, 'book', 1),
+       (2, 'ebook', 1),
+       (3, 'DVD', 4)
+ON CONFLICT (product_type_id) DO NOTHING;
+SELECT SETVAL('product_types_product_type_id_seq', (SELECT MAX(product_type_id) FROM product_types));
 
 -- Item status
 INSERT INTO item_status (item_status_id, item_status_name)
@@ -19,39 +22,40 @@ VALUES (1, 'available'),
        (3, 'borrowed'),
        (4, 'damaged'),
        (5, 'lost')
-    ON CONFLICT (item_status_id) DO NOTHING;
-SELECT setval('item_status_item_status_id_seq', (SELECT MAX(item_status_id) FROM item_status));
+ON CONFLICT (item_status_id) DO NOTHING;
+SELECT SETVAL('item_status_item_status_id_seq', (SELECT MAX(item_status_id) FROM item_status));
 
 -- Client Roles
 INSERT INTO user_roles (role_id, role_name)
 VALUES (1, 'ROLE_ADMIN'),
-       (2, 'ROLE_CLIENT');
+       (2, 'ROLE_CLIENT')
+ON CONFLICT (role_id) DO NOTHING;
+SELECT SETVAL('user_roles_role_id_seq', (SELECT MAX(role_id) FROM user_roles));;
 
 -- Languages  ─────────────────────────────────────────────────
 INSERT INTO languages (language_id, language_name)
-VALUES
-    (1, 'English'),
-    (2, 'German')
-    ON CONFLICT (language_id) DO NOTHING;
-SELECT setval('languages_language_id_seq', (SELECT MAX(language_id) FROM languages));
+VALUES (1, 'English'),
+       (2, 'German')
+ON CONFLICT (language_id) DO NOTHING;
+SELECT SETVAL('languages_language_id_seq', (SELECT MAX(language_id) FROM languages));
 -- DUMMY DATA BELLOW
 
 -- Products
 INSERT INTO products (product_id, product_type_id, product_link_to_emedia, title, release_year, photo, description)
-VALUES (1, 1, NULL, 'Harry Potter and the Philosopher''s Stone', '1997', 'photo_url_1','First book of Harry Potter series'),
-       (2, 2, 'https://ebooks.voebb.de/hp1', 'Harry Potter and the Philosopher''s Stone', '1997', 'photo_url_1','First book of Harry Potter series'),
+VALUES (1, 1, NULL, 'Harry Potter and the Philosopher''s Stone', '1997', 'photo_url_1', 'First book of Harry Potter series'),
+       (2, 2, 'https://ebooks.voebb.de/hp1', 'Harry Potter and the Philosopher''s Stone', '1997', 'photo_url_1', 'First book of Harry Potter series'),
        (3, 1, NULL, 'Harry Potter and the Chamber of Secrets', '1998', 'photo_url_2', 'Second book in the series'),
-       (4, 2, 'https://ebooks.voebb.de/hp2', 'Harry Potter and the Chamber of Secrets', '1998', 'photo_url_2','Second book in the series'),
+       (4, 2, 'https://ebooks.voebb.de/hp2', 'Harry Potter and the Chamber of Secrets', '1998', 'photo_url_2', 'Second book in the series'),
        (5, 1, NULL, 'Harry Potter and the Prisoner of Azkaban', '1999', 'photo_url_3', 'Third book in the series'),
-       (6, 2, 'https://ebooks.voebb.de/hp3', 'Harry Potter and the Goblet of Fire', '2000', 'photo_url_4','Fourth book in the series'),
+       (6, 2, 'https://ebooks.voebb.de/hp3', 'Harry Potter and the Goblet of Fire', '2000', 'photo_url_4', 'Fourth book in the series'),
        (7, 1, NULL, 'Harry Potter and the Order of the Phoenix', '2003', 'photo_url_5', 'Fifth book in the series'),
-       (8, 2, 'https://ebooks.voebb.de/hp4', 'Harry Potter and the Half-Blood Prince', '2005', 'photo_url_6','Sixth book in the series'),
+       (8, 2, 'https://ebooks.voebb.de/hp4', 'Harry Potter and the Half-Blood Prince', '2005', 'photo_url_6', 'Sixth book in the series'),
        (9, 1, NULL, 'Harry Potter and the Deathly Hallows', '2007', 'photo_url_7', 'Final book in the series'),
        (10, 1, NULL, 'Harry Potter and the Cursed Child', '2016', 'photo_url_8', 'Play based on Harry Potter universe'),
-       (11, 1, NULL, 'Fantastic Beasts and Where to Find Them', '2001', 'photo_url_9','A companion book to Harry Potter'),
-       (12, 3, NULL, 'The Matrix', '1999', 'photo_url_9','DVD format')
-    ON CONFLICT (product_id) DO NOTHING;
-SELECT setval('products_product_id_seq', (SELECT MAX(product_id) FROM products));
+       (11, 1, NULL, 'Fantastic Beasts and Where to Find Them', '2001', 'photo_url_9', 'A companion book to Harry Potter'),
+       (12, 3, NULL, 'The Matrix', '1999', 'photo_url_9', 'DVD format')
+ON CONFLICT (product_id) DO NOTHING;
+SELECT SETVAL('products_product_id_seq', (SELECT MAX(product_id) FROM products));
 
 -- Book Details (for both physical books and e-books)
 INSERT INTO book_details (product_id, book_isbn, book_edition, book_pages)
@@ -66,15 +70,15 @@ VALUES (1, '9780747532699', '1st Edition', 223),
        (9, '9780747595830', '1st Edition', 607),
        (10, '9781338216660', '1st Edition', 320),
        (11, '9781408880715', '1st Edition', 128)
-    ON CONFLICT DO NOTHING;
+ON CONFLICT DO NOTHING;
 
 -- Creators
 INSERT INTO creators (creator_id, creator_first_name, creator_last_name)
 VALUES (1, 'J. K.', 'Rowling'),
        (2, 'John', 'Tiffany'),
        (3, 'Jack', 'Thorne')
-    ON CONFLICT (creator_id) DO NOTHING;
-SELECT setval('creators_creator_id_seq', (SELECT MAX(creator_id) FROM creators));
+ON CONFLICT (creator_id) DO NOTHING;
+SELECT SETVAL('creators_creator_id_seq', (SELECT MAX(creator_id) FROM creators));
 
 -- Join table (same Creator ->  multiple  Roles; same Product -> multiple Creators)
 INSERT INTO creator_product_relation (creator_id, product_id, creator_role_id)
@@ -85,21 +89,32 @@ VALUES
 
     -- Cursed Child still has two co-authors
     (2, 10, 2), -- Tiffany  CO_AUTHOR
-    (3, 10, 2); -- Thorne   CO_AUTHOR
+    (3, 10, 2) -- Thorne   CO_AUTHOR
+ON CONFLICT(creator_id, product_id, creator_role_id) DO NOTHING;
 
 --  Clients ─────────────────────────────────────────────────
-INSERT INTO custom_users (first_name, last_name, email, phone_number, password, is_enabled, borrowed_books_count)
-VALUES ('User 1', 'One', 'test@gmail.com', '+1234567890', '12345678', true, 0),
-       ('User 2 ', 'Two', 'test@example.com', '+1234567891','1234', true, 0),
-       ('Helly ', 'R.', 'helly@example.com', '+1234567892','1234', true, 0),
-       ('Mark', 'S', 'mark@example.com', '+1234567893','1234', true, 0),
-       ('Ronald', 'B.', 'ronald@example.com', '+1234567896','1234', true, 5)
-    ON CONFLICT (custom_user_id) DO NOTHING;
+-- Test password is 12345678
+INSERT INTO custom_users (custom_user_id, first_name, last_name, email, phone_number, password, is_enabled, borrowed_books_count)
+VALUES (1, 'Admin 1', 'Admin 1', 'admin1@example.com', '+1234567890123451', '$2a$12$Rcps34Enqr7WYhMH0/POmesuIR9CiEGn1wrtq/VKqrh2H6tWmIu9e', TRUE, 0),
+       (2, 'Admin 2', 'Admin 2', 'admin2@example.com', '+1234567890123452', '$2a$12$Rcps34Enqr7WYhMH0/POmesuIR9CiEGn1wrtq/VKqrh2H6tWmIu9e', TRUE, 0),
+       (3, 'Client 1', 'Client 1', 'client1@example.com', '+1234567890123453', '$2a$12$Rcps34Enqr7WYhMH0/POmesuIR9CiEGn1wrtq/VKqrh2H6tWmIu9e', TRUE, 0),
+       (4, 'Client 2', 'Client 2', 'client2@example.com', '+1234567890123454', '$2a$12$Rcps34Enqr7WYhMH0/POmesuIR9CiEGn1wrtq/VKqrh2H6tWmIu9e', TRUE, 0),
+       (5, 'Helly ', 'R.', 'helly@example.com', '+1234567890123455', '$2a$12$Rcps34Enqr7WYhMH0/POmesuIR9CiEGn1wrtq/VKqrh2H6tWmIu9e', TRUE, 0),
+       (6, 'Mark', 'S', 'mark@example.com', '+1234567890123456', '$2a$12$Rcps34Enqr7WYhMH0/POmesuIR9CiEGn1wrtq/VKqrh2H6tWmIu9e', TRUE, 0),
+       (7, 'Ronald', 'B.', 'ronald@example.com', '+1234567890123457', '$2a$12$Rcps34Enqr7WYhMH0/POmesuIR9CiEGn1wrtq/VKqrh2H6tWmIu9e', TRUE, 5)
+ON CONFLICT (custom_user_id) DO NOTHING;
+SELECT SETVAL('custom_users_custom_user_id_seq', (SELECT MAX(custom_user_id) FROM custom_users));
 
 -- Join table (same Client -> multiple Roles)
 INSERT INTO users_roles_relation (custom_user_id, role_id)
 VALUES (1, 1),
-       (1, 2);
+       (2, 1),
+       (3, 2),
+       (4, 2),
+       (5, 2),
+       (6, 2),
+       (7, 2)
+ON CONFLICT (role_id, custom_user_id) DO NOTHING;
 
 -- Countries ─────────────────────────────────────────────────
 INSERT INTO countries (country_id, country_name)
@@ -125,154 +140,145 @@ VALUES (1, 'United Kingdom'),
        (20, 'South Africa'),
        (21, 'Argentina'),
        (22, 'New Zealand')
-    ON CONFLICT (country_id) DO NOTHING;
-SELECT setval('countries_country_id_seq', (SELECT MAX(country_id) FROM countries));
+ON CONFLICT (country_id) DO NOTHING;
+SELECT SETVAL('countries_country_id_seq', (SELECT MAX(country_id) FROM countries));
 
 -- Join table (product linked to countries)
 INSERT INTO country_relation (product_id, country_id)
-VALUES (1, 1),  -- Philosopher's Stone published in the UK
-       (1, 2),  -- ... and in the US
+VALUES (1, 1), -- Philosopher's Stone published in the UK
+       (1, 2), -- ... and in the US
        (10, 1), -- Cursed Child in the UK
-       (11, 2);  -- Fantastic Beasts in the US
+       (11, 2);
+-- Fantastic Beasts in the US
 
 -- Join table (product linked to languages)
 INSERT INTO language_relation (product_id, language_id)
 VALUES (1, 1),
        (1, 2)
-    ON CONFLICT (product_id, language_id) DO NOTHING;
+ON CONFLICT (product_id, language_id) DO NOTHING;
 
 -- Libraries ─────────────────────────────────────────────────
-INSERT INTO libraries (library_id, library_name, library_description, address_city, address_district, address_postcode, address_street, address_house_nr, address_osm_link) VALUES (1, 'Central City Library', 'Main public library in the heart of Berlin.', 'Berlin', 'Mitte', '10115', 'Hauptstraße', '123', 'https://www.openstreetmap.org/?mlat=52.5300&mlon=13.3847#map=16/52.5300/13.3847') ON CONFLICT (library_id) DO NOTHING;
-INSERT INTO libraries (library_id, library_name, library_description, address_city, address_district, address_postcode, address_street, address_house_nr, address_osm_link) VALUES (2, 'West End Library', 'A cozy library located in the Charlottenburg district.', 'Berlin', 'Charlottenburg-Wilmersdorf', '10623', 'Kaiser-Wilhelm-Straße', '45', 'https://www.openstreetmap.org/?mlat=52.5150&mlon=13.2900#map=16/52.5150/13.2900') ON CONFLICT (library_id) DO NOTHING;
-INSERT INTO libraries (library_id, library_name, library_description, address_city, address_district, address_postcode, address_street, address_house_nr, address_osm_link) VALUES (3, 'East Side Library', 'Library near the East Side Gallery, offering a variety of books.', 'Berlin', 'Friedrichshain-Kreuzberg', '10243', 'Mühlenstraße', '10', 'https://www.openstreetmap.org/?mlat=52.5074&mlon=13.4396#map=16/52.5074/13.4396') ON CONFLICT (library_id) DO NOTHING;
-INSERT INTO libraries (library_id, library_name, library_description, address_city, address_district, address_postcode, address_street, address_house_nr, address_osm_link) VALUES (4, 'North Gate Library', 'A library situated in the Reinickendorf district, known for its quiet reading rooms.', 'Berlin', 'Reinickendorf', '13407', 'Wilhelmsruher Damm', '23', 'https://www.openstreetmap.org/?mlat=52.5960&mlon=13.2905#map=16/52.5960/13.2905') ON CONFLICT (library_id) DO NOTHING;
-INSERT INTO libraries (library_id, library_name, library_description, address_city, address_district, address_postcode, address_street, address_house_nr, address_osm_link) VALUES (5, 'South Park Library', 'Located near the Tempelhofer Feld, offering a peaceful environment for reading.', 'Berlin', 'Tempelhof-Schöneberg', '12103', 'Tempelhofer Damm', '89', 'https://www.openstreetmap.org/?mlat=52.4706&mlon=13.3989#map=16/52.4706/13.3989') ON CONFLICT (library_id) DO NOTHING;
-SELECT setval('libraries_library_id_seq', (SELECT MAX(library_id) FROM libraries));
+INSERT INTO libraries (library_id, library_name, library_description, address_city, address_district, address_postcode,
+                       address_street, address_house_nr, address_osm_link)
+VALUES (1, 'Central City Library', 'Main public library in the heart of Berlin.', 'Berlin', 'Mitte', '10115', 'Hauptstraße', '123', 'https://www.openstreetmap.org/?mlat=52.5300&mlon=13.3847#map=16/52.5300/13.3847'),
+       (2, 'West End Library', 'A cozy library located in the Charlottenburg district.', 'Berlin', 'Charlottenburg-Wilmersdorf', '10623', 'Kaiser-Wilhelm-Straße', '45', 'https://www.openstreetmap.org/?mlat=52.5150&mlon=13.2900#map=16/52.5150/13.2900'),
+       (3, 'East Side Library', 'Library near the East Side Gallery, offering a variety of books.', 'Berlin', 'Friedrichshain-Kreuzberg', '10243', 'Mühlenstraße', '10', 'https://www.openstreetmap.org/?mlat=52.5074&mlon=13.4396#map=16/52.5074/13.4396'),
+       (4, 'North Gate Library', 'A library situated in the Reinickendorf district, known for its quiet reading rooms.', 'Berlin', 'Reinickendorf', '13407', 'Wilhelmsruher Damm', '23', 'https://www.openstreetmap.org/?mlat=52.5960&mlon=13.2905#map=16/52.5960/13.2905'),
+       (5, 'South Park Library', 'Located near the Tempelhofer Feld, offering a peaceful environment for reading.', 'Berlin', 'Tempelhof-Schöneberg', '12103', 'Tempelhofer Damm', '89', 'https://www.openstreetmap.org/?mlat=52.4706&mlon=13.3989#map=16/52.4706/13.3989')
+ON CONFLICT (library_id) DO NOTHING;
+SELECT SETVAL('libraries_library_id_seq', (SELECT MAX(library_id) FROM libraries));
 
 -- Items
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (1, 1, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (2, 1, 2) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (3, 1, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (4, 1, 3) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (5, 2, 2) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (6, 2, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (7, 2, 2) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (8, 3, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (9, 3, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (10, 3, 3) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (11, 3, 2) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (12, 3, 3) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (13, 4, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (14, 4, 2) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (15, 4, 2) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (16, 5, 3) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (17, 5, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (18, 5, 2) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (19, 5, 1) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (20, 6, 2) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (21, 6, 2) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (22, 6, 1) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (23, 7, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (24, 7, 3) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (25, 7, 3) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (26, 7, 2) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (27, 8, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (28, 8, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (29, 8, 2) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (30, 9, 2) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (31, 9, 3) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (32, 9, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (33, 9, 2) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (34, 10, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (35, 10, 2) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (36, 10, 3) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (37, 10, 1) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (38, 10, 2) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (39, 11, 3) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (40, 11, 2) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (41, 11, 1) ON CONFLICT (item_id) DO NOTHING;
-
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (42, 11, 4) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (43, 11, 5) ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO product_items (item_id, product_id, status_id) VALUES (44, 11, 5) ON CONFLICT (item_id) DO NOTHING;
-
-SELECT setval('product_items_item_id_seq', (SELECT MAX(item_id) FROM product_items));
+INSERT INTO product_items (item_id, product_id, status_id)
+VALUES (1, 1, 1),
+       (2, 1, 2),
+       (3, 1, 1),
+       (4, 1, 3),
+       (5, 2, 2),
+       (6, 2, 1),
+       (7, 2, 2),
+       (8, 3, 1),
+       (9, 3, 1),
+       (10, 3, 3),
+       (11, 3, 2),
+       (12, 3, 3),
+       (13, 4, 1),
+       (14, 4, 2),
+       (15, 4, 2),
+       (16, 5, 3),
+       (17, 5, 1),
+       (18, 5, 2),
+       (19, 5, 1),
+       (20, 6, 2),
+       (21, 6, 2),
+       (22, 6, 1),
+       (23, 7, 1),
+       (24, 7, 3),
+       (25, 7, 3),
+       (26, 7, 2),
+       (27, 8, 1),
+       (28, 8, 1),
+       (29, 8, 2),
+       (30, 9, 2),
+       (31, 9, 3),
+       (32, 9, 1),
+       (33, 9, 2),
+       (34, 10, 1),
+       (35, 10, 2),
+       (36, 10, 3),
+       (37, 10, 1),
+       (38, 10, 2),
+       (39, 11, 3),
+       (40, 11, 2),
+       (41, 11, 1),
+       (42, 11, 4),
+       (43, 11, 5),
+       (44, 11, 5)
+ON CONFLICT (item_id) DO NOTHING;
+SELECT SETVAL('product_items_item_id_seq', (SELECT MAX(item_id) FROM product_items));
 
 -- ItemLocation
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (1, 1, 'Shelf A-12') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (2, 3, 'Room 2-17') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (3, 5, 'Shelf B-23') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (4, 2, 'Top Rack-8') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (5, 4, 'Shelf C-4') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (6, 1, 'Room 1-19') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (7, 2, 'Lower Shelf-5') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (8, 5, 'Shelf A-3') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (9, 3, 'Room 1-22') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (10, 4, 'Shelf B-10') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (11, 1, 'Shelf C-14') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (12, 2, 'Top Rack-7') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (13, 5, 'Room 2-25') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (14, 3, 'Shelf A-9') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (15, 4, 'Shelf C-16') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (16, 1, 'Room 1-5') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (17, 2, 'Shelf B-20') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (18, 5, 'Lower Shelf-12') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (19, 3, 'Top Rack-2') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (20, 4, 'Room 2-28') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (21, 1, 'Shelf C-6') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (22, 2, 'Room 1-13') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (23, 5, 'Room 2-10') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (24, 3, 'Shelf A-4') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (25, 4, 'Shelf B-19') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (26, 1, 'Room 1-9') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (27, 2, 'Shelf C-11') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (28, 5, 'Top Rack-1') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (29, 3, 'Room 2-7') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (30, 4, 'Lower Shelf-20') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (31, 1, 'Shelf B-15') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (32, 2, 'Room 1-6') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (33, 5, 'Shelf A-22') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (34, 3, 'Room 2-19') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (35, 4, 'Shelf C-9') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (36, 1, 'Shelf A-2') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (37, 2, 'Room 2-14') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (38, 5, 'Shelf B-17') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (39, 3, 'Lower Shelf-1') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (40, 4, 'Room 1-18') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (41, 5, 'Top Rack-9') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (42, 3, 'Lower Shelf-1') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (43, 4, 'Room 1-18') ON CONFLICT (item_id) DO NOTHING;
-INSERT INTO item_location (item_id, library_id, location_in_library) VALUES (44, 5, 'Top Rack-9') ON CONFLICT (item_id) DO NOTHING;
+INSERT INTO item_location (item_id, library_id, location_in_library)
+VALUES (1, 1, 'Shelf A-12'),
+       (2, 3, 'Room 2-17'),
+       (3, 5, 'Shelf B-23'),
+       (4, 2, 'Top Rack-8'),
+       (5, 4, 'Shelf C-4'),
+       (6, 1, 'Room 1-19'),
+       (7, 2, 'Lower Shelf-5'),
+       (8, 5, 'Shelf A-3'),
+       (9, 3, 'Room 1-22'),
+       (10, 4, 'Shelf B-10'),
+       (11, 1, 'Shelf C-14'),
+       (12, 2, 'Top Rack-7'),
+       (13, 5, 'Room 2-25'),
+       (14, 3, 'Shelf A-9'),
+       (15, 4, 'Shelf C-16'),
+       (16, 1, 'Room 1-5'),
+       (17, 2, 'Shelf B-20'),
+       (18, 5, 'Lower Shelf-12'),
+       (19, 3, 'Top Rack-2'),
+       (20, 4, 'Room 2-28'),
+       (21, 1, 'Shelf C-6'),
+       (22, 2, 'Room 1-13'),
+       (23, 5, 'Room 2-10'),
+       (24, 3, 'Shelf A-4'),
+       (25, 4, 'Shelf B-19'),
+       (26, 1, 'Room 1-9'),
+       (27, 2, 'Shelf C-11'),
+       (28, 5, 'Top Rack-1'),
+       (29, 3, 'Room 2-7'),
+       (30, 4, 'Lower Shelf-20'),
+       (31, 1, 'Shelf B-15'),
+       (32, 2, 'Room 1-6'),
+       (33, 5, 'Shelf A-22'),
+       (34, 3, 'Room 2-19'),
+       (35, 4, 'Shelf C-9'),
+       (36, 1, 'Shelf A-2'),
+       (37, 2, 'Room 2-14'),
+       (38, 5, 'Shelf B-17'),
+       (39, 3, 'Lower Shelf-1'),
+       (40, 4, 'Room 1-18'),
+       (41, 5, 'Top Rack-9'),
+       (42, 3, 'Lower Shelf-1'),
+       (43, 4, 'Room 1-18'),
+       (44, 5, 'Top Rack-9')
+ON CONFLICT (item_id) DO NOTHING;
 
 --  ─────────── mock: client_id = 1 borrows item_id = 1 ───────────
 INSERT INTO borrows (borrow_id, custom_user_id, item_id, borrow_start_date, borrow_due_date, return_date, extends_count)
-VALUES (1, 1, 1,CURRENT_DATE, CURRENT_DATE + INTERVAL '14 day',NULL, 0) ON CONFLICT DO NOTHING;
-SELECT setval('borrows_borrow_id_seq', (SELECT MAX(borrow_id) FROM borrows));
+VALUES (1, 1, 1, CURRENT_DATE, CURRENT_DATE + INTERVAL '14 day', NULL, 0)
+ON CONFLICT DO NOTHING;
+SELECT SETVAL('borrows_borrow_id_seq', (SELECT MAX(borrow_id) FROM borrows));
 
 --  ─────────── mock: client_id = 1 reserves item_id = 1 ───────────
 INSERT INTO reservations (reservation_id, custom_user_id, item_id, reservation_start, reservation_due)
-VALUES (1, 2, 1, DATE '2025-05-10', DATE '2025-05-13') ON CONFLICT DO NOTHING;
-INSERT INTO reservations (reservation_id, custom_user_id, item_id, reservation_start, reservation_due)
-VALUES (2, 2, 2, DATE '2025-05-10', DATE '2025-05-13') ON CONFLICT DO NOTHING;
-INSERT INTO reservations (reservation_id, custom_user_id, item_id, reservation_start, reservation_due)
-VALUES (3, 1, 3, DATE '2025-05-08', DATE '2025-05-11') ON CONFLICT DO NOTHING;
-INSERT INTO reservations (reservation_id, custom_user_id, item_id, reservation_start, reservation_due)
-VALUES (4, 1, 3, DATE '2025-05-08', DATE '2025-05-11') ON CONFLICT DO NOTHING;
-INSERT INTO reservations (reservation_id, custom_user_id, item_id, reservation_start, reservation_due)
-VALUES (5, 4, 44, DATE '2025-05-08', DATE '2025-05-11') ON CONFLICT DO NOTHING;
-INSERT INTO reservations (reservation_id, custom_user_id, item_id, reservation_start, reservation_due)
-VALUES (6, 3, 20, DATE '2025-05-08', DATE '2025-05-11') ON CONFLICT DO NOTHING;
-INSERT INTO reservations (reservation_id, custom_user_id, item_id, reservation_start, reservation_due)
-VALUES (7, 4, 12, DATE '2025-05-08', DATE '2025-05-11') ON CONFLICT DO NOTHING;
-
-SELECT setval('reservations_reservation_id_seq', (SELECT MAX(reservation_id) FROM reservations));
\ No newline at end of file
+VALUES (1, 2, 1, DATE '2025-05-10', DATE '2025-05-13'),
+       (2, 2, 2, DATE '2025-05-10', DATE '2025-05-13'),
+       (3, 1, 3, DATE '2025-05-08', DATE '2025-05-11'),
+       (4, 1, 3, DATE '2025-05-08', DATE '2025-05-11'),
+       (5, 4, 44, DATE '2025-05-08', DATE '2025-05-11'),
+       (6, 3, 20, DATE '2025-05-08', DATE '2025-05-11'),
+       (7, 4, 12, DATE '2025-05-08', DATE '2025-05-11')
+ON CONFLICT DO NOTHING;
+SELECT SETVAL('reservations_reservation_id_seq', (SELECT MAX(reservation_id) FROM reservations));
\ No newline at end of file
diff --git a/src/main/resources/static/css/custom-style.css b/src/main/resources/static/css/custom-style.css
new file mode 100644
index 0000000..e0f9722
--- /dev/null
+++ b/src/main/resources/static/css/custom-style.css
@@ -0,0 +1,5 @@
+.nav-item:hover {
+    background-color: rgba(255, 255, 255, 0.1);
+    text-decoration: none;
+    border-radius: 0.5rem;
+}
diff --git a/src/main/resources/templates/admin/fragments/_item-create.html b/src/main/resources/templates/admin/fragments/_item-create.html
index ea9d210..0788ea8 100644
--- a/src/main/resources/templates/admin/fragments/_item-create.html
+++ b/src/main/resources/templates/admin/fragments/_item-create.html
@@ -32,7 +32,6 @@ <h5 class="modal-title">Create New Item</h5>
                                         Add Item Location Details
                                     </button>
 </span>
-
                         </li>
                     </ul>
 
diff --git a/src/main/resources/templates/public/fragments/login-dropdown.html b/src/main/resources/templates/public/fragments/login-dropdown.html
new file mode 100644
index 0000000..23cfb88
--- /dev/null
+++ b/src/main/resources/templates/public/fragments/login-dropdown.html
@@ -0,0 +1,58 @@
+<div th:fragment="public-login-dropdown" class="nav-item dropdown ms-auto">
+  <button th:if="${!#authorization.expr('isAuthenticated()')}"
+     class="btn btn-secondary h-100 dropdown-toggle d-flex align-items-center px-3 py-2 rounded-2 text-dark"
+     href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"
+     style="transition: background-color 0.2s ease;">
+    <span>
+      <i class="bi bi-person-circle" style="color: black;"></i> Login
+    </span>
+  </button>
+  <a th:if="${#authorization.expr('isAuthenticated()')}"
+     class="nav-link h-100 dropdown-toggle d-flex align-items-center px-3 py-2 rounded-2 text-white"
+     href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"
+     style="transition: background-color 0.2s ease;">
+    <span>
+      <!-- TODO: icon is to small and if set font size to 1.5 then icon move whole navbar down -->
+      <i class="bi bi-person-circle h-100" style="color: white;"></i>
+    </span>
+  </a>
+  
+  <div class="dropdown-menu dropdown-menu-end bg-secondary shadow-sm p-3 mb-5 bg-body rounded" style="width: 280px;">
+    <div th:if="${!#authorization.expr('isAuthenticated()')}" class="container rounded" style="width: 260px;">
+      <form th:action="@{/login}" method="post">
+        <div class="mb-3">
+          <label for="loginUsername" class="form-label">Username</label>
+          <input type="text" class="form-control" name="username" id="loginUsername" required>
+        </div>
+        
+        <div class="mb-3">
+          <label for="loginPassword" class="form-label">Password</label>
+          <input type="password" class="form-control" name="password" id="loginPassword" required>
+        </div>
+        
+        <div class="form-check mb-3">
+          <input class="form-check-input" type="checkbox" name="remember-me" id="dropDownCheck">
+          <label class="form-check-label" for="dropDownCheck">
+            Remember me
+          </label>
+        </div>
+        
+        <button type="submit" class="btn btn-primary">Login</button>
+      </form>
+      <div class="dropdown-divider"></div>
+      <div class="rounded-2" style="background-color: #f8f9fa">
+        <a class="dropdown-item rounded-2" href="/register">New around here? Sign up</a>
+        <a class="dropdown-item rounded-2" href="/restore-password">Forgot password?</a>
+      </div>
+    </div>
+    
+    <div th:if="${#authorization.expr('isAuthenticated()')}" class="container" style="width: 260px;">
+      <div class="rounded-2" style="background-color: #f8f9fa">
+        <a class="dropdown-item" href="/profile">Profile</a>
+        <form th:action="@{/logout}" method="post">
+          <button class="dropdown-item" type="submit">Logout</button>
+        </form>
+      </div>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/src/main/resources/templates/public/fragments/main-navbar.html b/src/main/resources/templates/public/fragments/main-navbar.html
index ab856d0..b6b58f3 100644
--- a/src/main/resources/templates/public/fragments/main-navbar.html
+++ b/src/main/resources/templates/public/fragments/main-navbar.html
@@ -1,27 +1,22 @@
-<nav class="navbar sticky-top navbar-expand-lg bg-primary" th:fragment="main-navbar" data-bs-theme="dark">
-    <div class="container-fluid">
-        <a class="navbar-brand" href="/">VÖBB</a>
-
-        <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
-                data-bs-target="#navbarColor01" aria-controls="navbarColor01"
-                aria-expanded="false" aria-label="Toggle navigation">
-            <span class="navbar-toggler-icon"></span>
-        </button>
-
-        <div class="collapse navbar-collapse" id="navbarColor01">
-            <ul class="navbar-nav me-auto">
-                <li class="nav-item" role="presentation">
-                    <a class="nav-link active" href="/libraries">Libraries</a>
-                </li>
-            </ul>
-
-            <button type="button"
-                    class="btn btn-secondary"
-                    data-bs-toggle="modal"
-                    data-bs-target="#loginModal">
-                Account
-            </button>
-        </div>
-
+<nav class="navbar sticky-top navbar-expand-lg bg-primary" th:fragment="main-navbar" data-bs-theme="light">
+  <div class="container-fluid">
+    <a class="navbar-brand text-light" href="/">VÖBB</a>
+    
+    <button class="navbar-toggler text-light" type="button" data-bs-toggle="collapse"
+            data-bs-target="#navbarColor01" aria-controls="navbarColor01"
+            aria-expanded="false" aria-label="Toggle navigation">
+      <span class="navbar-toggler-icon"></span>
+    </button>
+    
+    <div class="collapse navbar-collapse" id="navbarColor01">
+      <ul class="navbar-nav me-auto">
+        <li class="nav-item" role="presentation">
+          <a class="nav-link active text-light" href="/libraries">Libraries</a>
+        </li>
+      </ul>
+      
+      <div th:replace="~{public/fragments/login-dropdown :: public-login-dropdown}"></div>
+      
     </div>
+  </div>
 </nav>
\ No newline at end of file
diff --git a/src/main/resources/templates/public/fragments/user-login-modal.html b/src/main/resources/templates/public/fragments/user-login-modal.html
deleted file mode 100644
index 47a9794..0000000
--- a/src/main/resources/templates/public/fragments/user-login-modal.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<div id="loginModal"
-     class="modal fade"
-     xmlns:sec="http://www.w3.org/1999/xhtml">
-    <div class="modal-dialog"  role="document">
-        <div class="modal-content">
-
-            <div class="modal-body">
-                <!-- Login-->
-                <div sec:authorize="isAnonymous()">
-                    <form th:action="@{/login}" method="post">
-                        <div class="mb-3">
-                            <label for="loginUsername" class="form-label">Username</label>
-                            <input type="text" class="form-control" name="username" id="loginUsername" required>
-                        </div>
-
-                        <div class="mb-3">
-                            <label for="loginPassword" class="form-label">Password</label>
-                            <input type="password" class="form-control" name="password" id="loginPassword" required>
-                        </div>
-
-                        <div class="form-check">
-                            <input class="form-check-input" type="checkbox" name="remember-me" id="flexCheckDefault">
-                            <label class="form-check-label" for="flexCheckDefault">
-                                Remember me
-                            </label>
-                        </div>
-
-                        <button type="submit" class="btn btn-primary w-100">Login</button>
-                    </form>
-
-                    <div class="mt-3 text-center">
-                        <span>Don't have an account?</span>
-                        <a href="/register" class="text-decoration-none fw-bold">Register here!</a>
-                    </div>
-                </div>
-
-                <!-- Logout -->
-                <div sec:authorize="isAuthenticated()">
-                    <p>Hello, <strong sec:authentication="name"></strong></p>
-                    <form th:action="@{/logout}" method="post">
-                        <button type="submit" class="btn btn-danger w-100">Logout</button>
-                    </form>
-                </div>
-
-            </div>
-        </div>
-    </div>
-</div>
\ No newline at end of file
diff --git a/src/main/resources/templates/public/product/product-full-details.html b/src/main/resources/templates/public/product/product-full-details.html
index a3b864a..6f8b950 100644
--- a/src/main/resources/templates/public/product/product-full-details.html
+++ b/src/main/resources/templates/public/product/product-full-details.html
@@ -72,8 +72,7 @@ <h4>Locations</h4>
                 <td>
                     <!-- TODO: Button to borrow item, add logic that hides this button if user is not logged in-->
                     <a class="btn btn-sm btn-primary"
-                       data-bs-target="#loginModal"
-                       data-bs-toggle="modal"
+                       href="/register"
                        sec:authorize="isAnonymous()">Login and borrow</a>
                     <a class="btn btn-sm btn-primary"
                        th:href="@{/borrow}"
diff --git a/src/main/resources/templates/public/public-layout.html b/src/main/resources/templates/public/public-layout.html
index bd9510e..2bf4ff0 100644
--- a/src/main/resources/templates/public/public-layout.html
+++ b/src/main/resources/templates/public/public-layout.html
@@ -3,40 +3,39 @@
       xmlns:th="http://www.thymeleaf.org"
       xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
 <head>
-    <meta charset="UTF-8">
-    <link href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.5/dist/zephyr/bootstrap.min.css" rel="stylesheet">
-    <title data-layout-title-pattern="$CONTENT_TITLE - $LAYOUT_TITLE"></title>
+  <meta charset="UTF-8">
+  <link href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.5/dist/zephyr/bootstrap.min.css" rel="stylesheet">
+  <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css" rel="stylesheet">
+  <link th:href="@{/css/custom-style.css}" rel="stylesheet">
+  <title data-layout-title-pattern="$CONTENT_TITLE - $LAYOUT_TITLE"></title>
 </head>
 <body class="d-flex flex-column min-vh-100">
 <header class="sticky-top">
-    <div th:replace="~{public/fragments/main-navbar.html :: main-navbar}"></div>
-
-    <div class="w-100 p-2 bg-primary">
-        <div class="container">
-            <form th:action="@{/search}" method="GET">
-                <div class="d-flex mb-3" style="gap: 0.5rem;">
-                    <div class="form-floating flex-grow-1">
-                        <input type="text" class="form-control" id="title" name="title"
-                               th:value="${title}" placeholder="Search for a product">
-                        <label for="title">Search</label>
-                    </div>
-                    <button type="submit" class="btn btn-secondary">Search</button>
-                </div>
-            </form>
-
+  <div th:replace="~{public/fragments/main-navbar.html :: main-navbar}"></div>
+  
+  <div class="w-100 p-2 bg-primary">
+    <div class="container">
+      <form th:action="@{/search}" method="GET">
+        <div class="d-flex mb-3" style="gap: 0.5rem;">
+          <div class="form-floating flex-grow-1">
+            <input type="text" class="form-control" id="title" name="title"
+                   th:value="${title}" placeholder="Search for a product">
+            <label for="title">Search</label>
+          </div>
+          <button type="submit" class="btn btn-secondary">Search</button>
         </div>
+      </form>
+    
     </div>
+  </div>
 </header>
 
 <!--Main content-->
-<main layout:fragment="content"></main>
+<div layout:fragment="content"></div>
 
 <!-- Footer -->
 <footer th:replace="~{public/fragments/footer.html :: footer}" class="mt-auto"></footer>
 
-<!-- Modals -->
-<div th:replace="~{public/fragments/user-login-modal :: #loginModal}"></div>
-
 <!--Scripts-->
 <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
 
diff --git a/src/main/resources/templates/public/user/profile.html b/src/main/resources/templates/public/user/profile.html
new file mode 100644
index 0000000..0e67cbe
--- /dev/null
+++ b/src/main/resources/templates/public/user/profile.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html lang="en"
+      xmlns:th="http://www.thymeleaf.org"
+      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+      layout:decorate="~{public/public-layout}">
+<head>
+    <meta charset="UTF-8">
+    <title>VOEBB</title>
+</head>
+
+<body>
+<div layout:fragment="content" class="container mt-4">
+
+    <h2 th:text="'Hello, '+ ${userUpdateDTO.firstName} + ' ' + ${userUpdateDTO.lastName}"></h2>
+
+    <ul class="nav nav-tabs mb-4" role="tablist">
+        <li class="nav-item" role="presentation">
+            <a class="nav-link active" data-bs-toggle="tab" href="#info" aria-selected="true" role="tab"
+               tabindex="-1">Info</a>
+        </li>
+        <li class="nav-item" role="presentation">
+            <a class="nav-link" data-bs-toggle="tab" href="#changeInfo" aria-selected="false" role="tab">Change info</a>
+        </li>
+        <li class="nav-item" role="presentation">
+            <a class="nav-link" data-bs-toggle="tab" href="#myBorrowings" aria-selected="false" role="tab">My borrowings</a>
+        </li>
+
+    </ul>
+    <div id="myTabContent" class="tab-content">
+        <div class="tab-pane fade active show" id="info" role="tabpanel">
+            <div class="container mt-4">
+                <h2>User info</h2>
+                <p th:text="'Name:' + ${userUpdateDTO.firstName} + ' ' + ${userUpdateDTO.lastName}">full-name</p>
+                <p th:text="'Email:' + ${userUpdateDTO.email}">Email</p>
+                <p th:text="'Phone number: ' + ${userUpdateDTO.phoneNumber}">Phone number</p>
+            </div>
+            <div class="container mt-4">
+                <h2>Membership info</h2>
+                <p th:text="'Membership status: (add membership func)'"></p>
+                <p th:text="'Active till: (add membership func)' "></p>
+            </div>
+            
+        </div>
+        <div class="tab-pane fade" id="changeInfo" role="tabpanel">
+                <form th:action="@{/profile/edit}" th:object="${userUpdateDTO}" method="post" >
+                    <div class="row mb-3">
+                        <div class="col-md-6">
+                            <label for="firstName" class="form-label">First Name</label>
+                            <input type="text" id="firstName" th:field="*{firstName}" class="form-control" required>
+                        </div>
+                        <div class="col-md-6">
+                            <label for="lastName" class="form-label">Last Name</label>
+                            <input type="text" id="lastName" th:field="*{lastName}" class="form-control" required>
+                        </div>
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="email" class="form-label">Email</label>
+                        <input type="email" id="email" th:field="*{email}" class="form-control" required>
+                    </div>
+                    
+                    <div class="mb-3">
+                        <label for="phoneNumber" class="form-label">New Phone Number</label>
+                        <input type="tel" id="phoneNumber" th:field="*{phoneNumber}" class="form-control">
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="newPassword" class="form-label">New Password</label>
+                        <input type="password" id="newPassword" th:field="*{newPassword}" class="form-control">
+                    </div>
+
+                    <div class="mb-3">
+                        <label for="oldPassword" class="form-label">Enter your old password to confirm changes</label>
+                        <input type="password" id="oldPassword" th:field="*{oldPassword}" class="form-control" required>
+                    </div>
+
+                    <div class="btn-group mb-3 text-center" role="group">
+                        <button type="submit" class="btn btn-success">Submit</button>
+                    </div>
+                </form>
+
+        </div>
+        <div class="tab-pane fade" id="myBorrowings">
+            <p>Add table with all borrowings/reservations here</p>
+        </div>
+    </div>
+    
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/test/java/com/example/voebb/service/CustomUserServiceTest.java b/src/test/java/com/example/voebb/service/CustomUserServiceTest.java
index 44d2208..f506f0b 100644
--- a/src/test/java/com/example/voebb/service/CustomUserServiceTest.java
+++ b/src/test/java/com/example/voebb/service/CustomUserServiceTest.java
@@ -32,12 +32,14 @@ class CustomUserServiceTest {
     @Test
     void testCreateUser_success() {
         String email = "test@test.com";
+        String phoneNumber = "+4912312345678";
         String password = "12345678";
         String firstName = "TestFirstName";
         String lastName = "TestLastName";
 
         UserRegistrationDTO userRegistrationDTO = new UserRegistrationDTO(
                 email,
+                phoneNumber,
                 password,
                 firstName,
                 lastName


Reviews:
user: login: mitalisoti
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-05-22T19:28:59Z
user: login: nat-laz
body: love the new place of login Modal...
young generation wait for new brand library web-site xdxd
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-05-22T23:11:44Z
user: login: bruch-alex
state: COMMENTED
author_association: OWNER
submitted_at: 2025-05-23T08:31:47Z
user: login: bruch-alex
state: COMMENTED
author_association: OWNER
submitted_at: 2025-05-23T08:32:33Z
user: login: bruch-alex
state: COMMENTED
author_association: OWNER
submitted_at: 2025-05-23T09:31:49Z

Review comments:
diff_hunk: @@ -24,6 +24,7 @@ public SecurityConfig(PasswordEncoder passwordEncoder, CustomUserDetailsService
     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
         return http.authorizeHttpRequests(auth -> auth
                         //.requestMatchers("/admin/**").hasRole("ADMIN")
+                        .requestMatchers("/profile").hasRole("CLIENT")
path: src/main/java/com/example/voebb/config/SecurityConfig.java
user: login: nat-laz
body: why not give access to profile-page to the `ADMIN` too? 

` .requestMatchers("/profile").hasAnyRole("CLIENT", "ADMIN")`

and instead of MembershipInfo, info when ADMIN start working xd 
created_at: 2025-05-22T23:03:18Z
author_association: COLLABORATOR
diff_hunk: @@ -65,9 +61,13 @@ spring.datasource.driver-class-name=org.postgresql.Driver
 # update = modify existing schema
 # validate = check schema matches entities
 # none = disable DDL operations
-spring.jpa.hibernate.ddl-auto=create
+spring.jpa.hibernate.ddl-auto=update
path: src/main/resources/application.properties
user: login: nat-laz
body: maybe let's still keep it `create`, till more stable version of tables in DB

first when I run it, it crashed because i did not had the new added filed  "phone_number" in cutomUser Table in my local DB
created_at: 2025-05-22T23:10:13Z
author_association: COLLABORATOR
diff_hunk: @@ -24,6 +24,7 @@ public SecurityConfig(PasswordEncoder passwordEncoder, CustomUserDetailsService
     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
         return http.authorizeHttpRequests(auth -> auth
                         //.requestMatchers("/admin/**").hasRole("ADMIN")
+                        .requestMatchers("/profile").hasRole("CLIENT")
path: src/main/java/com/example/voebb/config/SecurityConfig.java
user: login: bruch-alex
body: We can do it or we can create separate profile page (we have it kinda already) for admins, we should discuss 
created_at: 2025-05-23T08:31:47Z
author_association: OWNER
diff_hunk: @@ -65,9 +61,13 @@ spring.datasource.driver-class-name=org.postgresql.Driver
 # update = modify existing schema
 # validate = check schema matches entities
 # none = disable DDL operations
-spring.jpa.hibernate.ddl-auto=create
+spring.jpa.hibernate.ddl-auto=update
path: src/main/resources/application.properties
user: login: bruch-alex
body: Oh yes, i just changed it during development and forgot change it back
created_at: 2025-05-23T08:32:33Z
author_association: OWNER
diff_hunk: @@ -65,9 +61,13 @@ spring.datasource.driver-class-name=org.postgresql.Driver
 # update = modify existing schema
 # validate = check schema matches entities
 # none = disable DDL operations
-spring.jpa.hibernate.ddl-auto=create
+spring.jpa.hibernate.ddl-auto=update
path: src/main/resources/application.properties
user: login: bruch-alex
body: Done
created_at: 2025-05-23T09:31:49Z
author_association: OWNER

PR comments:
user: login: bruch-alex
created_at: 2025-05-22T11:08:35Z
body: Check updates in description
author_association: OWNER