Index: docs/todo.txt
===================================================================
--- docs/todo.txt	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ docs/todo.txt	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -39,4 +39,5 @@
 - view channels page da sa napret, imas ideja toska ke ti teknit to so chatojte ko preview da sa. (ova samo za mene e viktor uzivaj)
 - edit na chat messages
+- statistiki za userot vo public profile page (vo kolku proekti rabotel, kolku vreme e vo nekoj proekt ...)
 ------------
 
@@ -57,4 +58,17 @@
 
 
+---- Dokumentacija
+
+najvazni scenarija:
+    - odgovara na diskusija
+    - kreira diskusija
+
+podelba:
+- viktor od kreira nov topic, do se prijavuva za ucestvo vo proekt (inclusive)
+- stefan od dodava i kreira tagovi (inclusive) do otstranuva programer od proekt
 
 
+
+
+
+
Index: src/main/java/com/db/finki/www/build_board/config/BeanConfig.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/config/BeanConfig.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/config/BeanConfig.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -1,5 +1,5 @@
 package com.db.finki.www.build_board.config;
 
-import com.db.finki.www.build_board.service.AuthenticationSuccessHandlerImpl;
+import com.db.finki.www.build_board.service.user.AuthenticationSuccessHandlerImpl;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
Index: src/main/java/com/db/finki/www/build_board/controller/ExceptionHandler.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/controller/ExceptionHandler.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/controller/ExceptionHandler.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -39,5 +39,8 @@
     @org.springframework.web.bind.annotation.ExceptionHandler(value = {Exception.class })
     public ModelAndView handleNotFound(Exception exception) {
-        return mavBuilder(exception,"Unknown exception",-1);
+        System.out.println(exception.getMessage());
+        exception.printStackTrace();
+        return mavBuilder(exception,"An error occurred.",-1);
+
     }
 }
Index: src/main/java/com/db/finki/www/build_board/controller/channel/ChannelController.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/controller/channel/ChannelController.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/controller/channel/ChannelController.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -7,4 +7,5 @@
 import com.db.finki.www.build_board.service.channel.ChannelService;
 import com.db.finki.www.build_board.service.channel.MessageService;
+import com.db.finki.www.build_board.service.thread.impl.ProjectService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.core.parameters.P;
@@ -23,9 +24,11 @@
     private final MessageMapper messageMapper;
     private final MessageService messageService;
+    private final ProjectService projectService;
 
-    public ChannelController(ChannelService channelService, MessageMapper messageMapper, MessageService messageService) {
+    public ChannelController(ChannelService channelService, MessageMapper messageMapper, MessageService messageService, ProjectService projectService) {
         this.channelService = channelService;
         this.messageMapper = messageMapper;
         this.messageService = messageService;
+        this.projectService = projectService;
     }
 
@@ -37,5 +40,5 @@
     }
 
-    @PreAuthorize("#project.getDevelopers().contains(#user)")
+    @PreAuthorize("@projectService.getAllDevelopersForProject(#project).contains(#user)")
     @GetMapping("/{channelName}")
     public String getChannel(@PathVariable String channelName,
@@ -51,4 +54,5 @@
             model.addAttribute("messages", messageMapper.toDTO(
                     messageService.getAllMessagesForProjectChannel(project.getId(), channelName)));
+            model.addAttribute("developers",projectService.getAllDevelopersForProject(project));
         } else {
             model.addAttribute("channel", c);
@@ -66,5 +70,5 @@
     }
 
-    @PreAuthorize("#project.getDevelopers().contains(#user)")
+    @PreAuthorize("@projectService.getAllDevelopersForProject(#project).contains(#user)")
     @PostMapping("/add")
     public String add(@PathVariable("title") @P("project") Project project, @RequestParam String channelName, @RequestParam String channelDescription, @SessionAttribute @P("user") BBUser user, RedirectAttributes redirectAttributes) {
Index: src/main/java/com/db/finki/www/build_board/controller/home_page/HomePageController.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/controller/home_page/HomePageController.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/controller/home_page/HomePageController.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -2,5 +2,5 @@
 
 import com.db.finki.www.build_board.entity.user_type.BBUser;
-import com.db.finki.www.build_board.service.BBUserDetailsService;
+import com.db.finki.www.build_board.service.user.BBUserDetailsService;
 import com.db.finki.www.build_board.service.search.SearchService;
 import com.db.finki.www.build_board.service.thread.itf.TagService;
Index: src/main/java/com/db/finki/www/build_board/controller/home_page/UserProfileController.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/controller/home_page/UserProfileController.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/controller/home_page/UserProfileController.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -3,5 +3,5 @@
 import com.db.finki.www.build_board.entity.entity_enum.Status;
 import com.db.finki.www.build_board.entity.user_type.BBUser;
-import com.db.finki.www.build_board.service.BBUserDetailsService;
+import com.db.finki.www.build_board.service.user.BBUserDetailsService;
 import com.db.finki.www.build_board.service.request.ProjectRequestService;
 import com.db.finki.www.build_board.service.util.FileUploadService;
@@ -30,15 +30,27 @@
         this.projectRequestService = projectRequestService;
     }
