diff --git a/backend/src/main/java/net/beetechgroup/beetask/frameworks/config/OrganizationUseCaseConfig.java b/backend/src/main/java/net/beetechgroup/beetask/frameworks/config/OrganizationUseCaseConfig.java index e7e43b8..c5edefa 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/frameworks/config/OrganizationUseCaseConfig.java +++ b/backend/src/main/java/net/beetechgroup/beetask/frameworks/config/OrganizationUseCaseConfig.java @@ -9,6 +9,7 @@ import net.beetechgroup.beetask.usecase.organization.requests.ListPendingRequest import net.beetechgroup.beetask.usecase.organization.requests.ListUserJoinRequestsUseCase; import net.beetechgroup.beetask.usecase.organization.requests.HandleJoinRequestUseCase; import net.beetechgroup.beetask.usecase.organization.auth.AuthorizeOrganizationAdminUseCase; +import net.beetechgroup.beetask.usecase.organization.members.ListOrganizationMembersUseCase; import net.beetechgroup.beetask.usecase.repository.OrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository; @@ -58,4 +59,10 @@ public class OrganizationUseCaseConfig { UserRepository userRepository) { return new ListUserJoinRequestsUseCase(userOrganizationRepository, userRepository); } -} \ No newline at end of file + + @Produces + public ListOrganizationMembersUseCase listOrganizationMembersUseCase(UserRepository userRepository, + UserOrganizationRepository userOrganizationRepository) { + return new ListOrganizationMembersUseCase(userRepository, userOrganizationRepository); + } +} diff --git a/backend/src/main/java/net/beetechgroup/beetask/frameworks/config/TaskUseCaseConfig.java b/backend/src/main/java/net/beetechgroup/beetask/frameworks/config/TaskUseCaseConfig.java index 1eb1422..1903f17 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/frameworks/config/TaskUseCaseConfig.java +++ b/backend/src/main/java/net/beetechgroup/beetask/frameworks/config/TaskUseCaseConfig.java @@ -5,6 +5,7 @@ import jakarta.enterprise.inject.Produces; import net.beetechgroup.beetask.usecase.repository.CategoryRepository; import net.beetechgroup.beetask.usecase.repository.ProjectRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository; +import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.task.create.CreateTaskUseCase; import net.beetechgroup.beetask.usecase.task.listall.ListAllTasksUseCase; @@ -20,8 +21,9 @@ public class TaskUseCaseConfig { @Produces public CreateTaskUseCase createTaskUseCase(TaskRepository taskRepository, ProjectRepository projectRepository, - CategoryRepository categoryRepository, UserRepository userRepository) { - return new CreateTaskUseCase(taskRepository, projectRepository, categoryRepository, userRepository); + CategoryRepository categoryRepository, UserRepository userRepository, + UserOrganizationRepository userOrganizationRepository) { + return new CreateTaskUseCase(taskRepository, projectRepository, categoryRepository, userRepository, userOrganizationRepository); } @Produces @@ -51,8 +53,9 @@ public class TaskUseCaseConfig { @Produces public UpdateTaskUseCase updateTaskUseCase(TaskRepository taskRepository, ProjectRepository projectRepository, - CategoryRepository categoryRepository, UserRepository userRepository) { - return new UpdateTaskUseCase(taskRepository, projectRepository, categoryRepository, userRepository); + CategoryRepository categoryRepository, UserRepository userRepository, + UserOrganizationRepository userOrganizationRepository) { + return new UpdateTaskUseCase(taskRepository, projectRepository, categoryRepository, userRepository, userOrganizationRepository); } @Produces diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/organization/OrganizationController.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/organization/OrganizationController.java index ad2700e..85b5522 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/organization/OrganizationController.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/organization/OrganizationController.java @@ -21,6 +21,8 @@ import net.beetechgroup.beetask.usecase.organization.requests.ListUserJoinReques import net.beetechgroup.beetask.usecase.organization.requests.UserJoinRequestOutput; import net.beetechgroup.beetask.usecase.organization.requests.JoinRequestOutput; import net.beetechgroup.beetask.usecase.organization.requests.HandleJoinRequestUseCase; +import net.beetechgroup.beetask.usecase.organization.members.ListOrganizationMembersUseCase; +import net.beetechgroup.beetask.usecase.organization.members.OrganizationMemberOutput; import org.jboss.logging.Logger; import java.util.List; @@ -47,6 +49,9 @@ public class OrganizationController { @Inject HandleJoinRequestUseCase handleJoinRequestUseCase; + @Inject + ListOrganizationMembersUseCase listOrganizationMembersUseCase; + @Inject SecurityIdentity securityIdentity; @@ -116,6 +121,17 @@ public class OrganizationController { LOGGER.infof("Join request rejected for user %d in organization %d by %s", userId, organizationId, userEmail); } + @GET + @Path("/{id}/members") + @Authenticated + @Operation(summary = "List organization members", description = "List all active members of an organization") + public List listMembers(@PathParam("id") Long organizationId) { + String userEmail = securityIdentity.getPrincipal().getName(); + List members = listOrganizationMembersUseCase.execute(userEmail, organizationId); + LOGGER.infof("User %s listed %d active members for organization %d", userEmail, members.size(), organizationId); + return members; + } + @GET @Path("/my-requests") @Authenticated diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/CreateTaskRequest.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/CreateTaskRequest.java index ea197fa..c2ffda0 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/CreateTaskRequest.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/CreateTaskRequest.java @@ -8,7 +8,9 @@ public record CreateTaskRequest( String title, String description, TaskStatus status, + Long organizationId, Long projectId, Long categoryId, + Long assigneeUserId, List history) { } diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/TaskControllerMapper.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/TaskControllerMapper.java index f55c668..4a584b6 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/TaskControllerMapper.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/TaskControllerMapper.java @@ -33,8 +33,10 @@ public class TaskControllerMapper { request.title(), request.description(), request.status(), + request.organizationId(), request.projectId(), request.categoryId(), + request.assigneeUserId(), userEmail, Objects.nonNull(request.history()) ? request.history().stream() .map(h -> new TaskHistoryItemInput(h.id(), h.startAt(), h.endAt())) @@ -85,8 +87,10 @@ public class TaskControllerMapper { request.title(), request.description(), request.status(), + request.organizationId(), request.projectId(), request.categoryId(), + request.assigneeUserId(), userEmail, Objects.nonNull(request.history()) ? request.history().stream() .map(h -> new TaskHistoryItemInput(h.id(), h.startAt(), h.endAt())) diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/members/ListOrganizationMembersUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/members/ListOrganizationMembersUseCase.java new file mode 100644 index 0000000..4993206 --- /dev/null +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/members/ListOrganizationMembersUseCase.java @@ -0,0 +1,47 @@ +package net.beetechgroup.beetask.usecase.organization.members; + +import java.util.List; + +import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus; +import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; +import net.beetechgroup.beetask.usecase.repository.UserRepository; +import org.jboss.logging.Logger; + +public class ListOrganizationMembersUseCase { + private static final Logger LOGGER = Logger.getLogger(ListOrganizationMembersUseCase.class); + + private final UserRepository userRepository; + private final UserOrganizationRepository userOrganizationRepository; + + public ListOrganizationMembersUseCase(UserRepository userRepository, + UserOrganizationRepository userOrganizationRepository) { + this.userRepository = userRepository; + this.userOrganizationRepository = userOrganizationRepository; + } + + public List execute(String userEmail, Long organizationId) { + var requester = userRepository.findByEmail(userEmail) + .orElseThrow(() -> new IllegalArgumentException("Usuário não encontrado: " + userEmail)); + + boolean belongsToOrganization = userRepository.findUserOrganizations(requester.getId()).stream() + .anyMatch(membership -> membership.getOrganization().getId().equals(organizationId)); + + if (!belongsToOrganization) { + LOGGER.warnf("User %s attempted to list members of organization %d without membership", userEmail, organizationId); + throw new SecurityException("Você não tem permissão para listar membros desta organização."); + } + + List members = userOrganizationRepository + .findByOrganizationIdAndStatus(organizationId, UserOrganizationStatus.ACTIVE) + .stream() + .map(membership -> new OrganizationMemberOutput( + membership.getUser().getId(), + membership.getUser().getName(), + membership.getUser().getEmail(), + membership.getUser().getPhoto())) + .toList(); + + LOGGER.infof("Loaded %d active members for organization %d requested by %s", members.size(), organizationId, userEmail); + return members; + } +} diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/members/OrganizationMemberOutput.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/members/OrganizationMemberOutput.java new file mode 100644 index 0000000..4c6e7ed --- /dev/null +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/members/OrganizationMemberOutput.java @@ -0,0 +1,7 @@ +package net.beetechgroup.beetask.usecase.organization.members; + +public record OrganizationMemberOutput( + Long userId, + String userName, + String userEmail, + String userPhoto) {} diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskInput.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskInput.java index d317bf4..2394ca0 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskInput.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskInput.java @@ -8,8 +8,10 @@ public record CreateTaskInput( String title, String description, TaskStatus status, + Long organizationId, Long projectId, Long categoryId, + Long assigneeUserId, String userEmail, List history) { } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskUseCase.java index 216e309..52efa35 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskUseCase.java @@ -4,8 +4,11 @@ import java.util.List; import java.util.Objects; import net.beetechgroup.beetask.entities.Category; import net.beetechgroup.beetask.entities.Project; +import net.beetechgroup.beetask.entities.User; +import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus; import net.beetechgroup.beetask.entities.task.Task; import net.beetechgroup.beetask.entities.task.TaskHistoryItem; +import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.CategoryRepository; import net.beetechgroup.beetask.usecase.repository.ProjectRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository; @@ -18,13 +21,16 @@ public class CreateTaskUseCase { private final ProjectRepository projectRepository; private final CategoryRepository categoryRepository; private final UserRepository userRepository; + private final UserOrganizationRepository userOrganizationRepository; public CreateTaskUseCase(TaskRepository taskRepository, ProjectRepository projectRepository, - CategoryRepository categoryRepository, UserRepository userRepository) { + CategoryRepository categoryRepository, UserRepository userRepository, + UserOrganizationRepository userOrganizationRepository) { this.taskRepository = taskRepository; this.projectRepository = projectRepository; this.categoryRepository = categoryRepository; this.userRepository = userRepository; + this.userOrganizationRepository = userOrganizationRepository; } public CreateTaskOutput execute(CreateTaskInput input) { @@ -52,11 +58,8 @@ public class CreateTaskUseCase { task.setCategory(category); } - task.setUser(userRepository.findByEmail(input.userEmail()) - .orElseThrow(() -> { - LOGGER.warnf("Task creation failed because user %s was not found", input.userEmail()); - return new IllegalArgumentException("Usuário não encontrado: " + input.userEmail()); - })); + validateRequesterMembership(input.organizationId(), input.userEmail()); + task.setUser(resolveAssignee(input.organizationId(), input.assigneeUserId())); if (Objects.nonNull(input.history())) { List historyItems = input.history().stream().map(h -> { @@ -72,4 +75,46 @@ public class CreateTaskUseCase { LOGGER.infof("Task %d created for user %s", output.id(), input.userEmail()); return output; } + + private void validateRequesterMembership(Long organizationId, String userEmail) { + if (Objects.isNull(organizationId)) { + throw new IllegalArgumentException("Organização é obrigatória para criar a tarefa."); + } + + User requester = userRepository.findByEmail(userEmail) + .orElseThrow(() -> { + LOGGER.warnf("Task creation failed because user %s was not found", userEmail); + return new IllegalArgumentException("Usuário não encontrado: " + userEmail); + }); + + boolean belongsToOrganization = userRepository.findUserOrganizations(requester.getId()).stream() + .anyMatch(membership -> membership.getOrganization().getId().equals(organizationId)); + + if (!belongsToOrganization) { + LOGGER.warnf("Task creation denied because user %s is not an active member of organization %d", userEmail, organizationId); + throw new SecurityException("Você não tem permissão para criar tarefas nesta organização."); + } + } + + private User resolveAssignee(Long organizationId, Long assigneeUserId) { + if (Objects.isNull(assigneeUserId)) { + return null; + } + + boolean belongsToOrganization = userOrganizationRepository + .findByOrganizationIdAndStatus(organizationId, UserOrganizationStatus.ACTIVE) + .stream() + .anyMatch(membership -> membership.getUser().getId().equals(assigneeUserId)); + + if (!belongsToOrganization) { + LOGGER.warnf("Task creation failed because assignee %d is not an active member of organization %d", assigneeUserId, organizationId); + throw new IllegalArgumentException("Responsável não pertence à organização selecionada."); + } + + return userRepository.findUserById(assigneeUserId) + .orElseThrow(() -> { + LOGGER.warnf("Task creation failed because assignee %d was not found", assigneeUserId); + return new IllegalArgumentException("Usuário responsável não encontrado: " + assigneeUserId); + }); + } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskInput.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskInput.java index ba7de67..28feee0 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskInput.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskInput.java @@ -8,8 +8,10 @@ public record UpdateTaskInput( String title, String description, TaskStatus status, + Long organizationId, Long projectId, Long categoryId, + Long assigneeUserId, String userEmail, List history) { } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskUseCase.java index e5107c4..158945f 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskUseCase.java @@ -4,11 +4,14 @@ import java.util.List; import java.util.Objects; import net.beetechgroup.beetask.entities.Category; import net.beetechgroup.beetask.entities.Project; +import net.beetechgroup.beetask.entities.User; +import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus; import net.beetechgroup.beetask.entities.task.Task; import net.beetechgroup.beetask.entities.task.TaskHistoryItem; import net.beetechgroup.beetask.usecase.repository.CategoryRepository; import net.beetechgroup.beetask.usecase.repository.ProjectRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository; +import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; @@ -20,26 +23,23 @@ public class UpdateTaskUseCase { private final ProjectRepository projectRepository; private final CategoryRepository categoryRepository; private final UserRepository userRepository; + private final UserOrganizationRepository userOrganizationRepository; public UpdateTaskUseCase(TaskRepository taskRepository, ProjectRepository projectRepository, - CategoryRepository categoryRepository, UserRepository userRepository) { + CategoryRepository categoryRepository, UserRepository userRepository, + UserOrganizationRepository userOrganizationRepository) { this.taskRepository = taskRepository; this.projectRepository = projectRepository; this.categoryRepository = categoryRepository; this.userRepository = userRepository; + this.userOrganizationRepository = userOrganizationRepository; } public CreateTaskOutput execute(UpdateTaskInput input) { LOGGER.infof("Updating task %d requested by user %s", input.id(), input.userEmail()); Task task = taskRepository.findTaskById(input.id()); - if (Objects.isNull(task.getUser())) { - task.setUser(userRepository.findByEmail(input.userEmail()) - .orElseThrow(() -> { - LOGGER.warnf("Task update failed because user %s was not found", input.userEmail()); - return new IllegalArgumentException("Usuário não encontrado: " + input.userEmail()); - })); - } + validateRequesterMembership(input.organizationId(), input.userEmail()); task.setTitle(input.title()); task.setDescription(input.description()); @@ -67,6 +67,8 @@ public class UpdateTaskUseCase { task.setCategory(null); } + task.setUser(resolveAssignee(input.organizationId(), input.assigneeUserId())); + if (Objects.nonNull(input.history())) { List updatedHistory = input.history().stream().map(hInput -> { if (Objects.nonNull(hInput.id())) { @@ -101,4 +103,46 @@ public class UpdateTaskUseCase { LOGGER.infof("Task %d updated successfully", input.id()); return output; } + + private void validateRequesterMembership(Long organizationId, String userEmail) { + if (Objects.isNull(organizationId)) { + throw new IllegalArgumentException("Organização é obrigatória para atualizar a tarefa."); + } + + User requester = userRepository.findByEmail(userEmail) + .orElseThrow(() -> { + LOGGER.warnf("Task update failed because user %s was not found", userEmail); + return new IllegalArgumentException("Usuário não encontrado: " + userEmail); + }); + + boolean belongsToOrganization = userRepository.findUserOrganizations(requester.getId()).stream() + .anyMatch(membership -> membership.getOrganization().getId().equals(organizationId)); + + if (!belongsToOrganization) { + LOGGER.warnf("Task update denied because user %s is not an active member of organization %d", userEmail, organizationId); + throw new SecurityException("Você não tem permissão para atualizar tarefas nesta organização."); + } + } + + private User resolveAssignee(Long organizationId, Long assigneeUserId) { + if (Objects.isNull(assigneeUserId)) { + return null; + } + + boolean belongsToOrganization = userOrganizationRepository + .findByOrganizationIdAndStatus(organizationId, UserOrganizationStatus.ACTIVE) + .stream() + .anyMatch(membership -> membership.getUser().getId().equals(assigneeUserId)); + + if (!belongsToOrganization) { + LOGGER.warnf("Task update failed because assignee %d is not an active member of organization %d", assigneeUserId, organizationId); + throw new IllegalArgumentException("Responsável não pertence à organização selecionada."); + } + + return userRepository.findUserById(assigneeUserId) + .orElseThrow(() -> { + LOGGER.warnf("Task update failed because assignee %d was not found", assigneeUserId); + return new IllegalArgumentException("Usuário responsável não encontrado: " + assigneeUserId); + }); + } } diff --git a/frontend/src/pages/CreateTaskPage.tsx b/frontend/src/pages/CreateTaskPage.tsx index e770997..6e398a9 100644 --- a/frontend/src/pages/CreateTaskPage.tsx +++ b/frontend/src/pages/CreateTaskPage.tsx @@ -7,6 +7,7 @@ import { Layout, PlusCircle, Send, + Users, XCircle, } from "lucide-react"; import { format } from "date-fns"; @@ -22,12 +23,18 @@ import { useAuth } from "../contexts/AuthContext"; import { useEffect } from "react"; import { projectService, type Project } from "../services/projectService"; import { categoryService } from "../services/categoryService"; +import { + organizationService, + type OrganizationMember, +} from "../services/organizationService"; import type { Category } from "../types/category"; import { CategoryIcon } from "../components/CategoryIcon"; +import { UserAvatar } from "../components/UserAvatar"; -type FormState = Omit & { +type FormState = Omit & { projectId: string; categoryId: string; + assigneeUserId: string; history: TaskHistoryItem[]; }; @@ -37,6 +44,7 @@ const initialFormState: FormState = { status: "NOT_STARTED", projectId: "", categoryId: "", + assigneeUserId: "", history: [], }; @@ -47,7 +55,9 @@ export function CreateTaskPage() { const [createdTask, setCreatedTask] = useState(null); const [projects, setProjects] = useState([]); const [categories, setCategories] = useState([]); - const { activeOrg } = useAuth(); + const [members, setMembers] = useState([]); + const [assignToLoggedUser, setAssignToLoggedUser] = useState(false); + const { activeOrg, user } = useAuth(); const { id } = useParams(); const navigate = useNavigate(); const isEdit = !!id; @@ -63,8 +73,10 @@ export function CreateTaskPage() { status: data.status, projectId: data.project?.id?.toString() || "", categoryId: data.category?.id?.toString() || "", + assigneeUserId: data.user?.id?.toString() || "", history: data.history || [], }); + setAssignToLoggedUser(Boolean(user?.email && data.user?.email === user.email)); } catch (error) { console.error("Erro ao carregar tarefa:", error); setErrorMessage("Não foi possível carregar os dados da tarefa."); @@ -72,7 +84,7 @@ export function CreateTaskPage() { } loadTask(); } - }, [id, isEdit]); + }, [id, isEdit, user?.email]); useEffect(() => { async function loadProjects() { @@ -93,10 +105,30 @@ export function CreateTaskPage() { console.error("Erro ao carregar categorias:", error); } } + async function loadMembers() { + if (!activeOrg) return; + try { + const data = await organizationService.listMembers(activeOrg.id); + setMembers(data); + } catch (error) { + console.error("Erro ao carregar membros:", error); + } + } loadProjects(); loadCategories(); + loadMembers(); }, [activeOrg]); + useEffect(() => { + if (!activeOrg) return; + if (assignToLoggedUser && user) { + const currentMember = members.find((member) => member.userEmail === user.email); + if (currentMember) { + setForm((current) => ({ ...current, assigneeUserId: currentMember.userId.toString() })); + } + } + }, [assignToLoggedUser, user, members, activeOrg]); + function updateField( event: ChangeEvent< HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement @@ -115,12 +147,18 @@ export function CreateTaskPage() { setIsSubmitting(true); try { + if (!activeOrg) { + throw new Error("Nenhuma organização ativa selecionada."); + } + const payload = { title: form.title.trim(), description: form.description.trim(), status: form.status, + organizationId: activeOrg.id, projectId: form.projectId ? Number(form.projectId) : null, categoryId: form.categoryId ? Number(form.categoryId) : null, + assigneeUserId: form.assigneeUserId ? Number(form.assigneeUserId) : null, history: form.history, }; @@ -131,6 +169,7 @@ export function CreateTaskPage() { const response = await taskService.createTask(payload); setCreatedTask(response); setForm(initialFormState); + setAssignToLoggedUser(false); } } catch (error) { setErrorMessage( @@ -198,6 +237,10 @@ export function CreateTaskPage() { ); } + const selectedAssignee = members.find( + (member) => member.userId === Number(form.assigneeUserId), + ); + return (
@@ -324,6 +367,69 @@ export function CreateTaskPage() {
+
+
+ + + +
+ + {selectedAssignee ? ( +
+ +
+

+ {selectedAssignee.userName} +

+

+ {selectedAssignee.userEmail} +

+
+
+ ) : ( +

+ Esta tarefa pode ficar sem responsável. +

+ )} +
+ {/* Description Section */}