product-runner 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -0
- package/common/agents/README.md +68 -0
- package/common/agents/agente-conceituacao.md +141 -0
- package/common/agents/agente-documentacao-funcional.md +107 -0
- package/common/agents/agente-gerador-spec.md +106 -0
- package/common/agents/agente-kickoff.md +121 -0
- package/common/agents/agente-prod-runner.md +107 -0
- package/common/agents/agente-review-code.md +97 -0
- package/common/agents/agente-review-llm.md +94 -0
- package/common/agents/agente-review-product.md +98 -0
- package/common/agents/agente-user-review.md +99 -0
- package/common/agents/protocolo-de-gates.md +51 -0
- package/common/claude-md.template.md +210 -0
- package/common/design-principles.md +229 -0
- package/common/lessons-learned.md +440 -0
- package/common/pipeline.md +143 -0
- package/common/spec-guide.md +327 -0
- package/common/specs/_open-issues.md +46 -0
- package/common/specs/_overview.md +75 -0
- package/dist/cli.js +187 -0
- package/dist/migrations.js +147 -0
- package/dist/scaffold.js +276 -0
- package/dist/update.js +400 -0
- package/migrations/0.3.0.md +54 -0
- package/migrations/0.4.0.md +76 -0
- package/migrations/0.5.0.md +55 -0
- package/migrations/README.md +68 -0
- package/package.json +41 -0
- package/profile-cli/README.md +54 -0
- package/profile-cli/claude-md.extension.md +102 -0
- package/profile-cli/code-patterns.md +363 -0
- package/profile-ssr/DESIGN-SYSTEM.md +795 -0
- package/profile-ssr/README.md +51 -0
- package/profile-ssr/api-patterns.md +70 -0
- package/profile-ssr/claude-md.extension.md +113 -0
- package/profile-ssr/code-patterns.md +175 -0
- package/profile-ssr/ui-patterns.md +97 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Perfil: Web SSR (Next.js Pages Router)
|
|
2
|
+
|
|
3
|
+
Use este perfil em projetos que:
|
|
4
|
+
|
|
5
|
+
- Rodam como **servidor web** com SSR/SSG (Next.js, Remix, etc.).
|
|
6
|
+
- Têm **API routes** + **UI React**.
|
|
7
|
+
- Precisam de **autenticação**, **formulários**, **componentes interativos**.
|
|
8
|
+
- Persistem em DB via ORM (Prisma + Postgres como default), ou em
|
|
9
|
+
arquivos como acoplamento com outros sistemas.
|
|
10
|
+
|
|
11
|
+
## Conteúdo
|
|
12
|
+
|
|
13
|
+
| Arquivo | Pra quê |
|
|
14
|
+
|---|---|
|
|
15
|
+
| [code-patterns](./code-patterns.md) | Estrutura de pastas, schemas Zod, services, repository, mapper toOutput |
|
|
16
|
+
| [api-patterns](./api-patterns.md) | API routes Next.js, validação, respostas, erros |
|
|
17
|
+
| [ui-patterns](./ui-patterns.md) | Componentes React, formulários, Tailwind, **responsividade mobile** |
|
|
18
|
+
| [claude-md.extension](../CLAUDE.md) | Seções específicas pra SSR (Next.js, deploy, comandos) |
|
|
19
|
+
|
|
20
|
+
## Como combinar com `common/`
|
|
21
|
+
|
|
22
|
+
Use o CLI — ele copia `common/` + este perfil pra `docs/` e gera o
|
|
23
|
+
`CLAUDE.md` raiz (mescla template + extension, substitui `{...}`):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx product-runner --name meu-projeto --profile ssr --port 3000 --dir .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Equivalente manual, se preferir sem npm:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cp common/*.md meu-projeto/docs/
|
|
33
|
+
cp profile-ssr/*.md meu-projeto/docs/
|
|
34
|
+
cat common/claude-md.template.md profile-ssr/claude-md.extension.md \
|
|
35
|
+
> meu-projeto/CLAUDE.md
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Origem
|
|
39
|
+
|
|
40
|
+
Conteúdo extraído de:
|
|
41
|
+
- **DocManager** (`retro-20260419`) — base SSR Next.js + Prisma + shadcn/ui
|
|
42
|
+
- **tradeBot** (`tradebot-202605`) — atualizações importadas:
|
|
43
|
+
- Princípios LLM-first (em [design-principles](./design-principles.md))
|
|
44
|
+
- Critérios meta M1/M2/M3 (em [spec-guide](./spec-guide.md))
|
|
45
|
+
- [_open-issues](../specs/_open-issues.md) (seed em `common/specs/`)
|
|
46
|
+
- Adições de responsividade mobile e integração com skill
|
|
47
|
+
`ui-design-system` (em [ui-patterns](./ui-patterns.md) deste perfil)
|
|
48
|
+
|
|
49
|
+
## Anti-pattern: usar este perfil pra projeto CLI
|
|
50
|
+
|
|
51
|
+
Stack assume request/response + UI. CLI sem isso. Use `profile-cli/`.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# API route patterns
|
|
2
|
+
|
|
3
|
+
API routes são cascas finas — sem lógica de negócio.
|
|
4
|
+
|
|
5
|
+
## Estrutura padrão
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
export default async function handler(req, res) {
|
|
9
|
+
try {
|
|
10
|
+
switch (req.method) {
|
|
11
|
+
case "GET": return handleGet(req, res);
|
|
12
|
+
case "POST": return handlePost(req, res);
|
|
13
|
+
default:
|
|
14
|
+
res.setHeader("Allow", ["GET", "POST"]);
|
|
15
|
+
return res.status(405).json({ error: "Método não permitido" });
|
|
16
|
+
}
|
|
17
|
+
} catch (err) {
|
|
18
|
+
return handleError(err, res);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function handlePost(req, res) {
|
|
23
|
+
const parsed = Schema.safeParse(req.body);
|
|
24
|
+
if (!parsed.success) {
|
|
25
|
+
return res.status(400).json({
|
|
26
|
+
error: "Dados inválidos",
|
|
27
|
+
details: parsed.error.flatten().fieldErrors,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const result = await serviceFunction(parsed.data);
|
|
31
|
+
return res.status(201).json(result);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Tratamento de erros centralizado
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
export function handleError(err: unknown, res) {
|
|
39
|
+
if (err instanceof DomainError) {
|
|
40
|
+
return res.status(err.statusCode).json({
|
|
41
|
+
error: err.message, code: err.code,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
console.error("Erro inesperado:", err);
|
|
45
|
+
return res.status(500).json({ error: "Erro interno" });
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Validação
|
|
50
|
+
|
|
51
|
+
Sempre `safeParse`, nunca `parse` em API routes.
|
|
52
|
+
Query params com `z.coerce` pra converter strings.
|
|
53
|
+
|
|
54
|
+
## Respostas padronizadas
|
|
55
|
+
|
|
56
|
+
| Situação | Status | Body |
|
|
57
|
+
|----------------------|--------|-------------------------|
|
|
58
|
+
| Sucesso (read) | 200 | `{ ...data }` |
|
|
59
|
+
| Sucesso (create) | 201 | `{ ...data }` |
|
|
60
|
+
| Sucesso (no content) | 204 | vazio |
|
|
61
|
+
| Aceito (async) | 202 | `{ ...queue info }` |
|
|
62
|
+
| Validação falhou | 400 | `{ error, details }` |
|
|
63
|
+
| Não encontrado | 404 | `{ error, code }` |
|
|
64
|
+
| Conflito | 409 | `{ error, code }` |
|
|
65
|
+
| Método não permitido | 405 | `{ error }` + Allow |
|
|
66
|
+
| Erro interno | 500 | `{ error }` genérico |
|
|
67
|
+
|
|
68
|
+
## Upload de arquivos
|
|
69
|
+
|
|
70
|
+
Desabilitar bodyParser, usar lib de parsing multipart.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Extensão do CLAUDE.md — Perfil SSR.
|
|
3
|
+
Este arquivo NÃO é concatenado: cada bloco abaixo declara, via diretiva
|
|
4
|
+
`prod-runner-merge`, como dobra numa seção do template-base (common/claude-md.template.md).
|
|
5
|
+
Modos: replace (troca a seção), append (acrescenta ao fim da seção),
|
|
6
|
+
after (insere logo após a seção). Edite o conteúdo, não as diretivas.
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
<!-- prod-runner-merge: append section="Stack" -->
|
|
10
|
+
|
|
11
|
+
- **Framework:** Next.js (Pages Router)
|
|
12
|
+
- **ORM:** Prisma + PostgreSQL (ou outro adapter quando configurado)
|
|
13
|
+
- **UI:** Tailwind CSS + shadcn/ui
|
|
14
|
+
- **Forms:** React Hook Form + Zod resolver
|
|
15
|
+
- **Charts (se aplicável):** Recharts (default) ou TradingView lightweight
|
|
16
|
+
charts (séries financeiras)
|
|
17
|
+
- **Background jobs (se aplicável):** BullMQ + Redis
|
|
18
|
+
- **Deploy:** Docker + VPS (Vercel não cobre storage persistente +
|
|
19
|
+
background jobs)
|
|
20
|
+
|
|
21
|
+
<!-- prod-runner-merge: replace section="Princípio central" -->
|
|
22
|
+
|
|
23
|
+
### Princípio central
|
|
24
|
+
|
|
25
|
+
Lógica de domínio desacoplada do framework. Services são TypeScript
|
|
26
|
+
puro. API routes são cascas finas que validam e delegam pros services.
|
|
27
|
+
Componentes UI consomem outputs tipados dos services.
|
|
28
|
+
|
|
29
|
+
<!-- prod-runner-merge: replace section="Estrutura de pastas" -->
|
|
30
|
+
|
|
31
|
+
### Estrutura de pastas
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
{project}/
|
|
35
|
+
├── CLAUDE.md
|
|
36
|
+
├── prisma/ ← schema.prisma + migrations
|
|
37
|
+
├── src/
|
|
38
|
+
│ ├── pages/
|
|
39
|
+
│ │ ├── api/ ← API routes (cascas finas)
|
|
40
|
+
│ │ └── {páginas SSR}
|
|
41
|
+
│ ├── services/
|
|
42
|
+
│ │ ├── {dominio}/
|
|
43
|
+
│ │ │ ├── schema.ts ← Zod entity + derivações
|
|
44
|
+
│ │ │ └── repository.ts ← Prisma queries + toOutput
|
|
45
|
+
│ │ └── integrations/ ← clients de APIs externas
|
|
46
|
+
│ ├── components/
|
|
47
|
+
│ │ └── ui/ ← shadcn/ui (gerados)
|
|
48
|
+
│ └── lib/
|
|
49
|
+
│ ├── prisma.ts
|
|
50
|
+
│ ├── errors.ts
|
|
51
|
+
│ └── {outros utilitários}
|
|
52
|
+
├── scripts/
|
|
53
|
+
├── specs/
|
|
54
|
+
├── docs/
|
|
55
|
+
└── docker-compose.yml ← infra (Postgres, Redis se aplicável)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
<!-- prod-runner-merge: append section="Convenções de código" -->
|
|
59
|
+
|
|
60
|
+
### API Routes (SSR)
|
|
61
|
+
|
|
62
|
+
- Validar com `Schema.safeParse()`.
|
|
63
|
+
- Chamar o service e retornar o resultado.
|
|
64
|
+
- Sem lógica de negócio.
|
|
65
|
+
- Detalhe em [api-patterns](./docs/api-patterns.md).
|
|
66
|
+
|
|
67
|
+
### UI (SSR)
|
|
68
|
+
|
|
69
|
+
- Componentes shadcn/ui como base (gerados via CLI).
|
|
70
|
+
- Tailwind utility classes — sem CSS modules.
|
|
71
|
+
- Formulários com React Hook Form + Zod resolver.
|
|
72
|
+
- **Responsividade mobile validada em todo componente novo** (M4 — ver
|
|
73
|
+
[ui-patterns](./docs/ui-patterns.md)).
|
|
74
|
+
- Considerar skill `ui-design-system` em revisões de componente.
|
|
75
|
+
|
|
76
|
+
### Validação visual no browser (SSR)
|
|
77
|
+
|
|
78
|
+
Insubstituível — `tsc --noEmit` pega erros de tipo mas não de
|
|
79
|
+
comportamento. Smoke check humano em mobile (375x667) e desktop
|
|
80
|
+
(1280x720) faz parte do M4.
|
|
81
|
+
|
|
82
|
+
<!-- prod-runner-merge: replace section="Comandos úteis" -->
|
|
83
|
+
|
|
84
|
+
## Comandos úteis
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
docker compose up -d # infra (Postgres, Redis)
|
|
88
|
+
npm install # deps
|
|
89
|
+
npx prisma migrate dev # migrations
|
|
90
|
+
npx prisma generate # client
|
|
91
|
+
npm run dev # dev server (port {PORT})
|
|
92
|
+
npm test # testes
|
|
93
|
+
npx tsc --noEmit # typecheck
|
|
94
|
+
npx shadcn@latest add <comp> # adicionar componente
|
|
95
|
+
npm run format # formatar tudo
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Configurar port fixo no `package.json` (`"dev": "next dev -p {PORT}"`) pra
|
|
99
|
+
evitar mudança entre sessões. Code deve reportar o port real no output ao
|
|
100
|
+
iniciar o dev server.
|
|
101
|
+
|
|
102
|
+
<!-- prod-runner-merge: replace section="Configuração" -->
|
|
103
|
+
|
|
104
|
+
## Configuração
|
|
105
|
+
|
|
106
|
+
| Arquivo | Conteúdo | Comitado? |
|
|
107
|
+
| ---------------------- | --------------------- | --------- |
|
|
108
|
+
| `.env` | DATABASE_URL, secrets | ❌ NUNCA |
|
|
109
|
+
| `.env.example` | Mesmas chaves sem valor | ✅ |
|
|
110
|
+
| `prisma/schema.prisma` | Schema do DB | ✅ |
|
|
111
|
+
| `next.config.js` | Config Next | ✅ |
|
|
112
|
+
| `tailwind.config.ts` | Config Tailwind | ✅ |
|
|
113
|
+
| `components.json` | Config shadcn/ui | ✅ |
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Data patterns
|
|
2
|
+
|
|
3
|
+
Padrões para definição de schemas, derivação de tipos,
|
|
4
|
+
e transformação de dados entre camadas.
|
|
5
|
+
|
|
6
|
+
## Princípio
|
|
7
|
+
|
|
8
|
+
Cada domínio tem UM arquivo `schema.ts` com:
|
|
9
|
+
1. Entity base (espelha o ORM model)
|
|
10
|
+
2. Refs de relações (formas simplificadas de entities relacionadas)
|
|
11
|
+
3. Inputs (derivados da entity por pick/extend)
|
|
12
|
+
4. Outputs (derivados da entity por omit/extend)
|
|
13
|
+
|
|
14
|
+
Nunca escrever interfaces ou types manualmente.
|
|
15
|
+
Sempre derivar do schema de validação e inferir tipos.
|
|
16
|
+
|
|
17
|
+
## Entity base
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// services/{domínio}/schema.ts
|
|
21
|
+
|
|
22
|
+
export const {Entity}Entity = z.object({
|
|
23
|
+
id: z.string().uuid(),
|
|
24
|
+
// ... espelha todos os campos do ORM model
|
|
25
|
+
createdAt: z.string().datetime(),
|
|
26
|
+
updatedAt: z.string().datetime(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export type {Entity}Entity = z.infer<typeof {Entity}Entity>;
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Regras:
|
|
33
|
+
- Nomes de campos idênticos ao ORM model.
|
|
34
|
+
- DateTime → `z.string().datetime()`.
|
|
35
|
+
- Nullable → `.nullable()`.
|
|
36
|
+
- Enums como `z.enum([...])`.
|
|
37
|
+
|
|
38
|
+
## Refs de relações
|
|
39
|
+
|
|
40
|
+
Formas simplificadas pra usar como campos em outputs:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
export const {Related}Ref = z.object({
|
|
44
|
+
id: z.string().uuid(),
|
|
45
|
+
name: z.string(),
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Derivação de inputs
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
export const Create{Entity}Input = {Entity}Entity
|
|
53
|
+
.pick({ /* campos editáveis */ })
|
|
54
|
+
.partial()
|
|
55
|
+
.extend({ /* campos adicionais */ });
|
|
56
|
+
|
|
57
|
+
export type Create{Entity}Input = z.infer<typeof Create{Entity}Input>;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Regras:
|
|
61
|
+
- Usar `.shape.fieldName` pra referenciar tipos da entity.
|
|
62
|
+
- Usar `.unwrap()` pra remover nullable quando obrigatório no input.
|
|
63
|
+
|
|
64
|
+
## Derivação de outputs
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
export const {Entity}Output = {Entity}Entity
|
|
68
|
+
.omit({ /* campos internos */ })
|
|
69
|
+
.extend({
|
|
70
|
+
// relações resolvidas
|
|
71
|
+
{related}: {Related}Ref.nullable(),
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Regras:
|
|
76
|
+
- Omitir campos internos (paths, IDs de FK).
|
|
77
|
+
- Substituir FK IDs por refs resolvidas.
|
|
78
|
+
|
|
79
|
+
## Mapper toOutput
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
function toOutput(doc: any): {Entity}OutputType {
|
|
83
|
+
return {
|
|
84
|
+
// mapear campos, resolver relações
|
|
85
|
+
createdAt: doc.createdAt.toISOString(),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Validação de output em dev
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
if (process.env.NODE_ENV === "development") {
|
|
94
|
+
return {Entity}Output.parse(data);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
# Service patterns
|
|
101
|
+
|
|
102
|
+
Padrões para services, repositories, erros e integrações.
|
|
103
|
+
|
|
104
|
+
## Estrutura de um domínio
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
services/{domínio}/
|
|
108
|
+
├── schema.ts ← entity + inputs + outputs
|
|
109
|
+
├── repository.ts ← CRUD via ORM, mapper
|
|
110
|
+
├── {lógica}.ts ← lógica de negócio específica
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Regras:
|
|
114
|
+
- `repository.ts` faz queries e retorna outputs tipados.
|
|
115
|
+
- Outros arquivos contêm lógica de negócio.
|
|
116
|
+
- Services podem importar outros services.
|
|
117
|
+
- Services NUNCA importam tipos do framework.
|
|
118
|
+
|
|
119
|
+
## Repository
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
const defaultInclude = { /* relações padrão */ } as const;
|
|
123
|
+
|
|
124
|
+
export async function create{Entity}(
|
|
125
|
+
input: Create{Entity}Input
|
|
126
|
+
): Promise<{Entity}Output> {
|
|
127
|
+
const record = await orm.{entity}.create({
|
|
128
|
+
data: { /* ... */ },
|
|
129
|
+
include: defaultInclude,
|
|
130
|
+
});
|
|
131
|
+
return toOutput(record);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Erros de domínio
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
export class DomainError extends Error {
|
|
139
|
+
constructor(message: string, public code: string, public statusCode: number = 400) {
|
|
140
|
+
super(message);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export class NotFoundError extends DomainError { /* 404 */ }
|
|
145
|
+
export class ConflictError extends DomainError { /* 409 */ }
|
|
146
|
+
export class ValidationError extends DomainError { /* 400 */ }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Regras de integridade
|
|
150
|
+
|
|
151
|
+
Validações que o ORM não cobre ficam no service:
|
|
152
|
+
- XOR constraints (campo A OU B, nunca ambos)
|
|
153
|
+
- Prevenção de ciclos (auto-referência hierárquica)
|
|
154
|
+
- Recálculo de campos derivados
|
|
155
|
+
|
|
156
|
+
## Integrações externas
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
services/integrations/
|
|
160
|
+
├── {provider}.ts ← client isolado, tipos simples
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- Retornam tipos simples, não outputs de domínio.
|
|
164
|
+
- O service de domínio orquestra e converte resultados.
|
|
165
|
+
- Credenciais via env vars, nunca hardcoded.
|
|
166
|
+
|
|
167
|
+
## Transações
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
await orm.$transaction(async (tx) => {
|
|
171
|
+
// operações atômicas
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Usar sempre que uma operação envolve múltiplas writes.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# UI patterns
|
|
2
|
+
|
|
3
|
+
Padrões para componentes, páginas, formulários e estilização.
|
|
4
|
+
|
|
5
|
+
## Estrutura
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/components/
|
|
9
|
+
├── ui/ ← componentes da lib (shadcn, etc) — gerados
|
|
10
|
+
├── {componente}.tsx ← componentes do projeto
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Estilização
|
|
14
|
+
|
|
15
|
+
Tailwind utility classes para tudo. Sem CSS modules,
|
|
16
|
+
sem styled-components, sem arquivos CSS customizados.
|
|
17
|
+
|
|
18
|
+
## Formulários
|
|
19
|
+
|
|
20
|
+
Usar React Hook Form + Zod resolver, reutilizando
|
|
21
|
+
os schemas do service:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
const form = useForm<InputType>({
|
|
25
|
+
resolver: zodResolver(InputSchema),
|
|
26
|
+
defaultValues: { /* ... */ },
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Regras:
|
|
31
|
+
- NUNCA duplicar validação — reutilizar o schema do service.
|
|
32
|
+
- Erros inline via FormMessage do componente lib.
|
|
33
|
+
- Erros de API via toast.
|
|
34
|
+
|
|
35
|
+
## Páginas
|
|
36
|
+
|
|
37
|
+
- Tipar dados com os types de output do service.
|
|
38
|
+
- Loading states com skeleton.
|
|
39
|
+
- Empty states com mensagem + ação sugerida.
|
|
40
|
+
- Layout consistente.
|
|
41
|
+
|
|
42
|
+
## Componentes da lib (ex: shadcn/ui)
|
|
43
|
+
|
|
44
|
+
- Sempre usar CLI pra instalar: `npx shadcn@latest add <comp>`.
|
|
45
|
+
- Nunca criar componentes da lib manualmente.
|
|
46
|
+
- Editar depois de gerado é permitido.
|
|
47
|
+
|
|
48
|
+
## Responsividade mobile
|
|
49
|
+
|
|
50
|
+
Toda página/componente novo é validado em **dois viewports** antes
|
|
51
|
+
de declarar pronto:
|
|
52
|
+
|
|
53
|
+
| Viewport | Resolução | Comportamento esperado |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| Mobile | 375 × 667 (iPhone SE) | Sem scroll horizontal; touch targets ≥ 44px; menus colapsam |
|
|
56
|
+
| Desktop | 1280 × 720 | Layout completo; hover states funcionam |
|
|
57
|
+
|
|
58
|
+
### Regras de Tailwind
|
|
59
|
+
|
|
60
|
+
- Mobile-first: classes sem prefix são pra mobile; `md:`, `lg:`
|
|
61
|
+
são extensões pra telas maiores.
|
|
62
|
+
- Evitar `w-screen` puro (causa scroll horizontal); usar `w-full`.
|
|
63
|
+
- Espaçamentos generosos em mobile (`p-4`, `gap-4`) que diminuem
|
|
64
|
+
em desktop (`md:p-6`, `lg:p-8`).
|
|
65
|
+
- Tipografia: `text-base` mobile, `md:text-lg` desktop quando
|
|
66
|
+
precisar de hierarquia visual.
|
|
67
|
+
|
|
68
|
+
### Anti-patterns
|
|
69
|
+
|
|
70
|
+
- ❌ `min-w-[800px]` — causa scroll horizontal em mobile.
|
|
71
|
+
- ❌ `fixed` sem prever que mobile pode cobrir conteúdo.
|
|
72
|
+
- ❌ Testar só em desktop e descobrir o mobile depois.
|
|
73
|
+
|
|
74
|
+
## Skill de UI/UX (suporte externo)
|
|
75
|
+
|
|
76
|
+
Considerar invocar a skill `ui-design-system` (Cowork) ao revisar
|
|
77
|
+
componentes novos pra obter:
|
|
78
|
+
- Análise de hierarquia visual.
|
|
79
|
+
- Identificação de inconsistências com design system existente.
|
|
80
|
+
- Indicação de pontos de responsividade frágeis.
|
|
81
|
+
- Sugestões de spec-anchored (componentes para o Code aplicar
|
|
82
|
+
consistentemente).
|
|
83
|
+
|
|
84
|
+
## Critério meta M4 — UI responsiva
|
|
85
|
+
|
|
86
|
+
Toda spec que cria/altera componente UI inclui no checklist:
|
|
87
|
+
|
|
88
|
+
- [ ] Componente funciona em viewport `375x667` (mobile) sem scroll
|
|
89
|
+
horizontal.
|
|
90
|
+
- [ ] Touch targets ≥ 44px em mobile (botões, links clicáveis).
|
|
91
|
+
- [ ] Componente funciona em viewport `1280x720` (desktop) com
|
|
92
|
+
hover states ativos.
|
|
93
|
+
- [ ] Code rodou screenshot ou descreveu o layout nos dois viewports
|
|
94
|
+
no report final.
|
|
95
|
+
|
|
96
|
+
M4 é critério meta da própria spec — referenciar como "aplicam-se
|
|
97
|
+
M1, M2, M3, M4" em specs de UI.
|