530 lines
11 KiB
Markdown
530 lines
11 KiB
Markdown
|
|
# Guia de Desenvolvimento - DRE Gerencial
|
||
|
|
|
||
|
|
## Configuração do Ambiente
|
||
|
|
|
||
|
|
### 1. **Pré-requisitos**
|
||
|
|
- Node.js 18+
|
||
|
|
- PostgreSQL 13+
|
||
|
|
- npm ou yarn
|
||
|
|
- Git
|
||
|
|
|
||
|
|
### 2. **Instalação**
|
||
|
|
```bash
|
||
|
|
# Clone o repositório
|
||
|
|
git clone <repository-url>
|
||
|
|
cd dre-modelo
|
||
|
|
|
||
|
|
# Instale as dependências
|
||
|
|
npm install
|
||
|
|
|
||
|
|
# Configure as variáveis de ambiente
|
||
|
|
cp .env.example .env.local
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. **Variáveis de Ambiente**
|
||
|
|
```env
|
||
|
|
# Database
|
||
|
|
POSTGRES_DB=dre_gerencial
|
||
|
|
POSTGRES_HOST=localhost
|
||
|
|
POSTGRES_PORT=5432
|
||
|
|
POSTGRES_USER=postgres
|
||
|
|
POSTGRES_PASSWORD=sua_senha
|
||
|
|
|
||
|
|
# Next.js
|
||
|
|
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. **Configuração do Banco**
|
||
|
|
```bash
|
||
|
|
# Criar banco de dados
|
||
|
|
createdb dre_gerencial
|
||
|
|
|
||
|
|
# Executar migrações (se houver)
|
||
|
|
npx drizzle-kit migrate
|
||
|
|
```
|
||
|
|
|
||
|
|
## Scripts Disponíveis
|
||
|
|
|
||
|
|
### 1. **Desenvolvimento**
|
||
|
|
```bash
|
||
|
|
# Iniciar servidor de desenvolvimento
|
||
|
|
npm run dev
|
||
|
|
|
||
|
|
# Build para produção
|
||
|
|
npm run build
|
||
|
|
|
||
|
|
# Iniciar servidor de produção
|
||
|
|
npm run start
|
||
|
|
|
||
|
|
# Linting
|
||
|
|
npm run lint
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. **Banco de Dados**
|
||
|
|
```bash
|
||
|
|
# Gerar migração
|
||
|
|
npx drizzle-kit generate
|
||
|
|
|
||
|
|
# Aplicar migração
|
||
|
|
npx drizzle-kit migrate
|
||
|
|
|
||
|
|
# Visualizar schema
|
||
|
|
npx drizzle-kit studio
|
||
|
|
```
|
||
|
|
|
||
|
|
## Estrutura do Projeto
|
||
|
|
|
||
|
|
```
|
||
|
|
src/
|
||
|
|
├── app/ # Next.js App Router
|
||
|
|
│ ├── api/ # API Routes
|
||
|
|
│ │ ├── analitico/ # API analítica
|
||
|
|
│ │ └── dre/ # API DRE
|
||
|
|
│ ├── DRE/ # Páginas DRE
|
||
|
|
│ │ ├── analitico.tsx # Componente analítico
|
||
|
|
│ │ ├── page.tsx # Página principal
|
||
|
|
│ │ └── teste.tsx # Componente principal
|
||
|
|
│ ├── globals.css # Estilos globais
|
||
|
|
│ └── layout.tsx # Layout raiz
|
||
|
|
├── components/ # Componentes reutilizáveis
|
||
|
|
│ └── ui/ # Componentes UI base
|
||
|
|
├── db/ # Configuração do banco
|
||
|
|
│ ├── index.ts # Conexão Drizzle
|
||
|
|
│ └── schema.ts # Schema do banco
|
||
|
|
└── lib/ # Utilitários
|
||
|
|
└── utils.ts # Funções utilitárias
|
||
|
|
```
|
||
|
|
|
||
|
|
## Padrões de Código
|
||
|
|
|
||
|
|
### 1. **TypeScript**
|
||
|
|
- Sempre usar tipos explícitos
|
||
|
|
- Interfaces para props de componentes
|
||
|
|
- Tipos específicos para dados de API
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// ✅ Bom
|
||
|
|
interface AnaliticoItem {
|
||
|
|
id: number;
|
||
|
|
valor: number;
|
||
|
|
data_competencia: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ❌ Evitar
|
||
|
|
const data: any = await response.json();
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. **React Hooks**
|
||
|
|
- Usar `useCallback` para funções passadas como props
|
||
|
|
- Usar `useMemo` para cálculos pesados
|
||
|
|
- Evitar dependências desnecessárias em `useEffect`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// ✅ Bom
|
||
|
|
const fetchData = useCallback(async () => {
|
||
|
|
// lógica de fetch
|
||
|
|
}, [filtros]);
|
||
|
|
|
||
|
|
// ❌ Evitar
|
||
|
|
const fetchData = async () => {
|
||
|
|
// lógica de fetch
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. **Styling**
|
||
|
|
- Usar Tailwind CSS para styling
|
||
|
|
- Classes utilitárias para responsividade
|
||
|
|
- Variantes com class-variance-authority
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// ✅ Bom
|
||
|
|
const buttonVariants = cva(
|
||
|
|
'inline-flex items-center justify-center',
|
||
|
|
{
|
||
|
|
variants: {
|
||
|
|
variant: {
|
||
|
|
default: 'bg-primary text-primary-foreground',
|
||
|
|
outline: 'border border-input bg-background',
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
// ❌ Evitar
|
||
|
|
const styles = {
|
||
|
|
button: 'bg-blue-500 text-white px-4 py-2',
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
## Desenvolvimento de Novas Funcionalidades
|
||
|
|
|
||
|
|
### 1. **Adicionando Nova API**
|
||
|
|
|
||
|
|
#### Criar arquivo de rota
|
||
|
|
```typescript
|
||
|
|
// src/app/api/nova-funcionalidade/route.ts
|
||
|
|
import { NextRequest, NextResponse } from 'next/server';
|
||
|
|
import db from '@/db';
|
||
|
|
|
||
|
|
export async function GET(request: NextRequest) {
|
||
|
|
try {
|
||
|
|
// Lógica da API
|
||
|
|
const data = await db.execute(sql`SELECT * FROM tabela`);
|
||
|
|
|
||
|
|
return NextResponse.json(data.rows);
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro:', error);
|
||
|
|
return NextResponse.json(
|
||
|
|
{ error: 'Erro interno' },
|
||
|
|
{ status: 500 }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Adicionar tipos
|
||
|
|
```typescript
|
||
|
|
// src/types/nova-funcionalidade.ts
|
||
|
|
export interface NovaFuncionalidadeItem {
|
||
|
|
id: number;
|
||
|
|
nome: string;
|
||
|
|
valor: number;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. **Adicionando Novo Componente**
|
||
|
|
|
||
|
|
#### Estrutura do componente
|
||
|
|
```typescript
|
||
|
|
// src/components/NovaFuncionalidade.tsx
|
||
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState, useEffect } from 'react';
|
||
|
|
import { Button } from '@/components/ui/button';
|
||
|
|
|
||
|
|
interface NovaFuncionalidadeProps {
|
||
|
|
filtros: {
|
||
|
|
dataInicio: string;
|
||
|
|
dataFim: string;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function NovaFuncionalidade({ filtros }: NovaFuncionalidadeProps) {
|
||
|
|
const [data, setData] = useState([]);
|
||
|
|
const [loading, setLoading] = useState(false);
|
||
|
|
|
||
|
|
const fetchData = async () => {
|
||
|
|
setLoading(true);
|
||
|
|
try {
|
||
|
|
const response = await fetch(`/api/nova-funcionalidade?${new URLSearchParams(filtros)}`);
|
||
|
|
const result = await response.json();
|
||
|
|
setData(result);
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Erro:', error);
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
fetchData();
|
||
|
|
}, [filtros]);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="w-full">
|
||
|
|
<h2 className="text-lg font-bold mb-4">Nova Funcionalidade</h2>
|
||
|
|
|
||
|
|
{loading ? (
|
||
|
|
<div>Carregando...</div>
|
||
|
|
) : (
|
||
|
|
<div>
|
||
|
|
{/* Renderizar dados */}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. **Adicionando Nova Página**
|
||
|
|
|
||
|
|
#### Criar página
|
||
|
|
```typescript
|
||
|
|
// src/app/nova-pagina/page.tsx
|
||
|
|
import NovaFuncionalidade from '@/components/NovaFuncionalidade';
|
||
|
|
|
||
|
|
export default function NovaPagina() {
|
||
|
|
return (
|
||
|
|
<div className="w-full min-h-screen p-4">
|
||
|
|
<NovaFuncionalidade filtros={{ dataInicio: '', dataFim: '' }} />
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Debugging
|
||
|
|
|
||
|
|
### 1. **Logs de Desenvolvimento**
|
||
|
|
```typescript
|
||
|
|
// Usar console.log para debugging
|
||
|
|
console.log('Dados recebidos:', data);
|
||
|
|
|
||
|
|
// Logs estruturados
|
||
|
|
console.log({
|
||
|
|
timestamp: new Date().toISOString(),
|
||
|
|
component: 'AnaliticoComponent',
|
||
|
|
action: 'fetchData',
|
||
|
|
data: data.length
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. **React Developer Tools**
|
||
|
|
- Instalar extensão do Chrome/Firefox
|
||
|
|
- Inspecionar estado dos componentes
|
||
|
|
- Profiler para performance
|
||
|
|
|
||
|
|
### 3. **Network Tab**
|
||
|
|
- Verificar requisições de API
|
||
|
|
- Analisar tempo de resposta
|
||
|
|
- Verificar payloads
|
||
|
|
|
||
|
|
## Testes
|
||
|
|
|
||
|
|
### 1. **Configuração de Testes**
|
||
|
|
```bash
|
||
|
|
# Instalar dependências de teste
|
||
|
|
npm install --save-dev @testing-library/react @testing-library/jest-dom jest jest-environment-jsdom
|
||
|
|
|
||
|
|
# Configurar Jest
|
||
|
|
# jest.config.js
|
||
|
|
const nextJest = require('next/jest');
|
||
|
|
|
||
|
|
const createJestConfig = nextJest({
|
||
|
|
dir: './',
|
||
|
|
});
|
||
|
|
|
||
|
|
const customJestConfig = {
|
||
|
|
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
||
|
|
moduleNameMapping: {
|
||
|
|
'^@/(.*)$': '<rootDir>/src/$1',
|
||
|
|
},
|
||
|
|
testEnvironment: 'jest-environment-jsdom',
|
||
|
|
};
|
||
|
|
|
||
|
|
module.exports = createJestConfig(customJestConfig);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. **Testes Unitários**
|
||
|
|
```typescript
|
||
|
|
// __tests__/components/Analitico.test.tsx
|
||
|
|
import { render, screen } from '@testing-library/react';
|
||
|
|
import AnaliticoComponent from '@/app/DRE/analitico';
|
||
|
|
|
||
|
|
describe('AnaliticoComponent', () => {
|
||
|
|
it('renders without crashing', () => {
|
||
|
|
const filtros = {
|
||
|
|
dataInicio: '2024-01',
|
||
|
|
dataFim: '2024-12',
|
||
|
|
};
|
||
|
|
|
||
|
|
render(<AnaliticoComponent filtros={filtros} />);
|
||
|
|
expect(screen.getByText('Análise Analítica')).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. **Testes de API**
|
||
|
|
```typescript
|
||
|
|
// __tests__/api/analitico.test.ts
|
||
|
|
import { GET } from '@/app/api/analitico/route';
|
||
|
|
|
||
|
|
describe('/api/analitico', () => {
|
||
|
|
it('returns data for valid parameters', async () => {
|
||
|
|
const request = new Request('http://localhost:3000/api/analitico?dataInicio=2024-01&dataFim=2024-12');
|
||
|
|
const response = await GET(request);
|
||
|
|
const data = await response.json();
|
||
|
|
|
||
|
|
expect(response.status).toBe(200);
|
||
|
|
expect(Array.isArray(data)).toBe(true);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
## Performance
|
||
|
|
|
||
|
|
### 1. **Otimizações de Bundle**
|
||
|
|
```typescript
|
||
|
|
// Lazy loading de componentes
|
||
|
|
const AnaliticoComponent = lazy(() => import('./analitico'));
|
||
|
|
|
||
|
|
// Dynamic imports
|
||
|
|
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
|
||
|
|
loading: () => <div>Carregando...</div>,
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. **Otimizações de Renderização**
|
||
|
|
```typescript
|
||
|
|
// Memoização de componentes
|
||
|
|
const MemoizedComponent = memo(({ data }) => {
|
||
|
|
return <div>{data.map(item => <Item key={item.id} data={item} />)}</div>;
|
||
|
|
});
|
||
|
|
|
||
|
|
// Memoização de cálculos
|
||
|
|
const expensiveValue = useMemo(() => {
|
||
|
|
return data.reduce((sum, item) => sum + item.valor, 0);
|
||
|
|
}, [data]);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. **Otimizações de API**
|
||
|
|
```typescript
|
||
|
|
// Cache de dados
|
||
|
|
const { data, isLoading } = useQuery({
|
||
|
|
queryKey: ['analitico', filtros],
|
||
|
|
queryFn: () => fetchAnaliticoData(filtros),
|
||
|
|
staleTime: 5 * 60 * 1000, // 5 minutos
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
## Deploy
|
||
|
|
|
||
|
|
### 1. **Build de Produção**
|
||
|
|
```bash
|
||
|
|
# Build otimizado
|
||
|
|
npm run build
|
||
|
|
|
||
|
|
# Verificar build
|
||
|
|
npm run start
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. **Variáveis de Ambiente de Produção**
|
||
|
|
```env
|
||
|
|
# Produção
|
||
|
|
POSTGRES_DB=dre_gerencial_prod
|
||
|
|
POSTGRES_HOST=prod-db-host
|
||
|
|
POSTGRES_PORT=5432
|
||
|
|
POSTGRES_USER=prod_user
|
||
|
|
POSTGRES_PASSWORD=prod_password
|
||
|
|
|
||
|
|
NEXT_PUBLIC_APP_URL=https://dre-gerencial.com
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. **Docker (Opcional)**
|
||
|
|
```dockerfile
|
||
|
|
# Dockerfile
|
||
|
|
FROM node:18-alpine
|
||
|
|
|
||
|
|
WORKDIR /app
|
||
|
|
COPY package*.json ./
|
||
|
|
RUN npm ci --only=production
|
||
|
|
|
||
|
|
COPY . .
|
||
|
|
RUN npm run build
|
||
|
|
|
||
|
|
EXPOSE 3000
|
||
|
|
CMD ["npm", "start"]
|
||
|
|
```
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### 1. **Problemas Comuns**
|
||
|
|
|
||
|
|
#### Erro de Conexão com Banco
|
||
|
|
```bash
|
||
|
|
# Verificar se PostgreSQL está rodando
|
||
|
|
pg_ctl status
|
||
|
|
|
||
|
|
# Verificar conexão
|
||
|
|
psql -h localhost -U postgres -d dre_gerencial
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Erro de Build
|
||
|
|
```bash
|
||
|
|
# Limpar cache
|
||
|
|
rm -rf .next node_modules
|
||
|
|
npm install
|
||
|
|
npm run build
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Erro de TypeScript
|
||
|
|
```bash
|
||
|
|
# Verificar tipos
|
||
|
|
npx tsc --noEmit
|
||
|
|
|
||
|
|
# Atualizar tipos
|
||
|
|
npm update @types/react @types/react-dom
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. **Logs de Erro**
|
||
|
|
```typescript
|
||
|
|
// Error boundary
|
||
|
|
class ErrorBoundary extends Component {
|
||
|
|
constructor(props) {
|
||
|
|
super(props);
|
||
|
|
this.state = { hasError: false };
|
||
|
|
}
|
||
|
|
|
||
|
|
static getDerivedStateFromError(error) {
|
||
|
|
return { hasError: true };
|
||
|
|
}
|
||
|
|
|
||
|
|
componentDidCatch(error, errorInfo) {
|
||
|
|
console.error('Error caught by boundary:', error, errorInfo);
|
||
|
|
}
|
||
|
|
|
||
|
|
render() {
|
||
|
|
if (this.state.hasError) {
|
||
|
|
return <div>Algo deu errado.</div>;
|
||
|
|
}
|
||
|
|
|
||
|
|
return this.props.children;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Contribuição
|
||
|
|
|
||
|
|
### 1. **Fluxo de Trabalho**
|
||
|
|
```bash
|
||
|
|
# Criar branch
|
||
|
|
git checkout -b feature/nova-funcionalidade
|
||
|
|
|
||
|
|
# Fazer commits
|
||
|
|
git add .
|
||
|
|
git commit -m "feat: adiciona nova funcionalidade"
|
||
|
|
|
||
|
|
# Push
|
||
|
|
git push origin feature/nova-funcionalidade
|
||
|
|
|
||
|
|
# Criar Pull Request
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. **Padrões de Commit**
|
||
|
|
```
|
||
|
|
feat: nova funcionalidade
|
||
|
|
fix: correção de bug
|
||
|
|
docs: atualização de documentação
|
||
|
|
style: formatação de código
|
||
|
|
refactor: refatoração de código
|
||
|
|
test: adição de testes
|
||
|
|
chore: tarefas de manutenção
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. **Code Review**
|
||
|
|
- Verificar tipos TypeScript
|
||
|
|
- Testar funcionalidades
|
||
|
|
- Validar performance
|
||
|
|
- Verificar acessibilidade
|
||
|
|
- Revisar documentação
|
||
|
|
|
||
|
|
## Próximos Passos
|
||
|
|
|
||
|
|
1. **Implementar CI/CD** com GitHub Actions
|
||
|
|
2. **Adicionar testes E2E** com Playwright
|
||
|
|
3. **Implementar monitoramento** com Sentry
|
||
|
|
4. **Adicionar Storybook** para componentes
|
||
|
|
5. **Implementar PWA** para mobile
|
||
|
|
6. **Adicionar internacionalização** (i18n)
|
||
|
|
7. **Implementar cache** com Redis
|
||
|
|
8. **Adicionar métricas** com Analytics
|