-   
+
+    @PreAuthorize("#user.getUsername().equals(#username)")
+    @GetMapping("/profile/edit")
+    public String getEditProfilePage(@PathVariable @P("username") String username, @SessionAttribute @P("user") BBUser user, Model model) {
+        try {
+            model.addAttribute("user", userService.loadUserByUsername(username));
+            model.addAttribute("canEdit", user.getUsername().equals(username));
+            return "/home_pages/private-profile";
+        }catch(Exception ignore){
+            return "redirect:/";
+        }
+    }
+
     @GetMapping("/profile")
     public String getProfilePage(@PathVariable String username, @SessionAttribute BBUser user, Model model) {
         try {
             model.addAttribute("user", userService.loadUserByUsername(username));
-            model.addAttribute("canEdit", user.getUsername().equals(username));
-            return "home_pages/profile";
+            return "/home_pages/public-profile";
         }catch(Exception ignore){
             return "redirect:/";
         }
     }
+
 
     @GetMapping("/project-requests")
@@ -95,5 +107,5 @@
         );
         session.setAttribute("user", user);
-        return "redirect:/";
+        return "redirect:/" + user.getUsername() + "/profile";
     }
 }
Index: src/main/java/com/db/finki/www/build_board/controller/thread_controller/ProjectController.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/controller/thread_controller/ProjectController.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/controller/thread_controller/ProjectController.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -31,4 +31,5 @@
         model.addAttribute("project", project);
         model.addAttribute("tags", tagService.getAll());
+        model.addAttribute("developers",projectService.getAllDevelopersForProject(project));
         String error = (String) redirectAttributes.getAttribute("error");
         if(error != null){
@@ -74,4 +75,5 @@
     {
         model.addAttribute("project", project);
+        model.addAttribute("developers", projectService.getAllDevelopersForProject(project));
         return "project_pages/members";
     }
@@ -79,5 +81,5 @@
     @PostMapping("/{pr-title}/members/{mem-id}/kick")
     public String kickMember(@PathVariable(name = "pr-title") @P("project") Project project,@PathVariable(name = "mem-id") int memberId,@SessionAttribute @P("user") BBUser user){
-        projectService.deleteMember(project, memberId);
+        projectService.kickMember(project, memberId);
         return "redirect:/projects/" + project.getTitle() + "/members";
     }
Index: src/main/java/com/db/finki/www/build_board/entity/compositeId/DeveloperAssociatedWithProjectId.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/entity/compositeId/DeveloperAssociatedWithProjectId.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
+++ src/main/java/com/db/finki/www/build_board/entity/compositeId/DeveloperAssociatedWithProjectId.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -0,0 +1,19 @@
+package com.db.finki.www.build_board.entity.compositeId;
+
+import com.db.finki.www.build_board.entity.thread.Project;
+import com.db.finki.www.build_board.entity.user_type.BBUser;
+import jakarta.persistence.Column;
+import jakarta.persistence.Id;
+import jakarta.persistence.ManyToOne;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+public class DeveloperAssociatedWithProjectId {
+    private BBUser developer;
+    private Project project;
+    private LocalDateTime startedAt;
+}
Index: src/main/java/com/db/finki/www/build_board/entity/thread/Project.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/entity/thread/Project.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/entity/thread/Project.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -42,5 +42,5 @@
             inverseJoinColumns = @JoinColumn(name = "developer_id")
     )
-    private Set<BBUser> developers = new HashSet<>(); // vaj dis
+    private Set<BBUser> developers = new HashSet<>(); // NE GO KORISTI GETTEROT OVDE
 
     @OneToMany(mappedBy = "project")
Index: src/main/java/com/db/finki/www/build_board/entity/user_type/BBUser.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/entity/user_type/BBUser.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/entity/user_type/BBUser.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -2,4 +2,5 @@
 
 import com.db.finki.www.build_board.entity.thread.BBThread;
+import com.db.finki.www.build_board.entity.thread.Project;
 import com.db.finki.www.build_board.service.util.FileUploadService;
 import jakarta.persistence.*;
@@ -16,8 +17,5 @@
 import java.nio.file.Path;
 import java.time.LocalDateTime;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
 
 
@@ -52,4 +50,6 @@
     private List<BBThread> threads;
 
