@horizon-framework/website-dev-docs 2.3.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.
@@ -0,0 +1,169 @@
1
+ # Arquitetura Geral — Horizon Platform
2
+
3
+ > Visao completa do projeto: 5 partes, 3 deploys Vercel, monorepo com pnpm.
4
+
5
+ ---
6
+
7
+ ## Estrutura do Projeto
8
+
9
+ ```
10
+ raiz/
11
+ ├── apps/
12
+ │ ├── web/ ← Frontend (Next.js 15, React 19, porta 3001)
13
+ │ ├── api/ ← Backend API (Next.js 14, Prisma, porta 5000)
14
+ │ └── automations/ ← Automacoes (Next.js 15, porta 8080)
15
+ ├── ai/ ← Cerebro do agente (docs NPM, config cliente)
16
+ ├── docs/ ← Docs especificas deste site/cliente
17
+ ├── dev/ ← Area de trabalho temporaria (screenshots, logs, rascunhos)
18
+ ├── CLAUDE.md ← Prompt master do agente
19
+ └── horizon.config.md ← Prontuario do site (versao, cliente, CRM, modulos)
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Os 3 Apps
25
+
26
+ ### WEB — Frontend (apps/web/)
27
+ - **Framework:** Next.js 15.5.7 + React 19.1.0
28
+ - **Porta dev:** 3001
29
+ - **Stack:** Tailwind 4 + shadcn/ui + Framer Motion + Zustand + React Query
30
+ - **Responsabilidade:** Todas as paginas do site, busca de imoveis, UI completa
31
+ - **Build:** `NODE_OPTIONS='--max-old-space-size=4096' next build`
32
+ - **Storybook:** porta 6006
33
+ - **Email preview:** via React Email
34
+
35
+ ### API — Backend (apps/api/)
36
+ - **Framework:** Next.js 14.2.32 + React 18.3.1
37
+ - **Porta dev:** 5000
38
+ - **Stack:** Prisma 6 + PostgreSQL (DigitalOcean) + PostGIS + Zod
39
+ - **Responsabilidade:** API REST, busca multipart, sync com CRM, schemas SSOT
40
+ - **Build output:** standalone
41
+ - **Vercel region:** gru1 (Sao Paulo)
42
+ - **Serverless max:** 30s
43
+
44
+ ### AUTOMATIONS — Integracoes (apps/automations/)
45
+ - **Framework:** Next.js 15.5.7 + React 19.0.0
46
+ - **Porta dev:** 8080
47
+ - **Stack:** Resend (emails) + React Email + Zod
48
+ - **Responsabilidade:** Receber leads, enviar emails, sincronizar com CRM
49
+ - **CORS:** Configura domínios permitidos
50
+
51
+ ---
52
+
53
+ ## Fluxo de Comunicacao
54
+
55
+ ```
56
+ Browser → WEB (3001) → API (5000) ← PostgreSQL + PostGIS
57
+
58
+ AUTOMATIONS (8080) → Resend (emails)
59
+ → CRM externo (Jetimob, SI9)
60
+ ```
61
+
62
+ 1. Usuario visita o site (WEB)
63
+ 2. WEB busca dados na API (imoveis, corretores, schemas)
64
+ 3. Usuario envia formulario → WEB envia lead para AUTOMATIONS
65
+ 4. AUTOMATIONS envia email via Resend + sincroniza com CRM
66
+
67
+ ---
68
+
69
+ ## Deploy Vercel
70
+
71
+ Cada app e deployada separadamente na Vercel:
72
+ - **WEB:** Site publico (dominio do cliente)
73
+ - **API:** API endpoints (region gru1 - SP)
74
+ - **AUTOMATIONS:** Endpoints de integracao
75
+
76
+ Cada app tem seu `vercel.json` com:
77
+ - framework: nextjs
78
+ - installCommand: pnpm i
79
+ - functions maxDuration: 30s
80
+
81
+ ---
82
+
83
+ ## Variaveis de Ambiente
84
+
85
+ ### WEB (.env.local)
86
+ ```
87
+ API_SECRET_KEY= # Chave da API
88
+ ADMIN_PASSWORD= # Senha admin (inspect)
89
+ NEXT_PUBLIC_SITE_URL= # URL do site (ex: http://localhost:3001)
90
+ NEXT_PUBLIC_API_URL= # URL da API (ex: http://localhost:5000)
91
+ NEXT_PUBLIC_AUTOMATIONS_URL= # URL automations (ex: http://localhost:8080)
92
+ YOUTUBE_API_KEY= # YouTube Data API v3
93
+ ```
94
+
95
+ ### API (.env)
96
+ ```
97
+ DATABASE_URL= # PostgreSQL connection string
98
+ API_SECRET_KEY= # Chave de autenticacao
99
+ API_BASE_URL= # URL da propria API
100
+ FRONTEND_BASE_URL= # URL do frontend
101
+ ```
102
+
103
+ ### AUTOMATIONS (.env.local)
104
+ ```
105
+ API_SECRET_KEY= # Chave da API
106
+ RESEND_API_KEY= # Chave do Resend (emails)
107
+ JETIMOB_PUBLIC_KEY= # CRM Jetimob
108
+ JETIMOB_PRIVATE_KEY= # CRM Jetimob
109
+ NEXT_PUBLIC_API_URL= # URL da API
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Banco de Dados
115
+
116
+ - **Provider:** PostgreSQL em DigitalOcean
117
+ - **Porta:** 25061 (SSL required)
118
+ - **ORM:** Prisma 6
119
+ - **Extensoes:** PostGIS (geolocation), UUID-OSSP
120
+ - **Models:** Property, Broker, Branch, spatial_ref_sys
121
+ - **Features:** Full-text search (tsvector), busca geoespacial (geometry)
122
+
123
+ ---
124
+
125
+ ## Pasta /dev/
126
+
127
+ Area de trabalho temporaria na raiz. Pode conter:
128
+ - Screenshots de debug
129
+ - Logs de teste
130
+ - Arquivos temporarios de desenvolvimento
131
+ - NAO e versionada com importancia (pode ser limpa)
132
+
133
+ ---
134
+
135
+ ## Pasta /ai/
136
+
137
+ Cerebro do agente de IA. Contem:
138
+ - `package.json` — instala pacotes de conhecimento via NPM
139
+ - `horizon.config.md` — prontuario do site
140
+ - `client/` — docs especificas deste cliente
141
+ - `node_modules/` — docs da plataforma + modulos + integracoes
142
+
143
+ ---
144
+
145
+ ## Scripts Uteis
146
+
147
+ ```bash
148
+ # Desenvolvimento
149
+ cd apps/web && pnpm dev # Frontend porta 3001
150
+ cd apps/api && pnpm dev # API porta 5000
151
+ cd apps/automations && pnpm dev # Automations porta 8080
152
+
153
+ # Build
154
+ cd apps/web && pnpm build
155
+ cd apps/api && pnpm build
156
+
157
+ # Banco de dados (API)
158
+ cd apps/api && pnpm db:setup # Setup inicial
159
+ cd apps/api && pnpm db:create # Criar tabelas
160
+ cd apps/api && pnpm db:sync # Sync Prisma
161
+ cd apps/api && npx prisma generate # Gerar client
162
+
163
+ # Storybook
164
+ cd apps/web && pnpm storybook # Porta 6006
165
+
166
+ # Email preview
167
+ cd apps/web && pnpm email
168
+ cd apps/automations && pnpm email
169
+ ```
@@ -0,0 +1,241 @@
1
+ # Arquitetura de Módulos - WEB
2
+
3
+ > Guia de estrutura padrão para módulos no frontend (`/apps/web/src/modules/`)
4
+
5
+ ---
6
+
7
+ ## Estrutura Padrão de um Módulo
8
+
9
+ ```
10
+ apps/web/src/modules/{entity}/
11
+ ├── core/ # Lógica core do módulo
12
+ │ ├── module.config.ts # Configuração do módulo
13
+ │ │
14
+ │ ├── item/ # Tudo relacionado a UM item
15
+ │ │ ├── index.ts # Exports principais
16
+ │ │ ├── {entity}-url-builder.ts # Builder de URLs
17
+ │ │ ├── enrichMapper.ts # Mapper para enriquecer dados
18
+ │ │ │
19
+ │ │ ├── schemas/ # Schemas do item
20
+ │ │ │ ├── index.ts # Exports dos schemas
21
+ │ │ │ ├── schema.ts # Schema TypeScript (SSOT)
22
+ │ │ │ ├── schema.generated.json # Schema gerado da API
23
+ │ │ │ ├── base-schema.json # Schema base (campos)
24
+ │ │ │ ├── display-layer-schema.json # Overrides de UI
25
+ │ │ │ └── computed-schema.json # Campos computados
26
+ │ │ │
27
+ │ │ └── computed-fields/ # Campos computados runtime
28
+ │ │ ├── index.ts
29
+ │ │ ├── {entity}-url.ts # Gerador de URL
30
+ │ │ └── assembleComputedData.ts
31
+ │ │
32
+ │ ├── list/ # Lógica de listagem (opcional)
33
+ │ │ └── ...
34
+ │ │
35
+ │ ├── libs/ # Bibliotecas específicas (opcional)
36
+ │ │ └── ...
37
+ │ │
38
+ │ └── services/ # Services do módulo
39
+ │ └── {Entity}Service.ts
40
+
41
+ └── features/ # Features/páginas
42
+ ├── item-display/ # Página individual
43
+ │ └── page-display/
44
+ │ ├── components/
45
+ │ │ └── sections/
46
+ │ └── data/
47
+ │ └── fetch{Entity}SSR.ts
48
+
49
+ └── search-list/ # Listagem com busca (opcional)
50
+ ├── components/
51
+ │ └── fields/
52
+ ├── config/
53
+ │ └── schema.ts
54
+ └── hooks/
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Arquivos Obrigatórios
60
+
61
+ ### **Core Item**
62
+
63
+ | Arquivo | Responsabilidade |
64
+ |---------|------------------|
65
+ | `core/item/index.ts` | Exporta enrichMapper, urlBuilder, schemas |
66
+ | `core/item/{entity}-url-builder.ts` | Gera URLs para páginas da entidade |
67
+ | `core/item/enrichMapper.ts` | Enriquece dados com schema + computed |
68
+
69
+ ### **Schemas**
70
+
71
+ | Arquivo | Responsabilidade |
72
+ |---------|------------------|
73
+ | `core/item/schemas/index.ts` | Exporta todos os schemas |
74
+ | `core/item/schemas/schema.ts` | Schema TypeScript principal |
75
+ | `core/item/schemas/schema.generated.json` | Schema gerado pela API (build time) |
76
+
77
+ ### **Computed Fields**
78
+
79
+ | Arquivo | Responsabilidade |
80
+ |---------|------------------|
81
+ | `core/item/computed-fields/index.ts` | Exporta funções de computed |
82
+ | `core/item/computed-fields/{entity}-url.ts` | Gera URL do item |
83
+ | `core/item/computed-fields/assembleComputedData.ts` | Monta objeto computed |
84
+
85
+ ---
86
+
87
+ ## 🔄 Fluxo de Dados
88
+
89
+ ```
90
+ API Response (raw data)
91
+
92
+
93
+ ┌─────────────────────┐
94
+ │ enrichMapper() │ ← Combina: data + schema + computedData
95
+ └─────────────────────┘
96
+
97
+
98
+ ┌─────────────────────┐
99
+ │ assembleComputed() │ ← Gera campos runtime (URLs, etc)
100
+ └─────────────────────┘
101
+
102
+
103
+ ┌─────────────────────┐
104
+ │ EnrichMapperResult │ → { data, schema, computedData, computedSchema }
105
+ └─────────────────────┘
106
+
107
+
108
+ Components
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Exemplo: Broker Module
114
+
115
+ ### **core/item/index.ts**
116
+ ```typescript
117
+ export { brokerEnrichMapper as enrichMapper } from "./enrichMapper";
118
+ export { brokerURLBuilder } from "./broker-url-builder";
119
+ export * from "./schemas";
120
+ export * from "./computed-fields";
121
+ ```
122
+
123
+ ### **core/item/schemas/index.ts**
124
+ ```typescript
125
+ export { brokerEntitySchema, getBrokerEntitySchema, type EntitySchema, type FieldSchema } from "./schema";
126
+ export { default as baseSchema } from "./base-schema.json";
127
+ export { default as displayLayerSchema } from "./display-layer-schema.json";
128
+ export { default as computedSchema } from "./computed-schema.json";
129
+ ```
130
+
131
+ ### **core/item/schemas/schema.ts**
132
+ ```typescript
133
+ import generatedSchema from "./schema.generated.json";
134
+
135
+ export interface EntitySchema {
136
+ entity: string;
137
+ version: string;
138
+ fields: FieldSchema[];
139
+ }
140
+
141
+ export interface FieldSchema {
142
+ key: string;
143
+ type: string;
144
+ categories: string[];
145
+ ui: { label: string; description?: string };
146
+ audit: { origin: string };
147
+ }
148
+
149
+ export const brokerEntitySchema = generatedSchema as EntitySchema;
150
+
151
+ export async function getBrokerEntitySchema(): Promise<EntitySchema> {
152
+ return brokerEntitySchema;
153
+ }
154
+ ```
155
+
156
+ ### **core/item/computed-fields/assembleComputedData.ts**
157
+ ```typescript
158
+ import { generateBrokerUrl } from "./broker-url";
159
+
160
+ export function assembleComputedData(rawData: Record<string, any>): Record<string, any> {
161
+ return {
162
+ src: generateBrokerUrl(rawData),
163
+ };
164
+ }
165
+ ```
166
+
167
+ ### **core/item/enrichMapper.ts**
168
+ ```typescript
169
+ import { baseSchema, displayLayerSchema, computedSchema } from "./schemas";
170
+ import { mergeArraysByKey } from "@/libs/horizon-utils/merge-arrays-by-key";
171
+ import type { EnrichMapperResult, FieldMetadata } from "@/libs/domain-data-display-enricher/types";
172
+ import { assembleComputedData } from "./computed-fields";
173
+
174
+ const displaySchema = mergeArraysByKey(baseSchema.fields, displayLayerSchema as any, "key");
175
+
176
+ export const brokerEnrichMapper = (rawData: Record<string, any>): EnrichMapperResult => {
177
+ return {
178
+ data: rawData,
179
+ schema: displaySchema as FieldMetadata[],
180
+ computedData: assembleComputedData(rawData),
181
+ computedSchema: computedSchema as FieldMetadata[],
182
+ };
183
+ };
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 🆚 Comparação: Property vs Broker
189
+
190
+ | Aspecto | Property | Broker |
191
+ |---------|----------|--------|
192
+ | **Complexidade** | Alta | Baixa |
193
+ | `core/item/schemas/` | | |
194
+ | `core/item/computed-fields/` | | |
195
+ | `core/libs/` | (7 módulos) | |
196
+ | `core/list/` | | |
197
+ | `features/search-list/` | (20+ componentes) | |
198
+ | `features/item-display/` | | |
199
+
200
+ **Use Property** como referência para módulos complexos com busca.
201
+ **Use Broker** como template para módulos simples (página individual apenas).
202
+
203
+ ---
204
+
205
+ ## 🚫 Erros Comuns
206
+
207
+ ### Schemas fora de `core/item/`
208
+ ```
209
+ # ERRADO
210
+ broker/core/schemas/
211
+
212
+ # CORRETO
213
+ broker/core/item/schemas/
214
+ ```
215
+
216
+ ### Imports com caminho antigo
217
+ ```typescript
218
+ // ERRADO
219
+ import baseSchema from "@/modules/broker/core/schemas/base-schema.json";
220
+
221
+ // CORRETO
222
+ import { baseSchema } from "./schemas";
223
+ ```
224
+
225
+ ### Dependência de pacote npm não instalado
226
+ ```typescript
227
+ // ERRADO (se broker-model não está no package.json)
228
+ import type { EntitySchema } from "@horizon-domains/broker-model";
229
+
230
+ // CORRETO (tipos locais)
231
+ export interface EntitySchema { ... }
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Referências
237
+
238
+ - **API Module Pattern**: [MODULE_CREATION_PATTERN.md](./MODULE_CREATION_PATTERN.md)
239
+ - **Search Engine API**: [SEARCH_ENGINE_API.md](./SEARCH_ENGINE_API.md)
240
+ - **Property Module**: `/apps/web/src/modules/property/` (referência completa)
241
+ - **Broker Module**: `/apps/web/src/modules/broker/` (referência simplificada)
@@ -0,0 +1,200 @@
1
+ # Batch Totals API - Menu Dinâmico com Performance
2
+
3
+ ## **Problema Resolvido**
4
+
5
+ **Cenário:** Menu de navegação em site imobiliário
6
+ ```
7
+ IMÓVEIS
8
+ ├── Apartamento 2 quartos (845) ← precisa do total
9
+ ├── Apartamento 3 quartos (723)
10
+ ├── Casa 2 quartos (400)
11
+ ├── Casa 3 quartos (169)
12
+ └── ... mais 12 categorias
13
+ ```
14
+
15
+ **Solução Ruim:** 16 requests HTTP separados
16
+ **Solução Inteligente:** 1 request com array de queries
17
+
18
+ ## **Performance**
19
+
20
+ | Abordagem | HTTP Calls | Tempo | Eficiência |
21
+ |-----------|------------|-------|------------|
22
+ | **Múltiplos requests** | 16 | ~2.3s | Lento |
23
+ | **Batch Totals** | 1 | ~250ms | 90% mais rápido |
24
+
25
+ ## **API Specification**
26
+
27
+ ### **Endpoint:**
28
+ ```
29
+ POST /api/property/batch-totals
30
+ ```
31
+
32
+ ### **Request Format:**
33
+ ```json
34
+ {
35
+ "queries": [
36
+ {
37
+ "key": "apartamento_2q",
38
+ "filters": {
39
+ "tipo": "Apartamento",
40
+ "dormitorios": 2
41
+ },
42
+ "meta": { "total": true }
43
+ },
44
+ {
45
+ "key": "casa_3q",
46
+ "filters": {
47
+ "tipo": "Casa",
48
+ "dormitorios": 3
49
+ },
50
+ "meta": { "total": true }
51
+ }
52
+ ]
53
+ }
54
+ ```
55
+
56
+ ### **Response Format:**
57
+ ```json
58
+ {
59
+ "results": {
60
+ "apartamento_2q": { "total": 845 },
61
+ "casa_3q": { "total": 169 }
62
+ },
63
+ "metadata": {
64
+ "executionTime": 245,
65
+ "queriesProcessed": 2,
66
+ "parallelExecution": true
67
+ }
68
+ }
69
+ ```
70
+
71
+ ## **Implementação Técnica**
72
+
73
+ ### **Arquitetura:**
74
+ ```
75
+ Controller → Service → Promise.all() → advancedSearch() × N
76
+ ```
77
+
78
+ ### **Características:**
79
+ - **Execução paralela** com `Promise.all()`
80
+ - **Keys únicas** para cada query
81
+ - **Filtros flexíveis** (qualquer combinação)
82
+ - **Error handling** individual por query
83
+ - **Performance logs** automáticos
84
+
85
+ ## **Casos de Uso**
86
+
87
+ ### **1. Menu de Navegação**
88
+ ```javascript
89
+ const menuQueries = [
90
+ { key: "apt_2q", filters: { tipo: "Apartamento", dormitorios: 2 } },
91
+ { key: "apt_3q", filters: { tipo: "Apartamento", dormitorios: 3 } },
92
+ { key: "casa_2q", filters: { tipo: "Casa", dormitorios: 2 } }
93
+ ];
94
+ ```
95
+
96
+ ### **2. Dashboard com Métricas**
97
+ ```javascript
98
+ const dashboardQueries = [
99
+ { key: "total_ativos", filters: { status: "ativo" } },
100
+ { key: "total_vendidos", filters: { status: "vendido" } },
101
+ { key: "total_premium", filters: { destaque: true } }
102
+ ];
103
+ ```
104
+
105
+ ### **3. Filtros Dinâmicos**
106
+ ```javascript
107
+ const filterCounts = [
108
+ { key: "por_cidade", filters: { endereco_cidade: "Florianópolis" } },
109
+ { key: "por_preco", filters: { valor_venda: { gte: 500000 } } },
110
+ { key: "com_piscina", filters: { caracteristicas: { contains: "Piscina" } } }
111
+ ];
112
+ ```
113
+
114
+ ## **Frontend Integration**
115
+
116
+ ### **Next.js Static Props:**
117
+ ```typescript
118
+ export async function getStaticProps() {
119
+ const response = await fetch('/api/property/batch-totals', {
120
+ method: 'POST',
121
+ headers: { 'Content-Type': 'application/json' },
122
+ body: JSON.stringify({ queries: MENU_QUERIES })
123
+ });
124
+
125
+ const { results } = await response.json();
126
+
127
+ return {
128
+ props: { menuCounters: results },
129
+ revalidate: 7200 // Cache 2h
130
+ };
131
+ }
132
+ ```
133
+
134
+ ### **SWR Client-Side:**
135
+ ```typescript
136
+ import useSWR from 'swr';
137
+
138
+ const { data: counters } = useSWR(
139
+ 'menu-counters',
140
+ () => batchTotals(MENU_QUERIES),
141
+ { refreshInterval: 300000 } // 5min
142
+ );
143
+ ```
144
+
145
+ ## **Limitações & Considerações**
146
+
147
+ ### **Limites:**
148
+ - **Máximo:** 20 queries por request
149
+ - **Timeout:** 30 segundos total
150
+ - **Cache:** Recomendado 1-4 horas
151
+
152
+ ### **Error Handling:**
153
+ ```json
154
+ {
155
+ "results": {
156
+ "valid_query": { "total": 845 },
157
+ "invalid_query": { "error": "Campo inválido", "total": 0 }
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### **Performance Tips:**
163
+ - Cache no cliente (SWR/React Query)
164
+ - Cache no servidor (Redis)
165
+ - Revalidação com webhooks
166
+ - Não usar para queries complexas
167
+
168
+ ## **Exemplo Completo**
169
+
170
+ ### **Request Real:**
171
+ ```bash
172
+ curl -X POST http://localhost:5000/api/property/batch-totals \
173
+ -H "Content-Type: application/json" \
174
+ -d '{
175
+ "queries": [
176
+ {
177
+ "key": "apartamento_2q",
178
+ "filters": { "tipo": "Apartamento", "dormitorios": 2 },
179
+ "meta": { "total": true }
180
+ }
181
+ ]
182
+ }'
183
+ ```
184
+
185
+ ### **Response Real:**
186
+ ```json
187
+ {
188
+ "results": {
189
+ "apartamento_2q": { "total": 845 }
190
+ },
191
+ "metadata": {
192
+ "executionTime": 156,
193
+ "queriesProcessed": 1
194
+ }
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ **Batch Totals API - Solução definitiva para menus dinâmicos com performance!**