diff --git a/backend/src/business/businessLogicProject.ts b/backend/src/business/businessLogicProject.ts index d9fc796..841b3f1 100644 --- a/backend/src/business/businessLogicProject.ts +++ b/backend/src/business/businessLogicProject.ts @@ -36,10 +36,6 @@ class BusinessLogicProject{ if (keywordsToInsert.length > 0) { await trx('ProjectsKeywords').insert(keywordsToInsert); - // --- NOVA LÓGICA DE ASSOCIAÇÃO AUTOMÁTICA --- - - // 3. Busca Comunidades que usam essas mesmas Keywords - // Usamos .distinct() para evitar duplicatas (caso uma comunidade tenha React E Node, por exemplo) const matchingCommunities = await trx('CommunitiesKeywords') .whereIn('keywordID', keywordIDs.map(k => k.keywordID)) .distinct('communityID'); @@ -226,7 +222,6 @@ class BusinessLogicProject{ } async getProjectById(projectId: string) { - // Busca o projeto com dados do autor (join) const project = await knex('Projects') .join('User', 'Projects.creatorID', '=', 'User.id') .where('Projects.projectID', projectId) @@ -242,13 +237,11 @@ class BusinessLogicProject{ throw new Error("Projeto não encontrado."); } - // Busca as tecnologias (keywords) associadas const keywords = await knex('ProjectsKeywords') .join('Keywords', 'ProjectsKeywords.keywordID', '=', 'Keywords.keywordID') .where('ProjectsKeywords.projectID', projectId) .select('Keywords.tag'); - // Retorna o objeto formatado return { ...project, technologies: keywords.map((k: any) => k.tag) diff --git a/backend/src/data/migrations/20251121192246_Communities.ts b/backend/src/data/migrations/20251121192246_Communities.ts index ca5a1db..0ebe3ac 100644 --- a/backend/src/data/migrations/20251121192246_Communities.ts +++ b/backend/src/data/migrations/20251121192246_Communities.ts @@ -9,7 +9,6 @@ export async function up(knex: Knex): Promise { table.timestamp('createdAt').defaultTo(knex.fn.now()); table.timestamp('updatedAt'); - // Chave estrangeira para o criador (User) table.integer('creatorID').unsigned(); table.foreign('creatorID') .references('id') diff --git a/backend/src/data/migrations/20251121192248_CommunityMembers.ts b/backend/src/data/migrations/20251121192248_CommunityMembers.ts index 8ab5d64..dbe83e3 100644 --- a/backend/src/data/migrations/20251121192248_CommunityMembers.ts +++ b/backend/src/data/migrations/20251121192248_CommunityMembers.ts @@ -5,10 +5,9 @@ export async function up(knex: Knex): Promise { table.integer('userID').unsigned(); table.integer('communityID').unsigned(); - table.string('role', 255).defaultTo('member'); // ex: 'admin', 'member' + table.string('role', 255).defaultTo('member'); table.timestamp('joinedAt').defaultTo(knex.fn.now()); - // Chaves Estrangeiras table.foreign('userID') .references('id') .inTable('User') @@ -18,8 +17,7 @@ export async function up(knex: Knex): Promise { .references('communityID') .inTable('Communities') .onDelete('CASCADE'); - - // Chave Primária Composta (Um usuário só pode entrar na mesma comunidade uma vez) + table.primary(['userID', 'communityID']); }); } diff --git a/backend/src/data/migrations/20251121192251_CommunitiesKeywords.ts b/backend/src/data/migrations/20251121192251_CommunitiesKeywords.ts index d92e83d..fd95fd8 100644 --- a/backend/src/data/migrations/20251121192251_CommunitiesKeywords.ts +++ b/backend/src/data/migrations/20251121192251_CommunitiesKeywords.ts @@ -5,7 +5,6 @@ export async function up(knex: Knex): Promise { table.integer('communityID').unsigned(); table.integer('keywordID').unsigned(); - // Chaves Estrangeiras table.foreign('communityID') .references('communityID') .inTable('Communities') @@ -16,7 +15,6 @@ export async function up(knex: Knex): Promise { .inTable('Keywords') .onDelete('CASCADE'); - // Chave Primária Composta table.primary(['communityID', 'keywordID']); }); } diff --git a/backend/src/data/migrations/20251121192253_ProjectCommunities.ts b/backend/src/data/migrations/20251121192253_ProjectCommunities.ts index 065b3ba..bad7f04 100644 --- a/backend/src/data/migrations/20251121192253_ProjectCommunities.ts +++ b/backend/src/data/migrations/20251121192253_ProjectCommunities.ts @@ -7,7 +7,6 @@ export async function up(knex: Knex): Promise { table.timestamp('associatedAt').defaultTo(knex.fn.now()); - // Chaves Estrangeiras table.foreign('projectID') .references('projectID') .inTable('Projects') @@ -18,7 +17,7 @@ export async function up(knex: Knex): Promise { .inTable('Communities') .onDelete('CASCADE'); - // Chave Primária Composta (Um projeto não pode ser linkado duas vezes na mesma comunidade) + table.primary(['projectID', 'communityID']); }); } diff --git a/backend/src/middleware/errorHandler.ts b/backend/src/middleware/errorHandler.ts index 2ee8538..cfea7b2 100644 --- a/backend/src/middleware/errorHandler.ts +++ b/backend/src/middleware/errorHandler.ts @@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from 'express'; import { z } from 'zod'; export function errorHandler(err: unknown, req: Request, res: Response, next: NextFunction) { - // Verifica se o erro é do Zod + if (err instanceof z.ZodError) { return res.status(400).json({ message: 'Erro de validação', @@ -18,7 +18,6 @@ export function errorHandler(err: unknown, req: Request, res: Response, next: Ne console.error(err); - // Retorna erro genérico para o usuário return res.status(500).json({ message: 'Erro interno do servidor', }); diff --git a/backend/src/routes.ts b/backend/src/routes.ts index 68348ab..bfbe86b 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -6,7 +6,6 @@ import { request } from 'http'; const routes = express.Router(); -// Endpoint para CADASTRAR um novo usuário routes.post('/api/register', async (request, response) => { console.log("Recebendo requisição de cadastro:", request.body); const {user, token} = await RequestController.createUser(request.body); @@ -17,7 +16,6 @@ routes.post('/api/register', async (request, response) => { }); }); -// Endpoint para verificar a existência de usuário routes.post('/api/login', async(request, response) => { const { user, token } = await RequestController.enterUser(request.body); return response.status(200).json({ @@ -28,14 +26,13 @@ routes.post('/api/login', async(request, response) => { }); -// Endpoint para enviar ao fronend os keywords disponíveis + routes.get('/api/keywords', async(request, response) => { const tags = await RequestController.getKeywords(); return response.status(200).json(tags); }); -// Endpoint para criar projeto routes.post('/api/user/newproject', authMiddleware, async(request, response) =>{ const projectData = request.body; @@ -50,7 +47,7 @@ routes.post('/api/user/newproject', authMiddleware, async(request, response) => }); -// Endpoint para enviar ao frontend os projetos de determinado usuário + routes.get('/api/user/projects', authMiddleware, async(request, response) => { const creatorID = request.user.id; const projects = await RequestController.getUserProjects(creatorID); @@ -59,7 +56,6 @@ routes.get('/api/user/projects', authMiddleware, async(request, response) => { }) }); -// Endpoint para atualizar um projeto existente routes.put('/api/user/updateproject/:projectId', authMiddleware, async(request, response) => { const { projectId } = request.params; const updatedData = request.body; @@ -92,7 +88,6 @@ routes.delete('/api/user/leavecommunity/:communityID', authMiddleware, async(req return response.status(200).json(result); }) -// Endpoint para criar uma comunidade routes.post('/api/newcommunity', authMiddleware, async(request, response) => { const newCommunity = await RequestController.newCommunity(request.body, request.user.id); diff --git a/backend/src/test/community.test.ts b/backend/src/test/community.test.ts index 417ac3f..78e8fb2 100644 --- a/backend/src/test/community.test.ts +++ b/backend/src/test/community.test.ts @@ -2,8 +2,8 @@ import request from 'supertest'; import app from '../app'; import knex from '../data'; -let tokenAdmin: string; // Criador -let tokenUser: string; // Membro +let tokenAdmin: string; +let tokenUser: string; let user1Id: number; let user2Id: number; diff --git a/backend/src/test/profile.test.ts b/backend/src/test/profile.test.ts index 680119b..82c6954 100644 --- a/backend/src/test/profile.test.ts +++ b/backend/src/test/profile.test.ts @@ -77,9 +77,9 @@ describe('Fluxo de Perfil de Usuário', () => { const response = await request(app) .put('/api/user/editprofile') .set('Authorization', `Bearer ${token}`) - .send({}); // Objeto vazio + .send({}); - // O controller deve lançar erro "Nenhum dado para atualizar" + expect(response.status).not.toBe(200); }); }); diff --git a/backend/src/test/project.test.ts b/backend/src/test/project.test.ts index 12d43f9..321c15a 100644 --- a/backend/src/test/project.test.ts +++ b/backend/src/test/project.test.ts @@ -64,7 +64,6 @@ describe('Fluxo de Projetos', () => { expect(response.body.project).toHaveProperty('projectID'); expect(response.body.project.title).toBe('Meu Projeto React'); - // Verifica se salvou no banco const projectDb = await knex('Projects').where({ title: 'Meu Projeto React' }).first(); expect(projectDb).toBeDefined(); }); @@ -90,7 +89,7 @@ describe('Fluxo de Projetos', () => { }); it('deve atualizar um projeto existente (Status 200)', async () => { - // Cria projeto + const resCreate = await request(app) .post('/api/user/newproject') .set('Authorization', `Bearer ${tokenUser1}`) @@ -98,7 +97,6 @@ describe('Fluxo de Projetos', () => { const projectId = resCreate.body.project.projectID; - // Atualiza const response = await request(app) .put(`/api/user/updateproject/${projectId}`) .set('Authorization', `Bearer ${tokenUser1}`) @@ -217,19 +215,18 @@ describe('Fluxo de Projetos', () => { }); it('deve impedir exclusão de comentário por quem não é o autor', async () => { - // User 1 cria projeto + const resProj = await request(app).post('/api/user/newproject') .set('Authorization', `Bearer ${tokenUser1}`) .send({ title: 'Post', description: '...', status: 'Aberto', startDate: new Date() }); const pId = resProj.body.project.projectID; - // User 1 comenta + const resComment = await request(app).post(`/api/project/${pId}/comments`) .set('Authorization', `Bearer ${tokenUser1}`) .send({ content: 'Meu comentário' }); const cId = resComment.body.commentID; - // User 2 tenta deletar const response = await request(app) .delete(`/api/project/${cId}/deletecomment`) .set('Authorization', `Bearer ${tokenUser2}`); @@ -239,7 +236,7 @@ describe('Fluxo de Projetos', () => { it('deve retornar 404 ao buscar projeto inexistente', async () => { const response = await request(app) - .get('/api/projects/999999') // ID que não existe + .get('/api/projects/999999') .set('Authorization', `Bearer ${tokenUser1}`); expect(response.status).toBe(404); @@ -298,7 +295,6 @@ describe('Fluxo de Projetos', () => { .set('Authorization', `Bearer ${tokenUser2}`) .send({ content: 'Segundo!' }); - // Busca comentários const response = await request(app).get(`/api/project/${projectId}/comments`); expect(response.status).toBe(200); @@ -306,13 +302,12 @@ describe('Fluxo de Projetos', () => { }); it('deve deletar um comentário próprio com sucesso', async () => { - // Cria comentário + const commRes = await request(app).post(`/api/project/${projectId}/comments`) .set('Authorization', `Bearer ${tokenUser1}`) .send({ content: 'Vou deletar' }); const commentId = commRes.body.commentID; - // Deleta const response = await request(app) .delete(`/api/project/${commentId}/deletecomment`) .set('Authorization', `Bearer ${tokenUser1}`); @@ -320,7 +315,6 @@ describe('Fluxo de Projetos', () => { expect(response.status).toBe(200); expect(response.body.message).toBe('Comentário deletado com sucesso.'); - // Verifica no banco const comments = await request(app).get(`/api/project/${projectId}/comments`); expect(comments.body).toHaveLength(0); }); @@ -346,7 +340,7 @@ describe('Fluxo de Projetos', () => { expect(response.status).toBe(200); expect(response.body).toHaveLength(2); - // Verifica se o título do projeto vem junto (conforme businessLogicProject.getUserComments) + const titulos = response.body.map((c: any) => c.projectTitle); expect(titulos).toContain('Discussão'); expect(titulos).toContain('Outro Projeto'); diff --git a/frontend/src/utils/getAvatarurl.ts b/frontend/src/utils/getAvatarurl.ts index a7aca93..3cb7851 100644 --- a/frontend/src/utils/getAvatarurl.ts +++ b/frontend/src/utils/getAvatarurl.ts @@ -1,6 +1,5 @@ -// Aceita um segundo parâmetro opcional 'style' + export function getAvatarUrl(seed: string, style: 'bottts' | 'identicon' = 'bottts') { - // Se não passar nada, usa 'bottts' (robôs) - // Se passar 'identicon', usa os padrões geométricos + return `https://api.dicebear.com/9.x/${style}/svg?seed=${seed}`; } \ No newline at end of file