+    @ManyToMany(mappedBy = "developers")
+private List<Project> projects = new ArrayList<>();
     @Override
     public boolean isEnabled() {
Index: src/main/java/com/db/finki/www/build_board/entity/user_type/DeveloperAssociatedWithProject.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/entity/user_type/DeveloperAssociatedWithProject.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
+++ src/main/java/com/db/finki/www/build_board/entity/user_type/DeveloperAssociatedWithProject.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -0,0 +1,25 @@
+package com.db.finki.www.build_board.entity.user_type;
+
+import com.db.finki.www.build_board.entity.compositeId.DeveloperAssociatedWithProjectId;
+import com.db.finki.www.build_board.entity.thread.Project;
+import jakarta.persistence.*;
+
+import java.time.LocalDateTime;
+
+@Entity
+@Table(name = "developer_associated_with_project")
+@IdClass(DeveloperAssociatedWithProjectId.class)
+public class DeveloperAssociatedWithProject {
+
+    @Id
+    @ManyToOne
+    private BBUser developer;
+    @Id
+    @ManyToOne
+    private Project project;
+    @Id
+    @Column(name = "started_at")
+    private LocalDateTime startedAt;
+    @Column(name = "ended_at")
+    private LocalDateTime endedAt;
+}
Index: src/main/java/com/db/finki/www/build_board/repository/UserRepository.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/repository/UserRepository.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/repository/UserRepository.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -3,6 +3,9 @@
 import com.db.finki.www.build_board.entity.user_type.BBUser;
 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 java.util.List;
 import java.util.Optional;
 
@@ -10,4 +13,14 @@
 public interface UserRepository extends JpaRepository<BBUser, Long> {
     Optional<BBUser> findByUsername(String username);
+
     BBUser findById(long id);
+
+    @Query("""
+                SELECT d.developer
+                FROM DeveloperAssociatedWithProject d
+                WHERE d.project.id = :projectId
+                AND d.endedAt IS NULL
+            """)
+    List<BBUser> findAllActiveDevelopersForProject(@Param("projectId") int projectId);
+
 }
Index: src/main/java/com/db/finki/www/build_board/repository/thread/ProjectRepository.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/repository/thread/ProjectRepository.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/repository/thread/ProjectRepository.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -4,5 +4,9 @@
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
+
+import java.util.List;
 
 @Repository
@@ -10,3 +14,15 @@
     Project findByTitleStartingWith(String title);
     void deleteByTitle(String title);
+    List<Project> findAllByUserId(int userId);
+
+    @Modifying
+    @Query(nativeQuery = true,
+    value = "UPDATE developer_associated_with_project dap set ended_at=now() where dap.developer_id=:uid AND dap.project_id=:pid")
+    void removeUserFromProject(int pid,int uid);
+
+    @Modifying
+    @Query(nativeQuery = true,
+    value = "INSERT INTO developer_associated_with_project (project_id, developer_id, started_at, ended_at) VALUES (:pid,:uid,now(),null)")
+    void addUserToProject(int pid,int uid);
+
 }
Index: c/main/java/com/db/finki/www/build_board/service/AuthenticationSuccessHandlerImpl.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/service/AuthenticationSuccessHandlerImpl.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ 	(revision )
@@ -1,37 +1,0 @@
-package com.db.finki.www.build_board.service;
-
-import com.db.finki.www.build_board.entity.user_type.BBUser;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import jakarta.servlet.http.HttpSession;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
-import org.springframework.security.web.savedrequest.DefaultSavedRequest;
-import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
-
-import java.io.IOException;
-import java.util.Objects;
-
-public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
-    @Override
-    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
-        BBUser user = (BBUser) authentication.getPrincipal();
-        HttpSession session = request.getSession();
-        session.setAttribute("user", user);
-        session.setMaxInactiveInterval(1800);
-
-        String redirectUrl = getSavedRequestUrl(request);
-
-        System.out.println(redirectUrl + "REDIR");
-
-        response.sendRedirect(Objects.requireNonNullElse(redirectUrl, "/"));
-
-    }
-
-    private String getSavedRequestUrl(HttpServletRequest request) {
-        HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
-        DefaultSavedRequest savedRequest = (DefaultSavedRequest) requestCache.getRequest(request, null);
-        return (savedRequest != null) ? savedRequest.getRedirectUrl() : null;
-    }
-}
Index: c/main/java/com/db/finki/www/build_board/service/BBUserDetailsService.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/service/BBUserDetailsService.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ 	(revision )
@@ -1,72 +1,0 @@
-package com.db.finki.www.build_board.service;
-
-import com.db.finki.www.build_board.entity.user_type.BBUser;
-import com.db.finki.www.build_board.repository.UserRepository;
-import org.springframework.dao.DataIntegrityViolationException;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
-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.stereotype.Service;
-
-@Service
-public class BBUserDetailsService implements UserDetailsService {
-    private final UserRepository userRepository;
-    private final PasswordEncoder passwordEncoder;
-
-    public BBUserDetailsService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
-        this.userRepository = userRepository;
-        this.passwordEncoder = passwordEncoder;
-    }
-
-    @Override
-    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
-        return userRepository.findByUsername(username)
-                             .orElseThrow(
-                                     () -> new UsernameNotFoundException("User not found with username: " + username));
-    }
-
-    public BBUser changeInfoForUserWithUsername(String oldUsername, String newUsername, String email, String name, String description, String password) {
-        BBUser user = (BBUser) loadUserByUsername(oldUsername);
-
-        user.setUsername(newUsername);
-        user.setEmail(email);
-        user.setName(name);
-        user.setDescription(description);
-
-        if (!password.isBlank() && !password.isEmpty()) {
-            user.setPassword(
-                    passwordEncoder.encode(password)
-            );
-        }
-
-        return userRepository.save(user);
-    }
-
-    public BBUser createUser(String username, String email, String name, String password, String description, String sex) {
-        password = passwordEncoder.encode(password);
-        sex = sex.equals("male") ? "m" : "f";
-        return userRepository.save(
-                new BBUser(
-                        username,
-                        email,
-                        name,
-                        password,
-                        description,
-                        sex
-                )
-        );
-    }
-
-    public Authentication registerUser(String username, String email, String name, String password, String description, String sex) {
-        BBUser user = createUser(username, email, name, password, description, sex);
-        return new UsernamePasswordAuthenticationToken(user,user.getPassword(),user.getAuthorities());
-    }
-
-    public BBUser loadUserById(int id) {
-        return userRepository.findById(id);
-    }
-}
Index: src/main/java/com/db/finki/www/build_board/service/request/ProjectRequestService.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/service/request/ProjectRequestService.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/service/request/ProjectRequestService.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -6,6 +6,8 @@
 import com.db.finki.www.build_board.entity.thread.Project;
 import com.db.finki.www.build_board.entity.user_type.BBUser;
