Criando tarefa com projeto

This commit is contained in:
Gabriel Santana de Menezes 2026-05-03 21:28:56 -04:00
parent 23b2fa4e1a
commit e78c15898f
9 changed files with 58 additions and 17 deletions

View file

@ -41,4 +41,13 @@ public class Project {
public void setUsers(List<User> users) { public void setUsers(List<User> users) {
this.users = users; this.users = users;
} }
@Override
public String toString() {
return "Project{" +
"id=" + id +
", name='" + name + '\'' +
", organization=" + (organization != null ? organization.getName() : "null") +
'}';
}
} }

View file

@ -2,6 +2,7 @@ package net.beetechgroup.beetask.frameworks.config;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces; import jakarta.enterprise.inject.Produces;
import net.beetechgroup.beetask.usecase.repository.ProjectRepository;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskUseCase; import net.beetechgroup.beetask.usecase.task.create.CreateTaskUseCase;
import net.beetechgroup.beetask.usecase.task.listall.ListAllTasksUseCase; import net.beetechgroup.beetask.usecase.task.listall.ListAllTasksUseCase;
@ -13,8 +14,8 @@ import net.beetechgroup.beetask.usecase.task.update.UpdateTaskStatusUseCase;
public class TaskUseCaseConfig { public class TaskUseCaseConfig {
@Produces @Produces
public CreateTaskUseCase createTaskUseCase(TaskRepository taskRepository) { public CreateTaskUseCase createTaskUseCase(TaskRepository taskRepository, ProjectRepository projectRepository) {
return new CreateTaskUseCase(taskRepository); return new CreateTaskUseCase(taskRepository, projectRepository);
} }
@Produces @Produces

View file

@ -11,7 +11,6 @@ import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import net.beetechgroup.beetask.entities.Project;
import net.beetechgroup.beetask.entities.task.TaskStatus; import net.beetechgroup.beetask.entities.task.TaskStatus;
@Entity @Entity

View file

@ -23,6 +23,7 @@ public class LoginUseCase {
} }
public LoginOutput execute(LoginInput input) { public LoginOutput execute(LoginInput input) {
System.out.println(input.email() + " " + input.password());
Optional<User> userOptional = userRepository.findByEmail(input.email()); Optional<User> userOptional = userRepository.findByEmail(input.email());
if (userOptional.isEmpty()) { if (userOptional.isEmpty()) {

View file

@ -2,13 +2,16 @@ package net.beetechgroup.beetask.usecase.task.create;
import net.beetechgroup.beetask.entities.Project; import net.beetechgroup.beetask.entities.Project;
import net.beetechgroup.beetask.entities.task.Task; import net.beetechgroup.beetask.entities.task.Task;
import net.beetechgroup.beetask.usecase.repository.ProjectRepository;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
public class CreateTaskUseCase { public class CreateTaskUseCase {
private final TaskRepository taskRepository; private final TaskRepository taskRepository;
private final ProjectRepository projectRepository;
public CreateTaskUseCase(TaskRepository taskRepository) { public CreateTaskUseCase(TaskRepository taskRepository, ProjectRepository projectRepository) {
this.taskRepository = taskRepository; this.taskRepository = taskRepository;
this.projectRepository = projectRepository;
} }
public CreateTaskOutput execute(CreateTaskInput input) { public CreateTaskOutput execute(CreateTaskInput input) {
@ -16,11 +19,13 @@ public class CreateTaskUseCase {
task.setTitle(input.title()); task.setTitle(input.title());
task.setDescription(input.description()); task.setDescription(input.description());
task.setStatus(input.status()); task.setStatus(input.status());
if (input.projectId() != null) { if (input.projectId() != null) {
Project project = new Project(); Project project = projectRepository.findProjectById(input.projectId())
project.setId(input.projectId()); .orElseThrow(() -> new IllegalArgumentException("Projeto não encontrado com ID: " + input.projectId()));
task.setProject(project); task.setProject(project);
} }
return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task));
} }
} }

View file

@ -9,6 +9,7 @@ quarkus.datasource.jdbc.url=${DB_URL:jdbc:postgresql://localhost:5432/timetrack}
%dev.quarkus.datasource.db-kind=h2 %dev.quarkus.datasource.db-kind=h2
%dev.quarkus.datasource.jdbc.url=jdbc:h2:file:./data/beetask;DB_CLOSE_DELAY=-1;AUTO_SERVER=TRUE %dev.quarkus.datasource.jdbc.url=jdbc:h2:file:./data/beetask;DB_CLOSE_DELAY=-1;AUTO_SERVER=TRUE
%dev.quarkus.h2.console=true
%dev.quarkus.hibernate-orm.database.generation=update %dev.quarkus.hibernate-orm.database.generation=update
quarkus.flyway.migrate-at-start=false quarkus.flyway.migrate-at-start=false

View file

@ -15,14 +15,17 @@ import { taskService } from "../services/taskService";
import { cn } from "../lib/utils"; import { cn } from "../lib/utils";
import { TASK_STATUS_LABELS, TASK_STATUS_OPTIONS } from "../types/task"; import { TASK_STATUS_LABELS, TASK_STATUS_OPTIONS } from "../types/task";
import type { CreateTaskPayload, TaskResponse } from "../types/task"; import type { CreateTaskPayload, TaskResponse } from "../types/task";
import { useAuth } from "../contexts/AuthContext";
import { useEffect } from "react";
import { projectService, type Project } from "../services/projectService";
type FormState = CreateTaskPayload; type FormState = Omit<CreateTaskPayload, 'projectId'> & { projectId: string };
const initialFormState: FormState = { const initialFormState: FormState = {
title: "", title: "",
description: "", description: "",
status: "NOT_STARTED", status: "NOT_STARTED",
project: "", projectId: "",
}; };
export function CreateTaskPage() { export function CreateTaskPage() {
@ -30,6 +33,21 @@ export function CreateTaskPage() {
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null); const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [createdTask, setCreatedTask] = useState<TaskResponse | null>(null); const [createdTask, setCreatedTask] = useState<TaskResponse | null>(null);
const [projects, setProjects] = useState<Project[]>([]);
const { activeOrg } = useAuth();
useEffect(() => {
async function loadProjects() {
if (!activeOrg) return;
try {
const data = await projectService.listByOrganization(activeOrg.id);
setProjects(data);
} catch (error) {
console.error("Erro ao carregar projetos:", error);
}
}
loadProjects();
}, [activeOrg]);
function updateField( function updateField(
event: ChangeEvent< event: ChangeEvent<
@ -53,7 +71,7 @@ export function CreateTaskPage() {
title: form.title.trim(), title: form.title.trim(),
description: form.description.trim(), description: form.description.trim(),
status: form.status, status: form.status,
project: form.project.trim(), projectId: form.projectId ? Number(form.projectId) : null,
}; };
const response = await taskService.createTask(payload); const response = await taskService.createTask(payload);
@ -186,13 +204,19 @@ export function CreateTaskPage() {
<Layout size={16} /> <Layout size={16} />
Projeto Projeto
</div> </div>
<input <select
name="project" name="projectId"
value={form.project} value={form.projectId || ""}
onChange={updateField} onChange={updateField}
placeholder="Ex.: Backend API" className="h-12 w-full appearance-none rounded-xl border border-border-soft bg-app-bg px-4 text-sm font-medium outline-none transition-all focus:border-brand focus:ring-4 focus:ring-brand/10"
className="h-12 w-full rounded-xl border border-border-soft bg-app-bg px-4 text-sm font-medium outline-none transition-all focus:border-brand focus:ring-4 focus:ring-brand/10" >
/> <option value="">Sem Projeto (Geral)</option>
{projects.map((project) => (
<option key={project.id} value={project.id}>
{project.name}
</option>
))}
</select>
</label> </label>
<label className="group block"> <label className="group block">

View file

@ -6,7 +6,7 @@ export const taskService = {
return apiFetch<TaskResponse[]>('/tasks') return apiFetch<TaskResponse[]>('/tasks')
}, },
async createTask(task: { title: string; description?: string; project?: string }): Promise<TaskResponse> { async createTask(task: { title: string; description?: string; projectId?: number | null; status?: TaskStatus }): Promise<TaskResponse> {
return apiFetch<TaskResponse>('/tasks', { return apiFetch<TaskResponse>('/tasks', {
method: 'POST', method: 'POST',
body: task, body: task,

View file

@ -18,7 +18,7 @@ export type CreateTaskPayload = {
title: string title: string
description: string description: string
status: TaskStatus status: TaskStatus
project: string projectId: number | null
} }
export type TaskHistoryItem = { export type TaskHistoryItem = {
@ -32,5 +32,6 @@ export type TaskResponse = {
description: string description: string
status: TaskStatus status: TaskStatus
project: string project: string
projectId?: number
history?: TaskHistoryItem[] history?: TaskHistoryItem[]
} }