Skip to content

Commit 86533ed

Browse files
authored
Merge pull request #32 from nicolasreisdev/back/ceci
implementação da lógica de atualização de projeto e integração com o …
2 parents 20789e7 + fa2bac6 commit 86533ed

File tree

6 files changed

+140
-18
lines changed

6 files changed

+140
-18
lines changed

backend/src/business/businessLogicProject.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,72 @@ class businessLogicProject{
4242
});
4343
}
4444

45-
updateProject(){
45+
async updateProject(projectId: string, projectData: Partial<any>, userId: number) {
46+
47+
return knex.transaction(async (trx) => {
48+
49+
// Busca projeto e valida autoria
50+
const existingProject = await trx('Projects')
51+
.where({ projectID: projectId })
52+
.first();
53+
54+
if (!existingProject) {
55+
throw new Error("Projeto não encontrado.");
56+
}
57+
58+
if (existingProject.creatorID !== userId) {
59+
throw new Error("Você não tem permissão para editar este projeto.");
60+
}
61+
62+
// Prepara campos da tabela PRINCIPAL (Projects)
63+
const fieldsToUpdate: any = {};
64+
65+
if (projectData.title !== undefined) fieldsToUpdate.title = projectData.title;
66+
if (projectData.description !== undefined) fieldsToUpdate.description = projectData.description;
67+
if (projectData.status !== undefined) fieldsToUpdate.status = projectData.status;
68+
if (projectData.startDate !== undefined) fieldsToUpdate.startDate = projectData.startDate;
69+
70+
// Atualiza o 'updatedAt' se houver mudanças nos campos principais
71+
if (Object.keys(fieldsToUpdate).length > 0) {
72+
fieldsToUpdate.updatedAt = new Date();
73+
74+
await trx('Projects')
75+
.where({ projectID: projectId })
76+
.update(fieldsToUpdate);
77+
}
78+
79+
// Atualiza a tabela de relacionamento (ProjectsKeywords)
80+
if (projectData.technologies !== undefined) {
81+
82+
// Remove TODAS as associações antigas desse projeto
83+
await trx('ProjectsKeywords')
84+
.where({ projectID: projectId })
85+
.del();
86+
87+
// Se a nova lista não estiver vazia, insere as novas
88+
if (Array.isArray(projectData.technologies) && projectData.technologies.length > 0) {
89+
90+
// Busca os IDs das tags (Keywords) baseadas no nome (string) enviado pelo front
91+
const keywordIDs = await trx('Keywords')
92+
.whereIn('tag', projectData.technologies)
93+
.select('keywordID');
94+
95+
// Prepara o array de inserção
96+
const linksToInsert = keywordIDs.map((k: any) => ({
97+
projectID: projectId,
98+
keywordID: k.keywordID
99+
}));
100+
101+
// Insere
102+
if (linksToInsert.length > 0) {
103+
await trx('ProjectsKeywords').insert(linksToInsert);
104+
}
105+
}
106+
}
107+
108+
// Retorna o projeto atualizado
109+
return await trx('Projects').where({ projectID: projectId }).first();
110+
});
46111
}
47112

48113
async userProjects(creatorID: number){

backend/src/controller/requestController.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,18 @@ class requestController {
9191
throw error;
9292
}
9393
}
94+
95+
async updateProject(projectId: string, data: projectData, userId: number){
96+
try{
97+
98+
const updatedProject = await businessLogicProject.updateProject(projectId, data, userId);
99+
100+
return updatedProject;
101+
102+
}catch(error){
103+
throw error;
104+
}
105+
}
94106
}
95107

96108
export default new requestController();

backend/src/routes.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,37 @@ routes.get('/api/user/projects', authMiddleware, async(request, response) => {
134134
}
135135
})
136136

137+
// Endpoint para atualizar um projeto existente
138+
routes.put('/api/user/updateproject/:projectId', authMiddleware, async(request, response) => {
139+
try{
140+
const { projectId } = request.params;
141+
const updatedData = request.body;
142+
const creatorID = request.user.id;
143+
144+
const updatedProject = await requestController.updateProject(projectId, updatedData, creatorID);
145+
146+
return response.status(200).json({
147+
message: "Projeto atualizado com sucesso!",
148+
project: updatedProject
149+
});
150+
151+
}catch(error){
152+
153+
if (error instanceof z.ZodError) {
154+
return response.status(400).json({
155+
message: "Erro de validação",
156+
errors: error.flatten().fieldErrors
157+
});
158+
}
159+
160+
if(error instanceof Error){
161+
return response.status(400).json({ message: error.message });
162+
}
163+
164+
console.error("Erro interno no servidor:", error);
165+
return response.status(500).json({ message: "Erro interno no servidor." });
166+
167+
}
168+
});
169+
137170
export default routes;