+import com.db.finki.www.build_board.repository.UserRepository;
 import com.db.finki.www.build_board.repository.request.ProjectRequestRepo;
 import com.db.finki.www.build_board.service.thread.impl.ProjectService;
+import jakarta.transaction.Transactional;
 import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
@@ -17,13 +19,18 @@
     private final ProjectRequestRepo prReqRepo;
     private final FeedbackService feedbackService;
+    private final UserRepository userRepo;
+    private final ProjectService projectService;
 
-    public ProjectRequestService(ProjectRequestRepo prReqRepo, FeedbackService feedbackService) {
+
+    public ProjectRequestService(ProjectRequestRepo prReqRepo, FeedbackService feedbackService, UserRepository userRepo, ProjectService projectService) {
         this.prReqRepo = prReqRepo;
         this.feedbackService = feedbackService;
+        this.userRepo = userRepo;
+        this.projectService = projectService;
     }
 
     private ProjectRequests getRequestById(Integer id) {
         return prReqRepo.findById((long) id)
-                        .get();
+                .get();
     }
 
@@ -35,11 +42,10 @@
     }
 
+    @Transactional
     public void accept(BBUser creator, Integer reqId) {
         ProjectRequests prReq = getRequestById(reqId);
         prReq.setStatus(Status.ACCEPTED);
         prReq.setFeedback(feedbackService.create(creator, FeedbackFor.P, reqId));
-        prReq.getProject()
-             .getDevelopers()
-             .add(prReq.getCreator());
+        projectService.addDeveloperToProject(prReq.getProject(), prReq.getCreator());
         prReqRepo.save(prReq);
     }
Index: src/main/java/com/db/finki/www/build_board/service/thread/impl/ProjectService.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/service/thread/impl/ProjectService.java	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/java/com/db/finki/www/build_board/service/thread/impl/ProjectService.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -7,6 +7,8 @@
 import com.db.finki.www.build_board.entity.user_type.BBUser;
 import com.db.finki.www.build_board.entity.thread.Project;
+import com.db.finki.www.build_board.entity.user_type.Developer;
+import com.db.finki.www.build_board.repository.UserRepository;
 import com.db.finki.www.build_board.repository.thread.ProjectRepository;
-import com.db.finki.www.build_board.service.BBUserDetailsService;
+import com.db.finki.www.build_board.service.user.BBUserDetailsService;
 import com.db.finki.www.build_board.service.thread.itf.TagService;
 import com.db.finki.www.build_board.service.thread.itf.TopicService;
@@ -20,10 +22,12 @@
     private final TagService tagService;
     private final BBUserDetailsService userDetailsService;
+    private final UserRepository userRepository;
 
-    public ProjectService(ProjectRepository projectRepository, TopicServiceImpl topicService, TagServiceImpl tagService, BBUserDetailsService userDetailsService) {
+    public ProjectService(ProjectRepository projectRepository, TopicServiceImpl topicService, TagServiceImpl tagService, BBUserDetailsService userDetailsService, UserRepository userRepository) {
         this.projectRepository = projectRepository;
         this.topicService = topicService;
         this.tagService = tagService;
         this.userDetailsService = userDetailsService;
+        this.userRepository = userRepository;
     }
 
@@ -50,9 +54,14 @@
     }
 
+    public void addDeveloperToProject(Project project, BBUser user) {
+        projectRepository.addUserToProject(project.getId(),user.getId());
+    }
+
     @Transactional
