@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,233 @@
1
+ # Navegacao Dinamica -- Sistema de Menus do Horizon
2
+
3
+ > Arquitetura do sistema de navegacao: dados estaticos, fetchers dinamicos, composicao em camadas, provider pattern.
4
+
5
+ **Localizacao:** `apps/web/src/capabilities/navigations/`
6
+
7
+ ---
8
+
9
+ ## Conceito
10
+
11
+ O sistema de navegacao do Horizon usa **composicao em camadas**:
12
+
13
+ ```
14
+ Building Blocks (Imoveis, QuemSomos, Anuncie...)
15
+ | composicao simples
16
+ Menus Compostos (Institucional = [QuemSomos, Localizacao, ...])
17
+ | reutilizacao de blocos
18
+ Menus Dinamicos (Venda, Locacao com children da API + contagem)
19
+ | spread operator
20
+ Menus Finais (headerMenu, footerMenu, drawerMenu)
21
+ ```
22
+
23
+ Dados sao "burros" (so dados), componentes sao "inteligentes" (resolvem icones, estado ativo, etc.).
24
+
25
+ ---
26
+
27
+ ## Arquivos
28
+
29
+ | Arquivo | Funcao |
30
+ |---|---|
31
+ | `types.ts` | Interface NavigationItem (title, href, icon, children, count, badge, etc.) |
32
+ | `data/navigations.tsx` | Blocos basicos + menus compostos + `getMenus()` async |
33
+ | `fetchers/property.ts` | Busca facets da API e gera children dinamicos (Venda/Locacao por tipo) |
34
+ | `navigation.service.ts` | Cache server-side (`unstable_cache`, revalidate 1800s) |
35
+ | `NavigationProvider.tsx` | Context client-side (`useNavigation()` hook) |
36
+ | `helpers/` | `linkAttrs()`, `isActive()`, `slugify()`, `pluralize()` |
37
+
38
+ ---
39
+
40
+ ## Fluxo Completo
41
+
42
+ ```
43
+ 1. Server-side: layout.tsx chama await getNavigation()
44
+ 2. navigation.service.ts:
45
+ - DEV: chama getMenus() direto (sem cache)
46
+ - PROD: unstable_cache com revalidate: 1800s (30 min)
47
+ 3. getMenus() chama fetchers em paralelo (Promise.all)
48
+ - getVendaMenu() -> facet tipo_gen filtrado por operacao=venda
49
+ - getLocacaoMenu() -> facet tipo_gen filtrado por operacao=locacao
50
+ 4. Fetchers usam propertyService.search() com facets.includeCount=true
51
+ 5. API retorna facets com value + count por tipo de imovel
52
+ 6. Children montados com titulo, href (slug), count
53
+ 7. Resultado passa pelo NavigationProvider
54
+ 8. Client-side: useNavigation() retorna { headerMenu, footerMenu, drawerMenu }
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Interface NavigationItem
60
+
61
+ ```typescript
62
+ interface NavigationItem {
63
+ // Obrigatorios
64
+ title: string; // "Venda", "Apartamentos"
65
+ href: string; // "/imoveis?operacao=venda"
66
+
67
+ // Estrutura
68
+ children?: NavigationItem[]; // Submenu recursivo
69
+ separator?: boolean; // Divisor visual
70
+
71
+ // Apresentacao
72
+ icon?: string; // Nome do icone (string, nao import)
73
+ thumbnail?: string; // URL de imagem (para cards)
74
+ overline?: string; // Texto acima
75
+ description?: string; // Descricao/tooltip
76
+ badge?: string; // Text badge ("Novo", "3")
77
+
78
+ // Funcionalidade
79
+ count?: number; // Contagem dinamica (ex: 42 imoveis)
80
+ disabled?: boolean;
81
+ target?: "_self" | "_blank";
82
+ rel?: string; // Auto "noopener noreferrer" se _blank
83
+ match?: "exact" | "startsWith" | RegExp | ((pathname: string) => boolean);
84
+
85
+ // Avancado
86
+ id?: string;
87
+ audience?: ("guest" | "user" | "admin")[];
88
+ dropdownLayout?: 'default' | 'cards'; // Layout do dropdown (cards=4 cols com imagens)
89
+ meta?: Record<string, any>;
90
+ }
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Dinamico vs Estatico
96
+
97
+ ### Estaticos (em `data/navigations.tsx`)
98
+
99
+ ```typescript
100
+ // Building blocks reutilizaveis
101
+ export const Imoveis = { title: "Venda", href: "/imoveis?operacao=venda", icon: "property" };
102
+ export const Especiais = { title: "Servicos", href: "#", dropdownLayout: "cards", children: [...] };
103
+
104
+ // Fallback
105
+ export const headerMainMenu: NavigationItem[] = [Imoveis, Locacao, Especiais, Institucional];
106
+ ```
107
+
108
+ ### Dinamicos (via `fetchers/property.ts`)
109
+
110
+ ```typescript
111
+ // getVendaMenu() busca facets da API:
112
+ {
113
+ title: "Venda",
114
+ href: "/imoveis?operacao=venda",
115
+ icon: "house",
116
+ children: [
117
+ { title: "Apartamentos", href: "/imoveis/venda/apartamentos?...", count: 1240 },
118
+ { title: "Casas", href: "/imoveis/venda/casas?...", count: 892 },
119
+ // ... gerado automaticamente pelos facets
120
+ ]
121
+ }
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Contagem Dinamica (count)
127
+
128
+ ```
129
+ propertyService.search({
130
+ filters: { operacao: "venda" },
131
+ list: { limit: 1 },
132
+ facets: { includeCount: true, fields: [{ type: "terms", field: "tipo_gen" }] }
133
+ })
134
+ -> API retorna: { facets: { data: { tipo_gen: [{ value: "Apartamento", count: 1240 }, ...] } } }
135
+ -> Mapped para NavigationItem com count
136
+ -> Renderizado: <span>{child.count}</span>
137
+ ```
138
+
139
+ Os menus dinamicos se adaptam automaticamente ao trocar de CRM (dados vem da API via facets).
140
+
141
+ ---
142
+
143
+ ## Menus Finais
144
+
145
+ | Menu | Composicao |
146
+ |---|---|
147
+ | `headerMenu` | [Venda(dinamico), Locacao(dinamico), Especiais(estatico), Institucional(estatico)] |
148
+ | `footerMenu` | [Venda, Locacao, ...Especiais.children, ...Institucional.children] (flat) |
149
+ | `drawerMenu` | [Venda, Locacao, ...Institucional.children, Especiais] (mobile) |
150
+
151
+ ---
152
+
153
+ ## Converters: Social e Contact -> NavigationItem
154
+
155
+ O sistema converte dados de contato e redes sociais para NavigationItem, reutilizando a mesma interface:
156
+
157
+ ```typescript
158
+ // Social -> NavigationItem
159
+ toNavigationItem(social): { title: social.label, href: social.url, icon: getIconForSocial(social), target: "_blank" }
160
+
161
+ // Contact -> NavigationItem
162
+ toNavigationItem(contact): { title: contact.label, href: getContactUrl(contact), icon: getIconForContact(contact) }
163
+ ```
164
+
165
+ **Registries:**
166
+ - `capabilities/social/registry.ts` -- plataformas (instagram, facebook, whatsapp, etc.)
167
+ - `capabilities/contacts/registry.ts` -- tipos de contato (whatsapp, telegram, mobile, email)
168
+
169
+ **Dados:**
170
+ - `capabilities/social/data/examples.ts` -- redes sociais do cliente
171
+ - `capabilities/contacts/data/contacts.data.ts` -- contatos do cliente
172
+
173
+ ---
174
+
175
+ ## Helpers
176
+
177
+ | Helper | Funcao |
178
+ |---|---|
179
+ | `isActive(pathname, item)` | Determina se item esta ativo (exact, startsWith, RegExp, funcao) |
180
+ | `linkAttrs(item)` | Gera target/rel seguro (auto noopener noreferrer se _blank) |
181
+ | `slugify(text)` | Gera URL amigavel ("Ponto Comercial" -> "ponto-comercial") |
182
+ | `pluralize(word)` | Pluraliza em portugues ("Casa" -> "Casas", "Rural" -> "Rurais") |
183
+ | `filterByAudience(items, role)` | Filtra por tipo de usuario |
184
+ | `countBadges(items)` | Soma badges numericas recursivamente |
185
+
186
+ ---
187
+
188
+ ## Cache e Revalidacao
189
+
190
+ ```typescript
191
+ // navigation.service.ts
192
+ const getCachedNavigation = unstable_cache(
193
+ async () => getMenus(),
194
+ ["dynamic-navigation"],
195
+ { revalidate: 1800 } // 30 minutos
196
+ );
197
+
198
+ // DEV: sem cache (reflete mudancas instantaneamente)
199
+ // PROD: cache de 30min (economia de requests a API)
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Componentes que Usam
205
+
206
+ | Componente | Menu | Contexto |
207
+ |---|---|---|
208
+ | `NavigationDesktop.tsx` | headerMenu | Desktop dropdown com hover |
209
+ | `NavigationDesktopDropdown.tsx` | headerMenu | Alternativa com HoverCard 3 colunas |
210
+ | `FooterNavigation.tsx` | footerMenu | Links simples no rodape |
211
+ | `MobileMenu.tsx` | drawerMenu | Menu recursivo em drawer |
212
+ | `ContactBox.tsx` | contacts->nav | Contatos via converter |
213
+ | `SocialLinks.tsx` | socials->nav | Redes sociais via converter |
214
+
215
+ ---
216
+
217
+ ## Como Trocar/Adicionar Menus
218
+
219
+ 1. **Bloco basico novo:** adicionar export const em `data/navigations.tsx`
220
+ 2. **Novo fetcher dinamico:** criar funcao em `fetchers/` usando fetchFacetMenu pattern
221
+ 3. **Menu composto:** compor com children usando blocos existentes
222
+ 4. **Menu final:** incluir nos arrays de headerMenu/footerMenu/drawerMenu em getMenus()
223
+ 5. **Cache:** revalida a cada 30min em prod; sem cache em dev
224
+
225
+ ---
226
+
227
+ ## Regras
228
+
229
+ 1. Icones sao STRINGS (ex: "house"), nunca imports diretos de Lucide
230
+ 2. NavigationItem e a interface unica para TODOS os contextos (header, footer, drawer, contatos, sociais)
231
+ 3. Menus dinamicos se adaptam automaticamente ao trocar de CRM (dados vem da API)
232
+ 4. Sempre usar `linkAttrs()` para gerar target/rel seguro
233
+ 5. Ao adicionar menu dinamico, seguir o pattern de `getVendaMenu()` como referencia
@@ -0,0 +1,63 @@
1
+ # Paginas Basicas — Pacote Padrao Horizon
2
+
3
+ > Todo site Horizon vem com ~14 paginas basicas incluidas no pacote.
4
+
5
+ ---
6
+
7
+ ## Lista Completa das Paginas Basicas
8
+
9
+ | # | Rota | Pagina | Tipo |
10
+ |---|---|---|---|
11
+ | 1 | `/` ou `/home` | Home Page | Marketing |
12
+ | 2 | `/imoveis/[[...slug]]` | Busca/Lista de Imoveis | SSR Dinamica |
13
+ | 3 | `/imovel/[...slug]` | Detalhe do Imovel | SSR Dinamica |
14
+ | 4 | `/corretor/[...slug]` | Perfil do Corretor | SSR Dinamica |
15
+ | 5 | `/favoritos` | Favoritos | SSR |
16
+ | 6 | `/imoveis-compartilhados/[[...slug]]` | Imoveis Compartilhados | SSR Dinamica |
17
+ | 7 | `/quem-somos` | Quem Somos | Estatica |
18
+ | 8 | `/localizacao` | Localizacao/Unidades | Estatica |
19
+ | 9 | `/contato` | Formulario de Contato | Estatica |
20
+ | 10 | `/financiamento` | Financiamento | Estatica |
21
+ | 11 | `/anuncie-seu-imovel` | Anuncie | Estatica |
22
+ | 12 | `/encomende-seu-imovel` | Encomende | Estatica |
23
+ | 13 | `/obrigado` | Pagina de Agradecimento | Estatica |
24
+ | 14 | `/termos-uso` | Termos de Uso | Legal |
25
+ | 15 | `/politica-privacidade` | Politica de Privacidade | Legal |
26
+
27
+ ---
28
+
29
+ ## Paginas Auxiliares (incluso)
30
+
31
+ | Rota | Pagina | Tipo |
32
+ |---|---|---|
33
+ | `/guia-de-compra` | Guia de Compra | Marketing |
34
+ | `/guia-de-locacao` | Guia de Locacao | Marketing |
35
+ | `/avaliacao-imovel` | Avaliacao de Imovel | Marketing |
36
+ | `/login` | Login Admin | Minimal layout |
37
+ | `/print-imovel/[reference]` | Impressao de Imovel | Print layout |
38
+ | `/imovel/inspect/[...slug]` | Inspect Mode (admin) | Debug |
39
+ | `/sitemap.xml` | Sitemap XML | SEO |
40
+
41
+ ---
42
+
43
+ ## Layouts
44
+
45
+ As paginas sao organizadas em grupos de layout:
46
+
47
+ - **(marketing)** — Layout completo com header, footer, navegacao
48
+ - **(print)** — Layout minimo para impressao
49
+ - **(minimal)** — Layout minimo (login)
50
+ - **(design)** — Layout para paginas avancadas/demo
51
+
52
+ ---
53
+
54
+ ## Ao Criar Site Novo
55
+
56
+ Todas as paginas basicas vem do template Horizon. O que trocar:
57
+ 1. **Home** — Trocar conteudo, imagens, textos
58
+ 2. **Quem Somos** — Trocar historia, fotos, missao/visao
59
+ 3. **Localizacao** — Trocar enderecos e mapa
60
+ 4. **Contato** — Trocar destino de leads
61
+ 5. **Financiamento** — Verificar bancos disponiveis
62
+ 6. **Termos/Privacidade** — Trocar nome da empresa
63
+ 7. **SEO** — Trocar meta tags em cada pagina