frontend/src/API/Project.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export interface ProjectProps {
2+
id?: string;
23
title: string;
34
description: string;
45
technologies: string[];
@@ -33,7 +34,7 @@ export async function NewProject(data: ProjectProps) {
3334

3435
export async function UpdateProject(projectId: string, data: ProjectProps) {
3536

36-
/* const response = await fetch(`http://localhost:3000/api/editproject/${projectId}`, {
37+
const response = await fetch(`http://localhost:3000/api/user/updateproject/${projectId}`, {
3738
method: 'PUT',
3839
headers: {
3940
'Content-Type': 'application/json',
@@ -45,7 +46,7 @@ export async function UpdateProject(projectId: string, data: ProjectProps) {
4546
if (!response.ok) {
4647
const errorData = await response.json();
4748
throw new Error(errorData.message);
48-
} */
49+
}
4950
}
5051

5152
export async function GetFeedProjects(): Promise<ProjectProps[]> {
@@ -57,11 +58,13 @@ export async function GetFeedProjects(): Promise<ProjectProps[]> {
5758
// Por agora, vamos simular a resposta da API:
5859
return [
5960
{
61+
id: '1',
6062
title: 'Projeto CTable (React)',
6163
description: 'Post da "ceci" (usuário logado)',
6264
technologies: ['React', 'TS'], status: 'em-andamento', startDate: parseDate('01/10/2026')
6365
},
6466
{
67+
id: '2',
6568
title: 'Projeto de Outra Pessoa',
6669
description: 'Post de outro usuário...',
6770
technologies: ['Python'], status: 'finalizado', startDate: parseDate('01/10/2025')

frontend/src/components/domain/Postcard/index.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,13 @@ export default function Postcard({ post, showMenu }: PostcardProps) {
4545
}, [isMenuOpen]);
4646

4747
const handleEditClick = () => {
48-
// Navega para a página de edição e passa o objeto 'post' (projeto)
49-
// O 'state' é uma forma segura de passar os dados
50-
navigate(`/editProject/${post.name}`, { state: { projectToEdit: post } });
48+
const projectId = (post as any).id || (post as any).projectID;
49+
50+
if (projectId) {
51+
navigate(`/editProject/${projectId}`, { state: { projectToEdit: post } });
52+
} else {
53+
console.error("Erro: ID do projeto não encontrado no objeto:", post);
54+
}
5155
};
5256

5357
const handleDeleteProject = async () => {
@@ -78,10 +82,10 @@ export default function Postcard({ post, showMenu }: PostcardProps) {
7882
)}
7983

8084
<S.PostHeader>
81-
<img src={(post as any).avatarUrl || 'url_placeholder_avatar.png'} alt={post.name} />
82-
<span>{post.name}</span>
85+
<img src={(post as any).avatarUrl || 'url_placeholder_avatar.png'} alt={post.title} />
86+
<span>{post.title}</span>
8387
{/* Assumindo que você tenha um autor no post */}
84-
<small>{(post as any).author?.name || 'Autor'}</small>
88+
<small>{(post as any).author?.title || 'Autor'}</small>
8589
</S.PostHeader>
8690

8791
<S.PostContent>
@@ -114,7 +118,7 @@ export default function Postcard({ post, showMenu }: PostcardProps) {
114118
{/* Conteúdo interno do Modal */}
115119
<div style={{ textAlign: 'center' }}>
116120
<p style={{ marginBottom: '24px', color: '#555' }}>
117-
Tem certeza que deseja excluir permanentemente o projeto <strong>{post.name}</strong>?
121+
Tem certeza que deseja excluir permanentemente o projeto <strong>{post.title}</strong>?
118122
</p>
119123

120124
<ModalS.ModalActions>

frontend/src/pages/CreateProject/index.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,18 @@ interface ProjectFormProps extends Omit<ProjectProps, 'startDate'> {
2020
export default function CreateProject() {
2121
const navigate = useNavigate();
2222
const location = useLocation(); // Hook para ler o state
23-
const { projectId } = useParams<{ projectId?: string }>(); // Hook para ler o ID da URL
2423
const [keywords, setKeywords] = useState<string[]>([]);
2524

26-
// Verifica se estamos em modo de EDIÇÃO
27-
// Tenta pegar o projeto enviado pelo 'state' da navegação
28-
const projectToEdit = location.state?.projectToEdit as (ProjectProps & { id: string }) | undefined;
29-
const isEditMode = !!projectToEdit; // Se projectToEdit existe, estamos editando
25+
const { projectId: paramId } = useParams<{ projectId?: string }>();
26+
27+
// Recupera o projeto do estado
28+
const projectToEdit = location.state?.projectToEdit as (ProjectProps & { id?: string; projectID?: string }) | undefined;
29+
// Define se é modo edição
30+
const isEditMode = !!projectToEdit;
31+
32+
const validProjectId = projectToEdit?.id
33+
|| projectToEdit?.projectID
34+
|| (paramId !== 'undefined' ? paramId : undefined);
3035

3136
const formatDateToString = (date?: Date | string) => {
3237
if (!date) return "";
@@ -74,10 +79,10 @@ export default function CreateProject() {
7479
};
7580

7681
try {
77-
if (isEditMode) {
82+
if (isEditMode && validProjectId) {
7883
// MODO DE EDIÇÃO
79-
console.log("Atualizando Projeto:", projectId, finalData);
80-
// UpdateProject(projectId, finalData);
84+
console.log("Atualizando Projeto:", validProjectId, finalData);
85+
UpdateProject(validProjectId, finalData);
8186
setNotification({ message: 'Projeto atualizado com sucesso!', type: 'success' });
8287
} else {
8388
// MODO DE CRIAÇÃO

0 commit comments

Comments
 (0)