diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0010a93..5595bf5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -233,7 +233,7 @@ Closes #42 # Revisão -Todo Pull Request pode receber comentários e solicitações de alteração. +Todo Pull Request pode receber comentários e solicitações de alteração. Se você for um revisor ou quiser entender como o processo de revisão e aprovação funciona no projeto, consulte o [`docs/PR_REVIEW_GUIDE.md`](docs/PR_REVIEW_GUIDE.md). O processo de revisão tem como objetivo: diff --git a/README.md b/README.md index abdef68..d99e660 100644 --- a/README.md +++ b/README.md @@ -261,7 +261,9 @@ O deploy é automático via **Vercel** ao fazer merge em `main`. ## Contribuindo -Contribuições são bem-vindas! Leia [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) para entender os padrões adotados antes de abrir um PR. +Contribuições são bem-vindas! Leia [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) para entender os padrões de código adotados antes de começar a desenvolver. + +Se você estiver revisando Pull Requests de outros colaboradores ou quiser entender as regras de aceite de código, consulte o [`docs/PR_REVIEW_GUIDE.md`](docs/PR_REVIEW_GUIDE.md). 1. Faça um fork e clone o repositório 2. Crie uma branch: `git checkout -b feat/minha-feature` diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index de88493..4bad483 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -24,6 +24,7 @@ Este documento descreve a arquitetura técnica do projeto, os padrões de design 16. [Deploy e CI/CD](#16-deploy-e-cicd) 17. [Docker e Containers](#17-docker-e-containers) 18. [Scripts Utilitários](#18-scripts-utilitários) +19. [Revisão de Código e PRs](#19-revisão-de-código-e-prs) --- @@ -851,3 +852,11 @@ npm run migrate:members-to-profiles -- --execute ``` **Pré-requisito:** a variável `FIREBASE_SERVICE_ACCOUNT` deve conter o JSON da service account do Firebase Admin, ou `GOOGLE_APPLICATION_CREDENTIALS` deve apontar para o arquivo JSON equivalente. + +--- + +## 19. Revisão de Código e PRs + +Para manter a codebase organizada e estável à medida que mais desenvolvedores contribuem, adotamos um processo estruturado de revisão de Pull Requests. + +O fluxo de trabalho, os critérios de aceitação para merges e os checklists de revisão estão documentados em detalhes no [Guia de Revisão de PRs](./PR_REVIEW_GUIDE.md). diff --git a/docs/CODEBASE_MAP.md b/docs/CODEBASE_MAP.md index c806b3b..81a099b 100644 --- a/docs/CODEBASE_MAP.md +++ b/docs/CODEBASE_MAP.md @@ -150,12 +150,13 @@ match-tech/ │ └── main.tsx # createRoot + global error listeners │ ├── docs/ -│ ├── ARCHITECTURE.md ← Referência técnica principal (atualizada) -│ ├── CODEBASE_MAP.md ← Este arquivo -│ ├── VISION_MATCH_TECH.md ← Visão de produto e design system -│ ├── TODO_MATCH_TECH.md ← Histórico de desenvolvimento (arquivo) -│ ├── FRONTEND_BLUEPRINT.md ← Blueprint original (depreciado → ver ARCHITECTURE.md) -│ └── hackathon_tech_floripa_2026_strategy.md ← Estratégia do evento +│ ├── [ARCHITECTURE.md](./ARCHITECTURE.md) ← Referência técnica principal (atualizada) +│ ├── [CODEBASE_MAP.md](./CODEBASE_MAP.md) ← Este arquivo +│ ├── [VISION_MATCH_TECH.md](./VISION_MATCH_TECH.md) ← Visão de produto e design system +│ ├── [TODO_MATCH_TECH.md](./TODO_MATCH_TECH.md) ← Histórico de desenvolvimento (histórico) +│ ├── [FRONTEND_BLUEPRINT.md](./FRONTEND_BLUEPRINT.md) ← Blueprint original (depreciado → ver ARCHITECTURE.md) +│ ├── [PR_REVIEW_GUIDE.md](./PR_REVIEW_GUIDE.md) ← Guia de revisão de Pull Requests +│ └── [hackathon_tech_floripa_2026_strategy.md](./hackathon_tech_floripa_2026_strategy.md) ← Estratégia do evento │ ├── .github/workflows/ │ └── ci.yml # typecheck → lint → build (Node 22) diff --git a/docs/PR_REVIEW_GUIDE.md b/docs/PR_REVIEW_GUIDE.md new file mode 100644 index 0000000..9cb3151 --- /dev/null +++ b/docs/PR_REVIEW_GUIDE.md @@ -0,0 +1,183 @@ +# 🧭 Guia de Revisão de Pull Requests — Match Tech + +> Para o Tony: dev júnior, criador do projeto, principal mantenedor do `MatchDock/match-tech`. + +--- + +## O que é um Pull Request (PR)? + +É quando alguém do time faz alterações no código e pede para você **revisar antes de aceitar**. Seu papel como mantenedor é funcionar como um porteiro: decidir se aquela mudança entra ou não no projeto. + +> [!NOTE] +> **Você não precisa entender cada linha de código.** O objetivo da revisão é entender *o que* mudou, *por quê*, e se isso pode quebrar algo. + +--- + +## 🔁 Seu Fluxo de Revisão (Passo a Passo) + +### 1. Leia a descrição do PR primeiro + +Antes de ver qualquer código, leia o texto que a pessoa escreveu no PR. Pergunte-se: + +- ✅ O PR descreve o que mudou? +- ✅ Ele referencia uma issue? (ex: `Closes #42`) +- ✅ A branch está correta? (deve ir para `develop`, não para `main`) +- ❌ Se a pessoa só colocou "atualiza código" sem explicar nada → peça uma descrição melhor + +**Link rápido para ver os PRs:** [github.com/MatchDock/match-tech/pulls](https://github.com/MatchDock/match-tech/pulls) + +--- + +### 2. Entenda o escopo — quantas coisas ele mexeu? + +Na aba **"Files changed"** do PR, veja: + +| Sinal | O que significa | +|-------|-----------------| +| Verde (linhas `+`) | Código **adicionado** | +| Vermelho (linhas `-`) | Código **removido** | +| Poucos arquivos alterados | PR pequeno ✅ mais fácil de revisar | +| Muitos arquivos e linhas | PR gigante ⚠️ pode ser difícil de aprovar de uma vez | + +> [!TIP] +> O `CONTRIBUTING.md` do projeto pede PRs pequenos e focados. Se alguém mandou um PR com 50 arquivos alterados que não tem relação entre si, você pode pedir para dividir. + +--- + +### 3. Verifique se o PR segue as regras do projeto + +Baseado no [CONTRIBUTING.md](../CONTRIBUTING.md): + +- [ ] A branch do PR tem nome correto? (`feat/`, `bug/`, `docs/`, `task/`, `refactor/`) +- [ ] O PR está indo para `develop` (não para `main`)? +- [ ] Os commits seguem Conventional Commits? (`feat:`, `fix:`, `docs:`, `refactor:`) +- [ ] O PR faz **uma coisa só** (não mistura feature nova + bugfix + refactor)? + +--- + +### 4. Perguntas práticas para revisar o código + +Você não precisa ser expert. Faça estas perguntas ao olhar o diff: + +#### 🟢 Perguntas básicas (todo PR) +- O que essa mudança adiciona ou resolve? +- Isso pode quebrar algo que já funcionava? +- O nome dos arquivos e funções faz sentido? + +#### 🟡 Para PRs de feature (nova funcionalidade) +- Isso resolve a issue que está referenciada? +- A feature se encaixa no estilo visual do projeto? +- Mexe no Firebase/Firestore de forma que pode perder dados? + +#### 🔴 Para PRs de refatoração (reorganização do código) +- O comportamento da UI continua igual para o usuário? +- Passou no build? (`npm run build` ou CI do GitHub Actions) +- Passou no typecheck do TypeScript? (`tsc --noEmit`) + +#### 🔵 Para PRs de infra/docs +- A documentação faz sentido e está em português? +- Não quebra nenhuma configuração existente? + +--- + +### 5. Como pedir mudanças sem ser rude + +Quando algo está errado ou precisa de ajuste, você pode comentar assim: + +```text +Oi! Ficou bem legal, mas tenho uma dúvida/sugestão: + +❓ Esse arquivo [X] foi modificado, mas não estava relacionado à issue #42. +Você pode remover essa alteração desse PR? + +💡 Sugestão: [explica o que preferia ver] + +Fora isso, está bem feito! 🚀 +``` + +> [!IMPORTANT] +> No GitHub, você pode comentar linha por linha. Clique no `+` que aparece ao lado das linhas no "Files changed" para deixar um comentário específico. + +--- + +### 6. O CI do projeto já faz parte do trabalho por você! + +O repositório tem um **GitHub Actions CI**. Isso significa que automaticamente, em todo PR, o sistema verifica: + +- ✅ O TypeScript compila sem erros (`tsc --noEmit`) +- ✅ O build do Vite funciona (`npm run build`) +- ✅ Todos os testes passam (`npm test`) + +Se o CI falhar (mostrar ❌ vermelho no PR), **você não precisa aprovar**. Peça para o contribuidor corrigir primeiro. + +--- + +## 🎯 Checklist Rápido — Cole nos comentários do PR + +```text +## Checklist de Review ✅ + +- [ ] Descrição clara do que mudou +- [ ] Referencia a issue correta (ex: `Closes #XX`) +- [ ] Branch correta → develop (não main) +- [ ] Nome da branch no padrão (feat/, bug/, docs/...) +- [ ] Commits em Conventional Commits +- [ ] PR focado em uma única coisa +- [ ] CI passou (TypeScript + Build + Testes) ✅ +- [ ] Não quebra funcionalidades existentes +``` + +--- + +## 🚦 Quando aprovar (Merge), pedir mudanças ou fechar + +| Situação | Ação | +|----------|------| +| Tudo certo, CI passou | ✅ **Aprovar e fazer Merge** | +| Tem erros corrigíveis | 🔄 **Request changes** — pedir ajustes | +| PR gigante sem foco | 📝 Pedir para dividir em PRs menores | +| CI falhou (❌ vermelho) | 🚫 Não aprovar até corrigir | +| PR foi para `main` diretamente | 🚫 Fechar e pedir para reabrir para `develop` | +| Não tem relação com nenhuma issue | ❓ Perguntar o contexto antes de decidir | + +--- + +## 📚 Tipos de PR mais comuns no match-tech + +Com base no histórico do projeto, esses são os tipos que você vai ver: + +### PR de Feature (`feat/`) +Ex: adicionar filtro de perfis, nova página, novo componente UI +- Foco: **funciona? se encaixa no design?** + +### PR de Refatoração (`refactor/`) +Ex: PR #56 foi uma refatoração arquitetural gigante (Clean Architecture, Router v7...) +- Foco: **o comportamento mudou? CI passou? tem testes?** + +### PR de Documentação (`docs/`) +Ex: PR #36 atualizou o CONTRIBUTING.md +- Foco: **está em português? é claro? está correto?** + +### PR de Infra (`infra/`) +Ex: PR #57 adicionou GitHub Actions CI +- Foco: **vai rodar na conta da org? tem segredos expostos?** + +--- + +## 💬 Como pedir análise para mim (Antigravity) + +Se receber um PR complicado, pode me mandar assim no chat: + +```text +Me ajuda a revisar o PR #XX: [link] +``` + +Eu leio o diff, o histórico de commits, e te entrego um resumo em português explicando: +- O que mudou +- Se está correto +- O que pedir para o contribuidor ajustar +- Se você pode aprovar ou não + +--- + +> 🚀 **Você está indo bem!** Gerir um repositório open source com contribuidores externos durante um hackathon é bastante coisa. O importante é manter o ritmo, ser justo nas revisões, e não deixar PRs parados por muito tempo. diff --git a/docs/VISION_MATCH_TECH.md b/docs/VISION_MATCH_TECH.md index b03abf3..0686418 100644 --- a/docs/VISION_MATCH_TECH.md +++ b/docs/VISION_MATCH_TECH.md @@ -4,10 +4,11 @@ **Data de Criação:** 07 de Maio de 2026 **Última Atualização:** 07 de Maio de 2026 -> **ATENÇÃO AGENTE DE IA:** Este é o documento de referência PRIMÁRIO do projeto. -> Sempre que você sentir que está "alucinando" ou se perdendo na direção do código, -> volte aqui. Tudo o que este documento diz sobre identidade visual, arquitetura, -> e funcionalidades é LEI. Não invente funcionalidades que não estejam aqui. +> **ATENÇÃO AGENTE DE IA:** Este é o documento de referência PRIMÁRIO de produto do projeto. +> Sempre que você sentir que está "alucinando" ou se perdendo na direção do código ou regras de negócio, +> volte aqui. Tudo o que este documento diz sobre identidade visual, arquitetura de alto nível, +> e funcionalidades é LEI. +> Para a especificação técnica detalhada, organização de pastas pós-refatoração e padrões de código, consulte sempre o [ARCHITECTURE.md](./ARCHITECTURE.md). --- @@ -334,10 +335,10 @@ interface Squad { ## 11. O QUE ESTE DOCUMENTO NÃO COBRE -- Detalhes de implementação de código (veja `FRONTEND_BLUEPRINT.md`). -- Roadmap detalhado com checklist (veja `TODO_MATCH_TECH.md`). -- Regras de Firestore atualizadas (veja `../firestore.rules`). -- Estratégia pessoal do Tony para o hackathon (veja `hackathon_tech_floripa_2026_strategy.md`). +- Detalhes de implementação de código (consulte a referência atualizada em [ARCHITECTURE.md](./ARCHITECTURE.md) ou o histórico em [FRONTEND_BLUEPRINT.md](./FRONTEND_BLUEPRINT.md)). +- Roadmap detalhado com checklist (veja o arquivo histórico [TODO_MATCH_TECH.md](./TODO_MATCH_TECH.md)). +- Regras de Firestore atualizadas (veja [firestore.rules](../firestore.rules)). +- Estratégia pessoal do Tony para o hackathon (veja [hackathon_tech_floripa_2026_strategy.md](./hackathon_tech_floripa_2026_strategy.md)). --- diff --git a/src/infrastructure/firebase/profileRepository.ts b/src/infrastructure/firebase/profileRepository.ts index 10c7040..37529ca 100644 --- a/src/infrastructure/firebase/profileRepository.ts +++ b/src/infrastructure/firebase/profileRepository.ts @@ -21,8 +21,55 @@ import { db } from "@/shared/lib/firebase/firebase.client"; * Handles all Firestore operations related to member profiles * Uses Zod for runtime validation */ +function mapFirestoreToMemberData( + id: string, + data: Record | undefined, +): Record | undefined { + if (!data) return data; + + const mapped = { ...data }; + + // Map identification fields + mapped.uid = (data.uid ?? data.userId ?? id) as string; + mapped.displayName = (data.displayName ?? data.name ?? "") as string; + + // Map role + mapped.role = (data.role ?? data.primaryRole ?? "") as string; + + // Map squad status + mapped.squadStatus = (data.squadStatus ?? data.status ?? "open") as string; + + // Map tags from canvas if tags is not present + if (!mapped.tags) { + const tags: { name: string; sentiment: "love" | "ok" | "veto" }[] = []; + const canvas = data.canvas as + | { loves?: string[]; comfort?: string[]; veto?: string[] } + | undefined; + if (canvas) { + if (canvas.loves && Array.isArray(canvas.loves)) { + canvas.loves.forEach((name: string) => { + if (name) tags.push({ name, sentiment: "love" }); + }); + } + if (canvas.comfort && Array.isArray(canvas.comfort)) { + canvas.comfort.forEach((name: string) => { + if (name) tags.push({ name, sentiment: "ok" }); + }); + } + if (canvas.veto && Array.isArray(canvas.veto)) { + canvas.veto.forEach((name: string) => { + if (name) tags.push({ name, sentiment: "veto" }); + }); + } + } + mapped.tags = tags; + } + + return mapped; +} + export class FirebaseProfileRepository implements IProfileRepository { - private collectionName = "members"; + private collectionName = "profiles"; async getProfile(uid: string): Promise { try { @@ -34,7 +81,8 @@ export class FirebaseProfileRepository implements IProfileRepository { } const data = docSnap.data(); - return MemberSchema.parse(data); + const mappedData = mapFirestoreToMemberData(uid, data); + return MemberSchema.parse(mappedData); } catch (error) { if (error instanceof AppError) throw error; if (error instanceof Error) { @@ -58,7 +106,8 @@ export class FirebaseProfileRepository implements IProfileRepository { throw new AppError("UNAUTHORIZED", `Profile ${uid} is not public`); } - return PublicMemberSchema.parse(data); + const mappedData = mapFirestoreToMemberData(uid, data); + return PublicMemberSchema.parse(mappedData); } catch (error) { if (error instanceof AppError) throw error; if (error instanceof Error) { @@ -71,8 +120,35 @@ export class FirebaseProfileRepository implements IProfileRepository { async updateProfile(uid: string, data: Partial): Promise { try { const docRef = doc(this.getCollection(), uid); + + const dbData: Record = { ...data } as Record; + + if (data.displayName !== undefined) { + dbData.name = data.displayName; + delete dbData.displayName; + } + if (data.role !== undefined) { + dbData.primaryRole = data.role; + delete dbData.role; + } + if (data.squadStatus !== undefined) { + dbData.status = data.squadStatus; + delete dbData.squadStatus; + } + if (data.tags !== undefined) { + const loves = data.tags.filter((t) => t.sentiment === "love").map((t) => t.name); + const comfort = data.tags.filter((t) => t.sentiment === "ok").map((t) => t.name); + const veto = data.tags.filter((t) => t.sentiment === "veto").map((t) => t.name); + dbData.canvas = { loves, comfort, veto }; + delete dbData.tags; + } + if (data.uid !== undefined) { + dbData.userId = data.uid; + delete dbData.uid; + } + const updateData = { - ...data, + ...dbData, updatedAt: serverTimestamp(), }; @@ -91,16 +167,17 @@ export class FirebaseProfileRepository implements IProfileRepository { const constraints = [where("visibility", "==", "public")]; if (filters?.role) { - constraints.push(where("role", "==", filters.role)); + constraints.push(where("primaryRole", "==", filters.role)); } const q = query(this.getCollection(), ...constraints); const querySnapshot = await getDocs(q); const profiles: Member[] = []; - querySnapshot.forEach((doc) => { + querySnapshot.forEach((docSnap) => { try { - const parsed = MemberSchema.parse(doc.data()); + const mappedData = mapFirestoreToMemberData(docSnap.id, docSnap.data()); + const parsed = MemberSchema.parse(mappedData); profiles.push(parsed); } catch { // Skip profiles that fail validation