Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_MOCK_API_URL=http://localhost:3001
25 changes: 25 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
coverage
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
8 changes: 8 additions & 0 deletions frontend/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["react", "typescript", "oxc"],
"rules": {
"react/rules-of-hooks": "error",
"react/only-export-components": ["warn", { "allowConstantExport": true }]
}
}
71 changes: 71 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Verity — Cadastro (Teste Técnico Front-End)

Formulário de cadastro em 3 etapas (Dados Pessoais, Informações Residenciais e
Informações Profissionais) com resumo final e exportação em PDF, construído
com React + TypeScript + Vite.

## Stack

- **React 19** + **TypeScript** + **Vite**
- **React Hook Form** + **Zod** para validação dos formulários
- **react-router-dom** para navegação entre etapas
- **react-imask** para máscaras de Data de Nascimento, CPF, Telefone e CEP
- **jsPDF** para exportação do resumo em PDF
- **json-server** como serviço mockado para busca de CEP e lista de profissões
- **Vitest** + **Testing Library** para os testes unitários (cobertura ≥ 80%)

## Como rodar

Instale as dependências (pnpm):

```bash
pnpm install
```

Suba a aplicação e a API mockada juntas:

```bash
pnpm dev:all
```

Isso inicia o Vite em `http://localhost:5173` e o json-server em
`http://localhost:3001` (dados em `mock/db.json`, com os endpoints
`GET /ceps/:cep` e `GET /professions`).

Se preferir rodar cada um separadamente:

```bash
pnpm dev # front-end
pnpm mock:api # API mockada
```

## Busca de CEP

Ao digitar um CEP completo, a aplicação primeiro consulta o json-server
(`/ceps/:cep`). Caso o CEP não exista no mock, ela cai automaticamente para o
ViaCEP (endpoint público dos Correios/ViaCEP) como fallback — cobrindo tanto o
requisito principal quanto o "plus" do desafio. CEPs de exemplo disponíveis no
mock: `01310-100`, `01001-000`, `20040-020`, `30130-010`, `80010-000`,
`90010-000`, `40010-000`, `70040-010`, `60060-170`, `50030-230`.

## Testes

```bash
pnpm test # roda a suíte uma vez
pnpm test:watch # modo watch
pnpm test:coverage # com relatório de cobertura (mínimo configurado: 80%)
```

## Build

```bash
pnpm build
pnpm preview
```

## Identidade visual

A paleta (azul/branco) e o wordmark "verity" usados são um placeholder até a
logo oficial da Verity ser fornecida — o componente `Logo`
(`src/components/layout/Logo.tsx`) foi isolado exatamente para facilitar essa
troca depois.
13 changes: 13 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Verity | Cadastro</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
86 changes: 86 additions & 0 deletions frontend/mock/db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{
"ceps": [
{
"id": "01310100",
"endereco": "Avenida Paulista",
"bairro": "Bela Vista",
"cidade": "São Paulo",
"estado": "SP"
},
{
"id": "01001000",
"endereco": "Praça da Sé",
"bairro": "Sé",
"cidade": "São Paulo",
"estado": "SP"
},
{
"id": "20040020",
"endereco": "Avenida Rio Branco",
"bairro": "Centro",
"cidade": "Rio de Janeiro",
"estado": "RJ"
},
{
"id": "30130010",
"endereco": "Avenida Afonso Pena",
"bairro": "Centro",
"cidade": "Belo Horizonte",
"estado": "MG"
},
{
"id": "80010000",
"endereco": "Rua XV de Novembro",
"bairro": "Centro",
"cidade": "Curitiba",
"estado": "PR"
},
{
"id": "90010000",
"endereco": "Rua dos Andradas",
"bairro": "Centro Histórico",
"cidade": "Porto Alegre",
"estado": "RS"
},
{
"id": "40010000",
"endereco": "Avenida Sete de Setembro",
"bairro": "Centro",
"cidade": "Salvador",
"estado": "BA"
},
{
"id": "70040010",
"endereco": "Praça dos Três Poderes",
"bairro": "Zona Cívico-Administrativa",
"cidade": "Brasília",
"estado": "DF"
},
{
"id": "60060170",
"endereco": "Avenida Beira Mar",
"bairro": "Praia de Iracema",
"cidade": "Fortaleza",
"estado": "CE"
},
{
"id": "50030230",
"endereco": "Avenida Guararapes",
"bairro": "Santo Antônio",
"cidade": "Recife",
"estado": "PE"
}
],
"professions": [
{ "id": 1, "name": "Desenvolvedor(a) de Software" },
{ "id": 2, "name": "Analista de Sistemas" },
{ "id": 3, "name": "Designer UX/UI" },
{ "id": 4, "name": "Gerente de Projetos" },
{ "id": 5, "name": "Analista Financeiro" },
{ "id": 6, "name": "Advogado(a)" },
{ "id": 7, "name": "Médico(a)" },
{ "id": 8, "name": "Professor(a)" },
{ "id": 9, "name": "Contador(a)" },
{ "id": 10, "name": "Engenheiro(a) Civil" }
]
}
46 changes: 46 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"dev:all": "run-p dev mock:api",
"mock:api": "json-server --port 3001 mock/db.json",
"build": "tsc -b && vite build",
"lint": "oxlint",
"preview": "vite preview",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage"
},
"dependencies": {
"@hookform/resolvers": "^5.4.0",
"axios": "^1.18.1",
"jspdf": "^4.2.1",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"react-hook-form": "^7.81.0",
"react-imask": "^7.6.1",
"react-router-dom": "^7.18.1",
"zod": "^4.4.3"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^24.13.2",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.3",
"@vitest/coverage-v8": "^4.1.10",
"jsdom": "^29.1.1",
"json-server": "1.0.0-beta.15",
"npm-run-all2": "^9.0.2",
"oxlint": "^1.71.0",
"postcss-custom-media": "^12.0.1",
"typescript": "~6.0.2",
"vite": "^8.1.1",
"vitest": "^4.1.10"
}
}
Loading
Loading