-    public void deleteMember(Project project, int memberId) {
-        BBUser user = userDetailsService.loadUserById(memberId);
-        boolean removed = project.getDevelopers().remove(user);
-        System.out.println("REMOVED: " + removed);
+    public void kickMember(Project project, int memberId) {
+        projectRepository.removeUserFromProject(project.getId(),memberId);
+    }
+    public List<BBUser> getAllDevelopersForProject(Project project) {
+        return userRepository.findAllActiveDevelopersForProject(project.getId());
     }
 
Index: src/main/java/com/db/finki/www/build_board/service/user/AuthenticationSuccessHandlerImpl.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/service/user/AuthenticationSuccessHandlerImpl.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
+++ src/main/java/com/db/finki/www/build_board/service/user/AuthenticationSuccessHandlerImpl.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -0,0 +1,37 @@
+package com.db.finki.www.build_board.service.user;
+
+import com.db.finki.www.build_board.entity.user_type.BBUser;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.savedrequest.DefaultSavedRequest;
+import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
+
+import java.io.IOException;
+import java.util.Objects;
+
+public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
+    @Override
+    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+        BBUser user = (BBUser) authentication.getPrincipal();
+        HttpSession session = request.getSession();
+        session.setAttribute("user", user);
+        session.setMaxInactiveInterval(1800);
+
+        String redirectUrl = getSavedRequestUrl(request);
+
+        System.out.println(redirectUrl + "REDIR");
+
+        response.sendRedirect(Objects.requireNonNullElse(redirectUrl, "/"));
+
+    }
+
+    private String getSavedRequestUrl(HttpServletRequest request) {
+        HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
+        DefaultSavedRequest savedRequest = (DefaultSavedRequest) requestCache.getRequest(request, null);
+        return (savedRequest != null) ? savedRequest.getRedirectUrl() : null;
+    }
+}
Index: src/main/java/com/db/finki/www/build_board/service/user/BBUserDetailsService.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/service/user/BBUserDetailsService.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
+++ src/main/java/com/db/finki/www/build_board/service/user/BBUserDetailsService.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -0,0 +1,70 @@
+package com.db.finki.www.build_board.service.user;
+
+import com.db.finki.www.build_board.entity.user_type.BBUser;
+import com.db.finki.www.build_board.repository.UserRepository;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+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.stereotype.Service;
+
+@Service
+public class BBUserDetailsService implements UserDetailsService {
+    private final UserRepository userRepository;
+    private final PasswordEncoder passwordEncoder;
+
+    public BBUserDetailsService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
+        this.userRepository = userRepository;
+        this.passwordEncoder = passwordEncoder;
+    }
+
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+        return userRepository.findByUsername(username)
+                             .orElseThrow(
+                                     () -> new UsernameNotFoundException("User not found with username: " + username));
+    }
+
+    public BBUser changeInfoForUserWithUsername(String oldUsername, String newUsername, String email, String name, String description, String password) {
+        BBUser user = (BBUser) loadUserByUsername(oldUsername);
+
+        user.setUsername(newUsername);
+        user.setEmail(email);
+        user.setName(name);
+        user.setDescription(description);
+
+        if (!password.isBlank() && !password.isEmpty()) {
+            user.setPassword(
+                    passwordEncoder.encode(password)
+            );
+        }
+
+        return userRepository.save(user);
+    }
+
+    public BBUser createUser(String username, String email, String name, String password, String description, String sex) {
+        password = passwordEncoder.encode(password);
+        sex = sex.equals("male") ? "m" : "f";
+        return userRepository.save(
+                new BBUser(
+                        username,
+                        email,
+                        name,
+                        password,
+                        description,
+                        sex
+                )
+        );
+    }
+
+    public Authentication registerUser(String username, String email, String name, String password, String description, String sex) {
+        BBUser user = createUser(username, email, name, password, description, sex);
+        return new UsernamePasswordAuthenticationToken(user,user.getPassword(),user.getAuthorities());
+    }
+
+    public BBUser loadUserById(int id) {
+        return userRepository.findById(id);
+    }
+}
Index: src/main/java/com/db/finki/www/build_board/service/user/UserStatisticsService.java
===================================================================
--- src/main/java/com/db/finki/www/build_board/service/user/UserStatisticsService.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
+++ src/main/java/com/db/finki/www/build_board/service/user/UserStatisticsService.java	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -0,0 +1,24 @@
+package com.db.finki.www.build_board.service.user;
+
+import com.db.finki.www.build_board.entity.thread.Project;
+import com.db.finki.www.build_board.entity.user_type.BBUser;
+import com.db.finki.www.build_board.repository.UserRepository;
+import com.db.finki.www.build_board.repository.thread.ProjectRepository;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class UserStatisticsService {
+    private final UserRepository userRepository;
+    private final ProjectRepository projectRepository;
+
+    public UserStatisticsService(UserRepository userRepository, ProjectRepository projectRepository) {
+        this.userRepository = userRepository;
+        this.projectRepository = projectRepository;
+    }
+
+    public List<Project> getAllActiveProjectsForUser(BBUser user) {
+        return projectRepository.findAllByUserId(user.getId());
+    }
+}
Index: src/main/resources/db/migration/V2__add_test_data.sql
===================================================================
--- src/main/resources/db/migration/V2__add_test_data.sql	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/resources/db/migration/V2__add_test_data.sql	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -106,5 +106,5 @@
 INSERT INTO messages (sent_at, content, sent_by, project_id, channel_name)
 VALUES
-    (NOW(), 'Hello, team!', 3, 5, 'General'),
-    (NOW(), 'We need to push the deadline.', 3, 5, 'Updates');
+    (NOW(), 'Zdravo. Ova e real-time chat za dopisuvanje', 3, 5, 'General'),
+    (NOW(), 'Resen ladno a?', 3, 5, 'Updates');
 
Index: src/main/resources/templates/channels/show-channel.html
===================================================================
--- src/main/resources/templates/channels/show-channel.html	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/resources/templates/channels/show-channel.html	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -68,5 +68,5 @@
         <h5 style="text-align: center">Users</h5>
         <hr>
-        <div th:each="developer : ${channel.getProject().getDevelopers()}" class="user-item">
+        <div th:each="developer : ${developers}" class="user-item">
             <img th:src="${developer.getAvatarUrl()}" class="rounded-circle icon-small" alt="user-logo"
                  style="width: 3rem;height: 3rem;">
Index: src/main/resources/templates/home_pages/private-profile.html
===================================================================
--- src/main/resources/templates/home_pages/private-profile.html	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
+++ src/main/resources/templates/home_pages/private-profile.html	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Profile</title>
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
+</head>
+
+<body class="bg-light">
+    <div th:replace="/home_pages/home :: navigation"></div>
+    <div class="container mt-5 w-50-xs w-75-md " style="width: max-content">
+        <!-- User Profile Card -->
+        <div class="card">
+            <div class="card-header bg-primary text-white">
+                <h4 class="mb-0">User Profile</h4>
+            </div>
+
+            <div class="card-body me-5 ms-5 pe-5 ps-5 d-flex flex-row gap-5 ">
+
+                <!-- Profile Picture -->
+                <div
+                    class="col-md-4 flex-grow-2 text-center d-flex justify-content-center align-items-center flex-column">
+                    <form method="post" th:action="@{/{username}/upload-avatar(username=${user.getUsername()})}"
+                        enctype="multipart/form-data">
+                        <img th:src="${user.getAvatarUrl()}" alt="Profile Picture" id="profileImage-input"
+                            class="rounded-circle border border-1 border-info mb-3"
+                            style="width: 150px; height: 150px; object-fit: cover;">
+                        <th:block th:if="${canEdit}">
+                            <input type="file" id="userImage" name="userImage" accept="image/*"
+                                class="form-control mb-2">
+                            <input type="hidden" th:value="${session.user == null ? '' : session.user.getUsername()}"
+                                name="cur_user_username">
+                            <button type="submit" class="btn btn-success btn-sm w-100">Upload Picture</button>
+                        </th:block>
+                    </form>
+                </div>
+                <!-- User Details -->
+                <div
+                    th:replace="fragments/user_fields :: user_fields(username=${user.getUsername()})">
+                </div>
+                <!--            <form method="post"-->
+                <!--                  class="d-flex gap-2 flex-grow-1 flex-column gap-2 list-group-flush "-->
+                <!--                  th:action="@{/{username}/profile/change(username=${user.getUsername()})}"-->
+                <!--            >-->
+                <!--                <h5>Personal Details</h5>-->
+                <!--                <label class="fw-bold ">Username:-->
+                <!--                    <input th:readonly="${!canEdit}"-->
+                <!--                           name="username" type="text" th:value="${user.getUsername()}"-->
+                <!--                           class="w-100 border border-secondary ps-2 rounded list-group-item">-->
+                <!--                </label>-->
+                <!--                <label class="fw-bold ">Name:-->
+                <!--                    <input name="name" th:readonly="${!canEdit}"-->
+                <!--                           type="text" th:value="${user.getName()}" class="border ps-2 border-secondary rounded w-100 list-group-item">-->
+                <!--                </label>-->
+                <!--                <label th:readonly="${!canEdit}"-->
+                <!--                       class="fw-bold ">Email:-->
+                <!--                    <input name="email" type="text" th:value="${user.getEmail()}" class="border ps-2 border-secondary rounded w-100 list-group-item">-->
+                <!--                </label>-->
+                <!--                <label>-->
+                <!--                    <span class="fw-bold d-block">Description</span>-->
+                <!--                    <textarea name="description" class="border border-secondary ps-2 rounded w-100" th:readonly="${!canEdit}"-->
+                <!--                              th:text="${user.getDescription()}"></textarea>-->
+                <!--                </label>-->
+                <!--                <label th:readonly="${!canEdit}"-->
+                <!--                       class="fw-bold ">Password:-->
+                <!--                    <input name="password"-->
+                <!--                           placeholder="Leave empty if you don't want to change it"-->
+                <!--                           type="text" class="w-100 ps-2 list-group-item border border-secondary rounded">-->
+                <!--                </label>-->
+                <!--                <input type="hidden" name="cur_user_username"-->
+                <!--                       th:value="${session.user == null} ? '' : ${session.user.getUsername()}">-->
+                <!--                <button th:if="${canEdit}" class="btn btn-success btn-sm w-50 align-self-center">Save changes-->
+                <!--                </button>-->
+                <!--            </form>-->
+
+                <script>
+                    const img = document.querySelector("#profileImage-input");
+                    console.log(img)
+                    document.querySelector('input[type="file"]').addEventListener("change", ev => {
+                        const [file] = ev.target.files
+                        console.log(file)
+                        if (file) {
+                            console.log(URL.createObjectURL(file))
+                            img.setAttribute("src", URL.createObjectURL(file));
+                        }
+                    })
+                </script>
+            </div>
+        </div>
+    </div>
+
+        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
+</body>
+
+</html>
Index: c/main/resources/templates/home_pages/profile.html
===================================================================
--- src/main/resources/templates/home_pages/profile.html	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ 	(revision )
@@ -1,96 +1,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Profile</title>
-    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
-</head>
-
-<body class="bg-light">
-    <div th:replace="/home_pages/home :: navigation"></div>
-    <div class="container mt-5 w-50-xs w-75-md " style="width: max-content">
-        <!-- User Profile Card -->
-        <div class="card">
-            <div class="card-header bg-primary text-white">
-                <h4 class="mb-0">User Profile</h4>
-            </div>
-
-            <div class="card-body me-5 ms-5 pe-5 ps-5 d-flex flex-row gap-5 ">
-
-                <!-- Profile Picture -->
-                <div
-                    class="col-md-4 flex-grow-2 text-center d-flex justify-content-center align-items-center flex-column">
-                    <form method="post" th:action="@{/{username}/upload-avatar(username=${user.getUsername()})}"
-                        enctype="multipart/form-data">
-                        <img th:src="${user.getAvatarUrl()}" alt="Profile Picture" id="profileImage-input"
-                            class="rounded-circle border border-1 border-info mb-3"
-                            style="width: 150px; height: 150px; object-fit: cover;">
-                        <th:block th:if="${canEdit}">
-                            <input type="file" id="userImage" name="userImage" accept="image/*"
-                                class="form-control mb-2">
-                            <input type="hidden" th:value="${session.user == null ? '' : session.user.getUsername()}"
-                                name="cur_user_username">
-                            <button type="submit" class="btn btn-success btn-sm w-100">Upload Picture</button>
-                        </th:block>
-                    </form>
-                </div>
-                <!-- User Details -->
-                <div
-                    th:replace="fragments/user_fields :: user_fields(username=${user.getUsername()})">
-                </div>
-                <!--            <form method="post"-->
-                <!--                  class="d-flex gap-2 flex-grow-1 flex-column gap-2 list-group-flush "-->
-                <!--                  th:action="@{/{username}/profile/change(username=${user.getUsername()})}"-->
-                <!--            >-->
-                <!--                <h5>Personal Details</h5>-->
-                <!--                <label class="fw-bold ">Username:-->
-                <!--                    <input th:readonly="${!canEdit}"-->
-                <!--                           name="username" type="text" th:value="${user.getUsername()}"-->
-                <!--                           class="w-100 border border-secondary ps-2 rounded list-group-item">-->
-                <!--                </label>-->
-                <!--                <label class="fw-bold ">Name:-->
-                <!--                    <input name="name" th:readonly="${!canEdit}"-->
-                <!--                           type="text" th:value="${user.getName()}" class="border ps-2 border-secondary rounded w-100 list-group-item">-->
-                <!--                </label>-->
-                <!--                <label th:readonly="${!canEdit}"-->
-                <!--                       class="fw-bold ">Email:-->
-                <!--                    <input name="email" type="text" th:value="${user.getEmail()}" class="border ps-2 border-secondary rounded w-100 list-group-item">-->
-                <!--                </label>-->
-                <!--                <label>-->
-                <!--                    <span class="fw-bold d-block">Description</span>-->
-                <!--                    <textarea name="description" class="border border-secondary ps-2 rounded w-100" th:readonly="${!canEdit}"-->
-                <!--                              th:text="${user.getDescription()}"></textarea>-->
-                <!--                </label>-->
-                <!--                <label th:readonly="${!canEdit}"-->
-                <!--                       class="fw-bold ">Password:-->
-                <!--                    <input name="password"-->
-                <!--                           placeholder="Leave empty if you don't want to change it"-->
-                <!--                           type="text" class="w-100 ps-2 list-group-item border border-secondary rounded">-->
-                <!--                </label>-->
-                <!--                <input type="hidden" name="cur_user_username"-->
-                <!--                       th:value="${session.user == null} ? '' : ${session.user.getUsername()}">-->
-                <!--                <button th:if="${canEdit}" class="btn btn-success btn-sm w-50 align-self-center">Save changes-->
-                <!--                </button>-->
-                <!--            </form>-->
-
-                <script>
-                    const img = document.querySelector("#profileImage-input");
-                    console.log(img)
-                    document.querySelector('input[type="file"]').addEventListener("change", ev => {
-                        const [file] = ev.target.files
-                        console.log(file)
-                        if (file) {
-                            console.log(URL.createObjectURL(file))
-                            img.setAttribute("src", URL.createObjectURL(file));
-                        }
-                    })
-                </script>
-            </div>
-        </div>
-
-        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
-</body>
-
-</html>
Index: src/main/resources/templates/home_pages/public-profile.html
===================================================================
--- src/main/resources/templates/home_pages/public-profile.html	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
+++ src/main/resources/templates/home_pages/public-profile.html	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Public Profile</title>
+  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
+  <style>
+    /* Custom styles for the public profile */
+    .profile-card {
+      border: none;
+      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+      border-radius: 10px;
+      overflow: hidden;
+    }
+
+    .profile-header {
+      background: linear-gradient(135deg, #6a11cb, #2575fc);
+      color: white;
+      padding: 2rem;
+      text-align: center;
+      position: relative; /* For positioning the Edit link */
+    }
+
+    .profile-avatar {
+      width: 150px;
+      height: 150px;
+      object-fit: cover;
+      border: 4px solid white;
+      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
+    }
+
+    .profile-details {
+      padding: 2rem;
+      background: white;
+    }
+
+    .profile-details h5 {
+      color: #333;
+      font-weight: bold;
+    }
+
+    .profile-details p {
+      color: #666;
+      font-size: 1.1rem;
+    }
+
+    .profile-description {
+      background: #f8f9fa;
+      padding: 1.5rem;
+      border-radius: 8px;
+      margin-top: 1.5rem;
+    }
+
+    /* Edit link styling */
+    .edit-link {
+      position: absolute;
+      top: 1rem;
+      right: 1rem;
+      background: rgba(255, 255, 255, 0.2);
+      color: white;
+      padding: 0.5rem 1rem;
+      border-radius: 5px;
+      text-decoration: none;
+      transition: background 0.3s ease;
+    }
+
+    .edit-link:hover {
+      background: rgba(255, 255, 255, 0.3);
+      text-decoration: none;
+    }
+  </style>
+</head>
+
+<body class="bg-light">
+<!-- Navigation (replace with your navigation component) -->
+<div th:replace="/home_pages/home :: navigation"></div>
+
+<!-- Public Profile Section -->
+<div class="container mt-5">
+  <div class="row justify-content-center">
+    <div class="col-md-8">
+      <!-- Profile Card -->
+      <div class="card profile-card">
+        <!-- Profile Header -->
+        <div class="profile-header">
+          <!-- Edit Link (Top-Right Corner) -->
+          <a th:if="${session.user == user}" class="edit-link" th:href="@{/{username}/profile/edit (username=${user.getUsername()})}">Edit</a>
+
+          <!-- Profile Picture and Username -->
+          <img th:src="${user.getAvatarUrl()}" alt="Profile Picture" class="profile-avatar rounded-circle">
+          <h3 class="mt-3" th:text="${user.getUsername()}">Username</h3>
+        </div>
+
+        <!-- Profile Details -->
+        <div class="profile-details">
+          <div class="row">
+            <div class="col-md-12">
+              <!-- Username -->
+              <h5>Username</h5>
+              <p th:text="${user.getUsername()}">Username</p>
+
+              <!-- Description -->
+              <div class="profile-description">
+                <h5>About Me</h5>
+                <p th:text="${user.getDescription()} ?: 'No description provided.'">
+                  This is a sample description about the user. It can be a few lines long.
+                </p>
+              </div>
+              <div class="profile-description">
+                <h5>Projects worked in:</h5>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<!-- Bootstrap JS -->
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
+</body>
+
+</html>
Index: src/main/resources/templates/home_pages/register.html
===================================================================
--- src/main/resources/templates/home_pages/register.html	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/resources/templates/home_pages/register.html	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -18,4 +18,5 @@
               class="d-flex me-5 mt-3 ms-5 mb-3 gap-2 flex-grow-1 flex-column gap-2 list-group-flush "
               action="/register"
+              id="register-form"
         >
             <label class="fw-bold ">Username:
@@ -40,6 +41,13 @@
             <label class="fw-bold ">Password:
                 <input name="password"
-                       placeholder="Leave empty if you don't want to change it"
+                       id="password"
+                       placeholder="Enter a strong password"
                        type="password" class="w-100 ps-2 list-group-item border border-secondary rounded">
+            </label>
+            <label class="fw-bold ">Confirm Password:
+                <input
+                        id="confirm-password"
+                        placeholder="Retype your password"
+                        type="password" class="w-100 ps-2 list-group-item border border-secondary rounded">
             </label>
             <div>
@@ -52,5 +60,5 @@
                 </label>
             </div>
-            <button th:if="${canEdit}" class="btn btn-success btn-sm w-50 align-self-center">Create user
+            <button th:if="${canEdit}" class="btn btn-success btn-sm w-50 align-self-center">Register
             </button>
         </form>
@@ -58,13 +66,48 @@
 </div>
 <footer
-        th:if="${duplicatedUsername != null}"
         class="w-100 d-flex justify-content-center">
-    <div class="bg-danger w-80 mt-5 rounded p-2 ">
+    <div class="bg-danger w-80 mt-5 rounded p-2" th:if="${duplicatedUsername != null}">
         <p
                 th:text="|The username ${duplicatedUsername?: ''}  already exists|"
                 class="text-center text-body mb-0"></p>
     </div>
+    <div id="error" class="fs-6 text-danger mt-3"></div>
 </footer>
 </body>
+
+<script>
+    let form = document.getElementById("register-form");
+    let errorDiv = document.getElementById("error");
+
+    form.addEventListener("submit", (event) => {
+        let hasError = false;
+        errorDiv.innerHTML = '';
+        errorDiv.style.display = 'none';
+
+        form.querySelectorAll("input").forEach(input => {
+            if (input.value === '') {
+                hasError = true;
+                let errorMessage = document.createElement('p');
+                errorMessage.innerText = `Field ${input.name || input.id} cannot be empty.`;
+                errorDiv.appendChild(errorMessage);
+            }
+        });
+
+        let passwd = document.getElementById("password").value;
+        let confirm = document.getElementById("confirm-password").value;
+
+        if (passwd !== confirm) {
+            hasError = true;
+            let errorMessage = document.createElement('p');
+            errorMessage.innerText = "Passwords do not match.";
+            errorDiv.appendChild(errorMessage);
+        }
+
+        if (hasError) {
+            event.preventDefault();
+            errorDiv.style.display = 'block';
+        }
+    });
+</script>
 <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
 </html>
Index: src/main/resources/templates/project_pages/members.html
===================================================================
--- src/main/resources/templates/project_pages/members.html	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/resources/templates/project_pages/members.html	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -47,5 +47,5 @@
 
         <div class="list-group-item d-flex gap-2 justify-content-between align-items-center ps-4 pe-4"
-             th:each="member : ${project.getDevelopers()}">
+             th:each="member : ${developers}">
             <div class="d-flex flex-row align-items-center gap-3">
                 <img class="rounded-circle border border-1 border-info"
Index: src/main/resources/templates/project_pages/show-project.html
===================================================================
--- src/main/resources/templates/project_pages/show-project.html	(revision 1386eb4437cfaa9f58dcfa09ac6bf91aa634417c)
+++ src/main/resources/templates/project_pages/show-project.html	(revision e179f9816eca4d59ff29afda2107ac38dd25a76f)
@@ -31,5 +31,5 @@
                    th:href="@{/projects/{pr_title}/members(pr_title=${project.getTitle()})}">Show members</a>
                 <button th:if="${session.user != null && !project.getUser().equals(session.user)
-                 && !project.getDevelopers().contains(session.user)}"
+                 && !developers.contains(session.user)}"
                         class="text-decoration-none text-reset p-2 border border-success rounded bg-success"
                         th:attr="data-pr-title=${project.getTitle()}"
@@ -88,5 +88,5 @@
                 </div>
 
-                <div th:if="${project.getDevelopers().contains(session.user)}" class="w-75 d-flex flex-column align-items-center">
+                <div th:if="${developers.contains(session.user)}" class="w-75 d-flex flex-column align-items-center">
                     <h4>Channels:</h4>
                     <div class="list-group w-75">
