@hed-hog/catalog 0.0.293 → 0.0.295
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 +391 -361
- package/dist/catalog-resource.config.d.ts.map +1 -1
- package/dist/catalog-resource.config.js +51 -24
- package/dist/catalog-resource.config.js.map +1 -1
- package/dist/catalog.controller.d.ts +420 -0
- package/dist/catalog.controller.d.ts.map +1 -1
- package/dist/catalog.controller.js +98 -0
- package/dist/catalog.controller.js.map +1 -1
- package/dist/catalog.module.d.ts.map +1 -1
- package/dist/catalog.module.js +5 -1
- package/dist/catalog.module.js.map +1 -1
- package/dist/catalog.service.d.ts +216 -1
- package/dist/catalog.service.d.ts.map +1 -1
- package/dist/catalog.service.js +1121 -7
- package/dist/catalog.service.js.map +1 -1
- package/hedhog/data/catalog_attribute.yaml +202 -0
- package/hedhog/data/catalog_attribute_option.yaml +109 -0
- package/hedhog/data/catalog_category.yaml +47 -0
- package/hedhog/data/catalog_category_attribute.yaml +209 -0
- package/hedhog/data/menu.yaml +46 -12
- package/hedhog/data/role.yaml +7 -7
- package/hedhog/data/route.yaml +64 -0
- package/hedhog/frontend/app/[resource]/page.tsx.ejs +358 -0
- package/hedhog/frontend/app/_components/catalog-ai-form-assist-dialog.tsx.ejs +340 -0
- package/hedhog/frontend/app/_components/catalog-resource-form-sheet.tsx.ejs +815 -0
- package/hedhog/frontend/app/_lib/catalog-resources.tsx.ejs +504 -736
- package/hedhog/frontend/app/dashboard/page.tsx.ejs +14 -83
- package/hedhog/frontend/messages/en.json +150 -60
- package/hedhog/frontend/messages/pt.json +185 -95
- package/hedhog/table/catalog_affiliate_program.yaml +41 -41
- package/hedhog/table/catalog_attribute.yaml +22 -7
- package/hedhog/table/catalog_attribute_group.yaml +18 -18
- package/hedhog/table/catalog_attribute_option.yaml +40 -0
- package/hedhog/table/catalog_brand.yaml +34 -34
- package/hedhog/table/catalog_category.yaml +40 -0
- package/hedhog/table/catalog_category_attribute.yaml +13 -7
- package/hedhog/table/catalog_click_event.yaml +50 -50
- package/hedhog/table/catalog_comparison.yaml +3 -6
- package/hedhog/table/catalog_comparison_highlight.yaml +39 -39
- package/hedhog/table/catalog_comparison_item.yaml +30 -30
- package/hedhog/table/catalog_content_relation.yaml +42 -42
- package/hedhog/table/catalog_import_run.yaml +33 -33
- package/hedhog/table/catalog_import_source.yaml +24 -24
- package/hedhog/table/catalog_merchant.yaml +29 -29
- package/hedhog/table/catalog_offer.yaml +83 -83
- package/hedhog/table/catalog_price_history.yaml +34 -34
- package/hedhog/table/catalog_product.yaml +5 -3
- package/hedhog/table/catalog_product_attribute_value.yaml +15 -2
- package/hedhog/table/catalog_product_category.yaml +3 -3
- package/hedhog/table/catalog_product_image.yaml +34 -34
- package/hedhog/table/catalog_product_score.yaml +38 -38
- package/hedhog/table/catalog_product_site.yaml +47 -47
- package/hedhog/table/catalog_product_tag.yaml +19 -19
- package/hedhog/table/catalog_score_criterion.yaml +25 -8
- package/hedhog/table/catalog_seo_page_rule.yaml +2 -2
- package/hedhog/table/catalog_similarity_rule.yaml +19 -6
- package/hedhog/table/catalog_site.yaml +8 -0
- package/hedhog/table/catalog_site_category.yaml +3 -3
- package/package.json +7 -7
- package/src/catalog-resource.config.ts +51 -24
- package/src/catalog.controller.ts +67 -0
- package/src/catalog.module.ts +5 -1
- package/src/catalog.service.ts +1531 -6
- package/src/index.ts +1 -1
- package/src/language/en.json +4 -4
- package/src/language/pt.json +4 -4
package/README.md
CHANGED
|
@@ -1,379 +1,409 @@
|
|
|
1
|
-
```markdown
|
|
2
1
|
# @hed-hog/catalog
|
|
3
2
|
|
|
4
|
-
##
|
|
5
|
-
|
|
6
|
-
O módulo `@hed-hog/catalog` é responsável pela gestão centralizada dos recursos do catálogo, incluindo produtos, ofertas, marcas, atributos, comparações, imagens e regras relacionadas. Ele oferece uma API REST para operações CRUD, listagem paginada, estatísticas e manipulação de imagens de produtos, integrando-se com outros módulos do monorepo HedHog.
|
|
7
|
-
|
|
8
|
-
## 2. Escopo e responsabilidades
|
|
9
|
-
|
|
10
|
-
- Gerenciar recursos do catálogo como produtos, ofertas, marcas, atributos, comparações, imagens, entre outros.
|
|
11
|
-
- Fornecer endpoints para criação, leitura, atualização, exclusão e listagem paginada de recursos.
|
|
12
|
-
- Aplicar filtros e buscas sensíveis a campos configurados para cada recurso.
|
|
13
|
-
- Gerar estatísticas básicas dos recursos.
|
|
14
|
-
- Controlar imagens associadas a produtos.
|
|
15
|
-
- Garantir autenticação e autorização baseadas em papéis específicos.
|
|
16
|
-
- Integrar com serviços de paginação, localização e banco de dados Prisma.
|
|
17
|
-
|
|
18
|
-
## 3. Endpoints
|
|
19
|
-
|
|
20
|
-
### Listar imagens de um produto
|
|
21
|
-
|
|
22
|
-
- **Método:** GET
|
|
23
|
-
- **Path:** `/catalog/products/:id/images`
|
|
24
|
-
- **Autenticação:** Sim (roles: `admin`, `admin-catalog`)
|
|
25
|
-
- **Descrição:** Lista imagens associadas a um produto, com paginação e ordenação por `sort_order`.
|
|
26
|
-
- **Parâmetros:**
|
|
27
|
-
- `id` (path): ID numérico do produto
|
|
28
|
-
- Query params de paginação (ex: `page`, `limit`)
|
|
29
|
-
- Header `Accept-Language` para locale (ex: `pt-BR`)
|
|
30
|
-
- **Resposta:** Lista paginada de imagens do produto com campos conforme tabela `catalog_product_image`.
|
|
31
|
-
- **Erros:**
|
|
32
|
-
- 400 Bad Request: recurso não suportado
|
|
33
|
-
- 401 Unauthorized: acesso não autorizado
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
### Obter estatísticas de um recurso
|
|
38
|
-
|
|
39
|
-
- **Método:** GET
|
|
40
|
-
- **Path:** `/catalog/:resource/stats`
|
|
41
|
-
- **Autenticação:** Sim (roles: `admin`, `admin-catalog`)
|
|
42
|
-
- **Descrição:** Retorna estatísticas básicas do recurso, como total de registros e quantidade ativa (se aplicável).
|
|
43
|
-
- **Parâmetros:**
|
|
44
|
-
- `resource` (path): nome do recurso (ex: `product`, `brand`)
|
|
45
|
-
- Header `Accept-Language` para locale
|
|
46
|
-
- **Resposta:**
|
|
47
|
-
```json
|
|
48
|
-
{
|
|
49
|
-
"total": number,
|
|
50
|
-
"active": number (opcional)
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
- **Erros:**
|
|
54
|
-
- 400 Bad Request: recurso não suportado
|
|
55
|
-
- 401 Unauthorized
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
### Obter recurso por ID
|
|
60
|
-
|
|
61
|
-
- **Método:** GET
|
|
62
|
-
- **Path:** `/catalog/:resource/:id`
|
|
63
|
-
- **Autenticação:** Sim (roles: `admin`, `admin-catalog`)
|
|
64
|
-
- **Descrição:** Retorna um registro específico do recurso pelo ID.
|
|
65
|
-
- **Parâmetros:**
|
|
66
|
-
- `resource` (path): nome do recurso
|
|
67
|
-
- `id` (path): ID numérico do registro
|
|
68
|
-
- Header `Accept-Language` para locale
|
|
69
|
-
- **Resposta:** Objeto do recurso com campos conforme configuração.
|
|
70
|
-
- **Erros:**
|
|
71
|
-
- 400 Bad Request: recurso não suportado
|
|
72
|
-
- 404 Not Found: recurso não encontrado
|
|
73
|
-
- 401 Unauthorized
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
### Atualizar recurso por ID
|
|
78
|
-
|
|
79
|
-
- **Método:** PATCH
|
|
80
|
-
- **Path:** `/catalog/:resource/:id`
|
|
81
|
-
- **Autenticação:** Sim (roles: `admin`, `admin-catalog`)
|
|
82
|
-
- **Descrição:** Atualiza campos permitidos de um registro do recurso.
|
|
83
|
-
- **Parâmetros:**
|
|
84
|
-
- `resource` (path): nome do recurso
|
|
85
|
-
- `id` (path): ID do registro
|
|
86
|
-
- Body JSON: campos a atualizar (somente campos configurados para o recurso)
|
|
87
|
-
- Header `Accept-Language` para locale
|
|
88
|
-
- **Resposta:** Objeto atualizado do recurso.
|
|
89
|
-
- **Erros:**
|
|
90
|
-
- 400 Bad Request: recurso não suportado ou payload inválido
|
|
91
|
-
- 404 Not Found: recurso não encontrado
|
|
92
|
-
- 401 Unauthorized
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
### Deletar recurso por ID
|
|
97
|
-
|
|
98
|
-
- **Método:** DELETE
|
|
99
|
-
- **Path:** `/catalog/:resource/:id`
|
|
100
|
-
- **Autenticação:** Sim (roles: `admin`, `admin-catalog`)
|
|
101
|
-
- **Descrição:** Remove um registro do recurso pelo ID.
|
|
102
|
-
- **Parâmetros:**
|
|
103
|
-
- `resource` (path): nome do recurso
|
|
104
|
-
- `id` (path): ID do registro
|
|
105
|
-
- Header `Accept-Language` para locale
|
|
106
|
-
- **Resposta:** Objeto do recurso deletado.
|
|
107
|
-
- **Erros:**
|
|
108
|
-
- 400 Bad Request: recurso não suportado
|
|
109
|
-
- 404 Not Found: recurso não encontrado
|
|
110
|
-
- 401 Unauthorized
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
### Listar recursos com paginação e filtros
|
|
115
|
-
|
|
116
|
-
- **Método:** GET
|
|
117
|
-
- **Path:** `/catalog/:resource`
|
|
118
|
-
- **Autenticação:** Sim (roles: `admin`, `admin-catalog`)
|
|
119
|
-
- **Descrição:** Lista registros do recurso com paginação, ordenação e filtros configurados.
|
|
120
|
-
- **Parâmetros:**
|
|
121
|
-
- `resource` (path): nome do recurso
|
|
122
|
-
- Query params: filtros conforme campos configurados, paginação (`page`, `limit`)
|
|
123
|
-
- Header `Accept-Language` para locale
|
|
124
|
-
- **Resposta:** Lista paginada de registros do recurso.
|
|
125
|
-
- **Erros:**
|
|
126
|
-
- 400 Bad Request: recurso não suportado ou filtro inválido
|
|
127
|
-
- 401 Unauthorized
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
### Criar novo recurso
|
|
132
|
-
|
|
133
|
-
- **Método:** POST
|
|
134
|
-
- **Path:** `/catalog/:resource`
|
|
135
|
-
- **Autenticação:** Sim (roles: `admin`, `admin-catalog`)
|
|
136
|
-
- **Descrição:** Cria um novo registro do recurso com os campos permitidos.
|
|
137
|
-
- **Parâmetros:**
|
|
138
|
-
- `resource` (path): nome do recurso
|
|
139
|
-
- Body JSON: campos para criação (somente campos configurados para o recurso)
|
|
140
|
-
- Header `Accept-Language` para locale
|
|
141
|
-
- **Resposta:** Objeto criado do recurso.
|
|
142
|
-
- **Erros:**
|
|
143
|
-
- 400 Bad Request: recurso não suportado ou payload inválido
|
|
144
|
-
- 401 Unauthorized
|
|
145
|
-
|
|
146
|
-
## 4. Regras de autenticação e autorização
|
|
147
|
-
|
|
148
|
-
- Todos os endpoints são protegidos e requerem autenticação.
|
|
149
|
-
- Apenas usuários com os papéis `admin` ou `admin-catalog` podem acessar os endpoints do módulo.
|
|
150
|
-
- A autorização é baseada em roles definidas no sistema, conforme arquivo `hedhog/data/role.yaml`.
|
|
151
|
-
|
|
152
|
-
## 5. Estruturas de request/response
|
|
153
|
-
|
|
154
|
-
- **Request Body:**
|
|
155
|
-
- Campos aceitos são somente os definidos na configuração do recurso (`catalogResourceConfig`), ignorando campos extras.
|
|
156
|
-
- Valores booleanos podem ser enviados como strings `"true"` ou `"false"`.
|
|
157
|
-
- Valores numéricos podem ser enviados como strings numéricas e serão convertidos automaticamente.
|
|
158
|
-
- **Response:**
|
|
159
|
-
- Objetos JSON com os campos do recurso conforme tabelas do banco.
|
|
160
|
-
- Listagens paginadas seguem o padrão do serviço de paginação do HedHog.
|
|
161
|
-
|
|
162
|
-
## 6. Erros comuns
|
|
163
|
-
|
|
164
|
-
- **400 Bad Request:** Recurso não suportado, payload inválido ou filtro inválido.
|
|
165
|
-
- **401 Unauthorized:** Usuário não autenticado ou sem permissão.
|
|
166
|
-
- **404 Not Found:** Registro não encontrado para o ID informado.
|
|
167
|
-
|
|
168
|
-
## 7. Banco de dados (tabelas YAML)
|
|
169
|
-
|
|
170
|
-
### catalog_product
|
|
171
|
-
|
|
172
|
-
- **Finalidade:** Armazena os produtos do catálogo.
|
|
173
|
-
- **Colunas principais:**
|
|
174
|
-
- `id` (PK)
|
|
175
|
-
- `brand_id` (FK para `catalog_brand`, nullable)
|
|
176
|
-
- `category_id` (FK para `category`)
|
|
177
|
-
- `primary_content_id` (FK para `content`, nullable)
|
|
178
|
-
- `slug` (varchar 255, único)
|
|
179
|
-
- `name` (varchar 255)
|
|
180
|
-
- `short_description` (varchar 500, nullable)
|
|
181
|
-
- `description` (text, nullable)
|
|
182
|
-
- `model_name` (varchar 255, nullable)
|
|
183
|
-
- `sku` (varchar 120, nullable)
|
|
184
|
-
- `gtin` (varchar 120, nullable)
|
|
185
|
-
- `status` (enum: `draft`, `published`, `archived`, default `draft`)
|
|
186
|
-
- `comparison_status` (enum: `draft`, `ready`, `disabled`, default `draft`)
|
|
187
|
-
- `release_date` (datetime, nullable)
|
|
188
|
-
- `spec_snapshot_json` (json, nullable)
|
|
189
|
-
- `comparison_snapshot_json` (json, nullable)
|
|
190
|
-
- `is_active` (boolean, default `true`)
|
|
191
|
-
- `created_at`, `updated_at` (timestamps)
|
|
192
|
-
- **Defaults:** Conforme descrito acima.
|
|
193
|
-
- **Nulabilidade:** Campos como `brand_id`, `primary_content_id`, `short_description`, `description`, `model_name`, `sku`, `gtin`, `release_date`, `spec_snapshot_json`, `comparison_snapshot_json` são opcionais.
|
|
194
|
-
- **Integridade:** FK para `catalog_brand`, `category`, `content`.
|
|
195
|
-
- **Índices:**
|
|
196
|
-
- Único em `slug`
|
|
197
|
-
- Índices em `category_id` + `status`, `brand_id` + `status`, `primary_content_id`, `is_active`
|
|
198
|
-
|
|
199
|
-
---
|
|
200
|
-
|
|
201
|
-
### catalog_product_image
|
|
202
|
-
|
|
203
|
-
- **Finalidade:** Armazena imagens associadas a produtos.
|
|
204
|
-
- **Colunas principais:**
|
|
205
|
-
- `id` (PK)
|
|
206
|
-
- `product_id` (FK para `catalog_product`)
|
|
207
|
-
- `file_id` (FK para `file`)
|
|
208
|
-
- `role` (enum: `primary`, `gallery`, `thumbnail`, `lifestyle`, `technical`, default `gallery`)
|
|
209
|
-
- `sort_order` (int, default 0)
|
|
210
|
-
- `is_primary` (boolean, default false)
|
|
211
|
-
- `alt_text` (varchar 255, nullable)
|
|
212
|
-
- `created_at`, `updated_at` (timestamps)
|
|
213
|
-
- **Defaults:** Conforme descrito acima.
|
|
214
|
-
- **Nulabilidade:** `alt_text` é opcional.
|
|
215
|
-
- **Integridade:** FK para `catalog_product` e `file`.
|
|
216
|
-
- **Índices:**
|
|
217
|
-
- Índices em `product_id` + `sort_order`, `product_id` + `is_primary`, `file_id`
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
### catalog_brand
|
|
222
|
-
|
|
223
|
-
- **Finalidade:** Armazena marcas do catálogo.
|
|
224
|
-
- **Colunas principais:**
|
|
225
|
-
- `id` (PK)
|
|
226
|
-
- `slug` (varchar 255, único)
|
|
227
|
-
- `name` (varchar 255)
|
|
228
|
-
- `normalized_name` (varchar 255)
|
|
229
|
-
- `logo_file_id` (FK para `file`, nullable)
|
|
230
|
-
- `status` (enum: `active`, `inactive`, default `active`)
|
|
231
|
-
- `website_url` (varchar 500, nullable)
|
|
232
|
-
- `created_at`, `updated_at` (timestamps)
|
|
233
|
-
- **Defaults:** Conforme descrito acima.
|
|
234
|
-
- **Nulabilidade:** `logo_file_id` e `website_url` são opcionais.
|
|
235
|
-
- **Integridade:** FK para `file`.
|
|
236
|
-
- **Índices:**
|
|
237
|
-
- Único em `slug`
|
|
238
|
-
- Índices em `normalized_name`, `logo_file_id`, `status`
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
### catalog_offer
|
|
243
|
-
|
|
244
|
-
- **Finalidade:** Armazena ofertas vinculadas a produtos e comerciantes.
|
|
245
|
-
- **Colunas principais:**
|
|
246
|
-
- `id` (PK)
|
|
247
|
-
- `product_id` (FK para `catalog_product`)
|
|
248
|
-
- `merchant_id` (FK para `catalog_merchant`)
|
|
249
|
-
- `affiliate_program_id` (FK para `catalog_affiliate_program`, nullable)
|
|
250
|
-
- `site_id` (FK para `catalog_site`, nullable)
|
|
251
|
-
- `external_offer_id` (varchar 255)
|
|
252
|
-
- `title` (varchar 255)
|
|
253
|
-
- `price_amount` (decimal 14,2)
|
|
254
|
-
- `price_currency` (varchar 3)
|
|
255
|
-
- `original_price_amount` (decimal 14,2, nullable)
|
|
256
|
-
- `installment_json` (json, nullable)
|
|
257
|
-
- `availability_status` (enum: `in_stock`, `out_of_stock`, `pre_order`, `unknown`, default `unknown`)
|
|
258
|
-
- `affiliate_url` (varchar 500, nullable)
|
|
259
|
-
- `deep_link_url` (varchar 500, nullable)
|
|
260
|
-
- `priority_score` (int, default 0)
|
|
261
|
-
- `is_featured` (boolean, default false)
|
|
262
|
-
- `valid_from` (datetime, nullable)
|
|
263
|
-
- `valid_until` (datetime, nullable)
|
|
264
|
-
- `last_seen_at` (datetime, nullable)
|
|
265
|
-
- `created_at`, `updated_at` (timestamps)
|
|
266
|
-
- **Defaults:** Conforme descrito acima.
|
|
267
|
-
- **Nulabilidade:** Campos como `affiliate_program_id`, `site_id`, `original_price_amount`, `installment_json`, `affiliate_url`, `deep_link_url`, `valid_from`, `valid_until`, `last_seen_at` são opcionais.
|
|
268
|
-
- **Integridade:** FK para `catalog_product`, `catalog_merchant`, `catalog_affiliate_program`, `catalog_site`.
|
|
269
|
-
- **Índices:**
|
|
270
|
-
- Único em `product_id`, `merchant_id`, `external_offer_id`
|
|
271
|
-
- Índices em `product_id`, `availability_status`, `price_amount`
|
|
272
|
-
- Índices em `site_id`, `is_featured`
|
|
273
|
-
|
|
274
|
-
---
|
|
275
|
-
|
|
276
|
-
### catalog_affiliate_program
|
|
277
|
-
|
|
278
|
-
- **Finalidade:** Armazena programas de afiliados vinculados a comerciantes.
|
|
279
|
-
- **Colunas principais:**
|
|
280
|
-
- `id` (PK)
|
|
281
|
-
- `merchant_id` (FK para `catalog_merchant`, nullable)
|
|
282
|
-
- `slug` (varchar 255, único)
|
|
283
|
-
- `name` (varchar 255)
|
|
284
|
-
- `network_type` (enum: `amazon`, `mercado_livre`, `aliexpress`, `kabum`, `direct`, `network`, default `direct`)
|
|
285
|
-
- `tracking_template` (text, nullable)
|
|
286
|
-
- `commission_type` (enum: `percentage`, `fixed`, default `percentage`)
|
|
287
|
-
- `default_commission_value` (decimal 8,2, default 0)
|
|
288
|
-
- `status` (enum: `active`, `inactive`, default `active`)
|
|
289
|
-
- `created_at`, `updated_at` (timestamps)
|
|
290
|
-
- **Defaults:** Conforme descrito acima.
|
|
291
|
-
- **Nulabilidade:** `merchant_id` e `tracking_template` são opcionais.
|
|
292
|
-
- **Integridade:** FK para `catalog_merchant`.
|
|
293
|
-
- **Índices:**
|
|
294
|
-
- Único em `slug`
|
|
295
|
-
- Índices em `merchant_id`, `status`
|
|
296
|
-
|
|
297
|
-
---
|
|
298
|
-
|
|
299
|
-
*(Outras tabelas seguem padrão similar, com chaves primárias, estrangeiras, enums e índices conforme arquivos YAML fornecidos.)*
|
|
300
|
-
|
|
301
|
-
## 8. Regras de negócio relevantes
|
|
302
|
-
|
|
303
|
-
- O módulo suporta múltiplos recursos configurados dinamicamente via `catalogResourceMap`.
|
|
304
|
-
- Campos de busca e filtro são configurados por recurso para permitir consultas eficientes.
|
|
305
|
-
- Campos booleanos e numéricos são normalizados automaticamente no payload (ex: strings `"true"`, `"false"` e números em string são convertidos).
|
|
306
|
-
- Exclusão e atualização verificam existência prévia do registro, retornando erro 404 se não encontrado.
|
|
307
|
-
- Estatísticas incluem contagem total e contagem por status ativo quando aplicável.
|
|
308
|
-
- Imagens de produtos são ordenadas por `sort_order` e podem ter uma imagem primária.
|
|
309
|
-
- O sistema suporta múltiplos idiomas via parâmetro `locale` (header `Accept-Language`).
|
|
310
|
-
- Autorização é restrita a usuários com papéis `admin` e `admin-catalog`.
|
|
311
|
-
|
|
312
|
-
## 9. Guia rápido de uso (exemplos)
|
|
313
|
-
|
|
314
|
-
### Listar produtos com paginação e filtro por status
|
|
315
|
-
|
|
316
|
-
```http
|
|
317
|
-
GET /catalog/product?page=1&limit=10&status=published
|
|
318
|
-
Authorization: Bearer <token>
|
|
319
|
-
Accept-Language: pt-BR
|
|
320
|
-
```
|
|
3
|
+
## Overview
|
|
321
4
|
|
|
322
|
-
|
|
5
|
+
`@hed-hog/catalog` is the central catalog module for HedHog. It manages:
|
|
323
6
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
7
|
+
- catalog categories
|
|
8
|
+
- brands, merchants, sites, offers, and products
|
|
9
|
+
- structured technical attributes by category
|
|
10
|
+
- product attribute values with typed storage
|
|
11
|
+
- product images and comparison-oriented payloads
|
|
329
12
|
|
|
330
|
-
|
|
331
|
-
"slug": "nova-marca",
|
|
332
|
-
"name": "Nova Marca",
|
|
333
|
-
"status": "active",
|
|
334
|
-
"website_url": "https://novamarca.com"
|
|
335
|
-
}
|
|
336
|
-
```
|
|
13
|
+
The module keeps the existing generic CRUD backbone, pagination, locale, roles, Prisma integration, and admin experience. The main architectural change is that structured attribute tables are now the primary source of truth for technical metadata, while legacy JSON snapshots remain secondary and derived.
|
|
337
14
|
|
|
338
|
-
|
|
15
|
+
## Core architecture
|
|
339
16
|
|
|
340
|
-
|
|
341
|
-
PATCH /catalog/product/123
|
|
342
|
-
Authorization: Bearer <token>
|
|
343
|
-
Content-Type: application/json
|
|
344
|
-
Accept-Language: pt-BR
|
|
17
|
+
### Primary domain entities
|
|
345
18
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
19
|
+
- `catalog_category`
|
|
20
|
+
Product taxonomy used by the catalog itself.
|
|
21
|
+
- `catalog_attribute`
|
|
22
|
+
Master definition of a technical/comparable attribute.
|
|
23
|
+
- `catalog_attribute_option`
|
|
24
|
+
Enumerated values for `option` attributes.
|
|
25
|
+
- `catalog_category_attribute`
|
|
26
|
+
Rules that define which attributes belong to a category and how they behave in UI/comparison.
|
|
27
|
+
- `catalog_product_attribute_value`
|
|
28
|
+
Typed value storage for product attributes.
|
|
29
|
+
- `catalog_product`
|
|
30
|
+
Main product record with brand, category, content, status, and secondary snapshots.
|
|
351
31
|
|
|
352
|
-
###
|
|
32
|
+
### Supporting entities
|
|
353
33
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
34
|
+
- `catalog_brand`
|
|
35
|
+
Supports `logo_file_id` via the shared `file` module.
|
|
36
|
+
- `catalog_site`
|
|
37
|
+
Supports `logo_file_id` and site-specific JSON settings.
|
|
38
|
+
- `catalog_merchant`
|
|
39
|
+
Supports `logo_file_id` when merchant branding is needed.
|
|
40
|
+
- `catalog_product_image`
|
|
41
|
+
Product gallery/media records via `file_id`.
|
|
42
|
+
- `catalog_offer`, `catalog_affiliate_program`, `catalog_product_site`, `catalog_comparison`, and related tables
|
|
43
|
+
Commercial/publication/comparison layers that build on top of products and attributes.
|
|
359
44
|
|
|
360
|
-
|
|
45
|
+
## Structured attributes vs JSON snapshots
|
|
361
46
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
47
|
+
### Primary data
|
|
48
|
+
|
|
49
|
+
The canonical source for technical product data is:
|
|
50
|
+
|
|
51
|
+
1. `catalog_category`
|
|
52
|
+
2. `catalog_attribute`
|
|
53
|
+
3. `catalog_category_attribute`
|
|
54
|
+
4. `catalog_product_attribute_value`
|
|
55
|
+
|
|
56
|
+
This is what enables:
|
|
57
|
+
|
|
58
|
+
- consistent admin forms
|
|
59
|
+
- typed validation
|
|
60
|
+
- category-aware required fields
|
|
61
|
+
- filters and sorting by normalized values
|
|
62
|
+
- comparison payload generation
|
|
63
|
+
|
|
64
|
+
### Secondary data
|
|
65
|
+
|
|
66
|
+
`catalog_product.spec_snapshot_json` and `catalog_product.comparison_snapshot_json` are still present for compatibility and fast reads.
|
|
67
|
+
|
|
68
|
+
Important rules:
|
|
69
|
+
|
|
70
|
+
- they are **not** the primary source of truth anymore
|
|
71
|
+
- they should be treated as **derived/cache fields**
|
|
72
|
+
- they are materialized from structured attributes
|
|
73
|
+
- old data should not be deleted without a migration plan
|
|
74
|
+
|
|
75
|
+
The service now exposes snapshot materialization helpers and keeps snapshots warm after attribute updates.
|
|
76
|
+
|
|
77
|
+
## Main tables
|
|
78
|
+
|
|
79
|
+
### `catalog_category`
|
|
80
|
+
|
|
81
|
+
Suggested use:
|
|
82
|
+
|
|
83
|
+
- create taxonomy nodes like `gpu`, `cpu`, `motherboard`, `ram`, `ssd`, `monitor`
|
|
84
|
+
- optionally organize them with `parent_category_id`
|
|
85
|
+
- control whether the category participates in comparison with `comparison_enabled`
|
|
86
|
+
|
|
87
|
+
Key fields:
|
|
88
|
+
|
|
89
|
+
- `slug`
|
|
90
|
+
- `name`
|
|
91
|
+
- `description`
|
|
92
|
+
- `parent_category_id`
|
|
93
|
+
- `comparison_enabled`
|
|
94
|
+
- `status`
|
|
95
|
+
- `sort_order`
|
|
96
|
+
|
|
97
|
+
### `catalog_attribute`
|
|
98
|
+
|
|
99
|
+
Defines the technical vocabulary shared across categories.
|
|
100
|
+
|
|
101
|
+
Key fields:
|
|
102
|
+
|
|
103
|
+
- `code`
|
|
104
|
+
- `slug`
|
|
105
|
+
- `name`
|
|
106
|
+
- `description`
|
|
107
|
+
- `data_type`
|
|
108
|
+
- `unit`
|
|
109
|
+
- `group_name`
|
|
110
|
+
- `comparison_mode`
|
|
111
|
+
- `is_filterable`
|
|
112
|
+
- `is_sortable`
|
|
113
|
+
- `is_comparable`
|
|
114
|
+
- `is_required_default`
|
|
115
|
+
- `status`
|
|
116
|
+
- `display_order`
|
|
117
|
+
|
|
118
|
+
Supported `data_type` values:
|
|
119
|
+
|
|
120
|
+
- `text`
|
|
121
|
+
- `long_text`
|
|
122
|
+
- `number`
|
|
123
|
+
- `boolean`
|
|
124
|
+
- `option`
|
|
125
|
+
|
|
126
|
+
### `catalog_attribute_option`
|
|
127
|
+
|
|
128
|
+
Used when `catalog_attribute.data_type = option`.
|
|
129
|
+
|
|
130
|
+
Key fields:
|
|
131
|
+
|
|
132
|
+
- `attribute_id`
|
|
133
|
+
- `slug`
|
|
134
|
+
- `label`
|
|
135
|
+
- `option_value`
|
|
136
|
+
- `normalized_value`
|
|
137
|
+
- `sort_order`
|
|
138
|
+
- `status`
|
|
139
|
+
- `is_default`
|
|
140
|
+
|
|
141
|
+
### `catalog_category_attribute`
|
|
142
|
+
|
|
143
|
+
Binds attributes to categories and controls behavior.
|
|
144
|
+
|
|
145
|
+
Key fields:
|
|
146
|
+
|
|
147
|
+
- `catalog_category_id`
|
|
148
|
+
- `attribute_id`
|
|
149
|
+
- `is_required`
|
|
150
|
+
- `is_highlight`
|
|
151
|
+
- `is_filter_visible`
|
|
152
|
+
- `is_comparison_visible`
|
|
153
|
+
- `sort_order`
|
|
154
|
+
- `weight`
|
|
155
|
+
- `facet_mode`
|
|
156
|
+
|
|
157
|
+
### `catalog_product_attribute_value`
|
|
158
|
+
|
|
159
|
+
Stores one typed value per `product_id + attribute_id`.
|
|
160
|
+
|
|
161
|
+
Key fields:
|
|
162
|
+
|
|
163
|
+
- `product_id`
|
|
164
|
+
- `attribute_id`
|
|
165
|
+
- `attribute_option_id`
|
|
166
|
+
- `value_text`
|
|
167
|
+
- `value_number`
|
|
168
|
+
- `value_boolean`
|
|
169
|
+
- `raw_value`
|
|
170
|
+
- `value_unit`
|
|
171
|
+
- `normalized_value`
|
|
172
|
+
- `normalized_text`
|
|
173
|
+
- `normalized_number`
|
|
174
|
+
- `source_type`
|
|
175
|
+
- `confidence_score`
|
|
176
|
+
- `is_verified`
|
|
177
|
+
|
|
178
|
+
## Attribute value rules
|
|
179
|
+
|
|
180
|
+
Only one value channel should be used per attribute value row:
|
|
181
|
+
|
|
182
|
+
- `text` and `long_text` use `value_text`
|
|
183
|
+
- `number` uses `value_number`
|
|
184
|
+
- `boolean` uses `value_boolean`
|
|
185
|
+
- `option` uses `attribute_option_id`
|
|
186
|
+
|
|
187
|
+
The backend validates this and rejects inconsistent payloads.
|
|
188
|
+
|
|
189
|
+
## Initial catalog seeds
|
|
190
|
+
|
|
191
|
+
The module now ships declarative YAML seeds in `libraries/catalog/hedhog/data`.
|
|
192
|
+
|
|
193
|
+
Seeded categories:
|
|
194
|
+
|
|
195
|
+
- `gpu`
|
|
196
|
+
- `cpu`
|
|
197
|
+
- `motherboard`
|
|
198
|
+
- `ram`
|
|
199
|
+
- `ssd`
|
|
200
|
+
- `monitor`
|
|
201
|
+
|
|
202
|
+
Seeded example attributes:
|
|
203
|
+
|
|
204
|
+
### GPU
|
|
205
|
+
|
|
206
|
+
- `chipset`
|
|
207
|
+
- `vram_gb`
|
|
208
|
+
- `memory_type`
|
|
209
|
+
- `memory_bus_bits`
|
|
210
|
+
- `boost_clock_mhz`
|
|
211
|
+
- `tdp_w`
|
|
212
|
+
- `length_mm`
|
|
213
|
+
- `ray_tracing`
|
|
214
|
+
|
|
215
|
+
### CPU
|
|
216
|
+
|
|
217
|
+
- `cores`
|
|
218
|
+
- `threads`
|
|
219
|
+
- `base_clock_ghz`
|
|
220
|
+
- `boost_clock_ghz`
|
|
221
|
+
- `socket`
|
|
222
|
+
- `tdp_w`
|
|
223
|
+
- `integrated_graphics`
|
|
224
|
+
|
|
225
|
+
Seeded example options:
|
|
367
226
|
|
|
368
|
-
|
|
227
|
+
- GPU memory types like `GDDR6`, `GDDR6X`, `GDDR7`, `HBM3`, `HBM3E`
|
|
228
|
+
- CPU sockets like `AM4`, `AM5`, `LGA1700`, `LGA1851`, `sTR5`
|
|
369
229
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
230
|
+
## Admin resources
|
|
231
|
+
|
|
232
|
+
The catalog admin supports generic CRUD plus specialized product attribute editing.
|
|
233
|
+
|
|
234
|
+
Main resources:
|
|
235
|
+
|
|
236
|
+
- `categories`
|
|
237
|
+
- `brands`
|
|
238
|
+
- `sites`
|
|
239
|
+
- `products`
|
|
240
|
+
- `attribute-groups`
|
|
241
|
+
- `attributes`
|
|
242
|
+
- `attribute-options`
|
|
243
|
+
- `category-attributes`
|
|
244
|
+
- `product-attributes`
|
|
245
|
+
- `comparisons`
|
|
246
|
+
- `offers`
|
|
247
|
+
- `merchants`
|
|
248
|
+
- `affiliate-programs`
|
|
249
|
+
- `seo-rules`
|
|
250
|
+
- `import-sources`
|
|
251
|
+
|
|
252
|
+
### Product admin experience
|
|
253
|
+
|
|
254
|
+
When editing a product:
|
|
255
|
+
|
|
256
|
+
1. select `catalog_category_id`
|
|
257
|
+
2. the admin loads category attributes dynamically
|
|
258
|
+
3. fields are grouped by attribute group or `group_name`
|
|
259
|
+
4. typed inputs are rendered for `text`, `long_text`, `number`, `boolean`, and `option`
|
|
260
|
+
5. required fields are validated before save
|
|
261
|
+
6. values are persisted into `catalog_product_attribute_value`
|
|
262
|
+
|
|
263
|
+
The product screen also shows:
|
|
264
|
+
|
|
265
|
+
- brand
|
|
266
|
+
- category
|
|
267
|
+
- status and active flag
|
|
268
|
+
- grouped technical attributes
|
|
269
|
+
- product images as a secondary block
|
|
270
|
+
- snapshots as secondary/derived data
|
|
271
|
+
|
|
272
|
+
## Logos and files
|
|
273
|
+
|
|
274
|
+
The module uses the shared `file` flow already present in the monorepo.
|
|
275
|
+
|
|
276
|
+
### Brand logo
|
|
277
|
+
|
|
278
|
+
Use `catalog_brand.logo_file_id`.
|
|
279
|
+
|
|
280
|
+
### Site logo
|
|
281
|
+
|
|
282
|
+
Use `catalog_site.logo_file_id`.
|
|
283
|
+
|
|
284
|
+
### Merchant logo
|
|
285
|
+
|
|
286
|
+
Use `catalog_merchant.logo_file_id`.
|
|
287
|
+
|
|
288
|
+
### Product gallery
|
|
289
|
+
|
|
290
|
+
Use `catalog_product_image.file_id`.
|
|
291
|
+
|
|
292
|
+
No custom upload mechanism was introduced. The admin uses the same file upload pattern already used across the project.
|
|
293
|
+
|
|
294
|
+
## Runtime helpers
|
|
295
|
+
|
|
296
|
+
`CatalogService` now includes helpers for:
|
|
297
|
+
|
|
298
|
+
- listing category attributes
|
|
299
|
+
- listing product attributes
|
|
300
|
+
- building a structured product payload
|
|
301
|
+
- building a comparison payload from structured attributes
|
|
302
|
+
- materializing `spec_snapshot_json` and `comparison_snapshot_json`
|
|
303
|
+
|
|
304
|
+
Relevant endpoints:
|
|
305
|
+
|
|
306
|
+
- `GET /catalog/categories/tree`
|
|
307
|
+
- `GET /catalog/categories/:id/attributes`
|
|
308
|
+
- `GET /catalog/products/:id/attributes`
|
|
309
|
+
- `PUT /catalog/products/:id/attributes`
|
|
310
|
+
- `GET /catalog/products/:id/structured`
|
|
311
|
+
- `GET /catalog/products/:id/comparison-payload`
|
|
312
|
+
- `POST /catalog/products/:id/materialize-snapshots`
|
|
313
|
+
- generic CRUD under `/catalog/:resource`
|
|
314
|
+
|
|
315
|
+
## Safe transition strategy
|
|
316
|
+
|
|
317
|
+
The module preserves snapshot fields to avoid destructive migration behavior.
|
|
318
|
+
|
|
319
|
+
Current strategy:
|
|
320
|
+
|
|
321
|
+
- structured attribute tables are canonical
|
|
322
|
+
- snapshots remain readable
|
|
323
|
+
- snapshots are regenerated from structured values
|
|
324
|
+
- attribute updates trigger snapshot materialization
|
|
325
|
+
- compatibility aliases are still accepted in a few payloads where the old UI/model used different names
|
|
326
|
+
|
|
327
|
+
This keeps the module safe for gradual adoption while avoiding JSON-first modeling going forward.
|
|
328
|
+
|
|
329
|
+
## How to apply schema and seeds
|
|
330
|
+
|
|
331
|
+
This repository follows a database-first HedHog workflow.
|
|
332
|
+
|
|
333
|
+
### Schema/data apply
|
|
334
|
+
|
|
335
|
+
```powershell
|
|
336
|
+
hedhog dev apply
|
|
337
|
+
pnpm db:update
|
|
374
338
|
```
|
|
375
339
|
|
|
376
|
-
|
|
340
|
+
`hedhog dev apply` applies table YAML and the declarative data under `hedhog/data`, so the catalog seed data lives in the same standard workflow as the rest of the project.
|
|
377
341
|
|
|
378
|
-
|
|
342
|
+
### Build checks
|
|
343
|
+
|
|
344
|
+
```powershell
|
|
345
|
+
pnpm --filter api build
|
|
346
|
+
pnpm --filter @hed-hog/catalog build
|
|
347
|
+
pnpm --filter admin build
|
|
379
348
|
```
|
|
349
|
+
|
|
350
|
+
## Example flows
|
|
351
|
+
|
|
352
|
+
### 1. Create a brand with logo
|
|
353
|
+
|
|
354
|
+
1. Open `Catalog > Brands`
|
|
355
|
+
2. Create a brand with `name`, `slug`, and optional `website_url`
|
|
356
|
+
3. Upload the logo through the shared upload field
|
|
357
|
+
4. Save, which stores the uploaded file in `logo_file_id`
|
|
358
|
+
|
|
359
|
+
### 2. Create a category
|
|
360
|
+
|
|
361
|
+
1. Open `Catalog > Categories`
|
|
362
|
+
2. Create `gpu`-like or business-specific categories
|
|
363
|
+
3. Configure `comparison_enabled`, hierarchy, and status
|
|
364
|
+
|
|
365
|
+
### 3. Create an attribute
|
|
366
|
+
|
|
367
|
+
1. Open `Catalog > Attributes`
|
|
368
|
+
2. Define `code`, `slug`, `name`, `data_type`, and `group_name`
|
|
369
|
+
3. Mark whether the attribute is filterable/sortable/comparable
|
|
370
|
+
|
|
371
|
+
### 4. Link an attribute to a category
|
|
372
|
+
|
|
373
|
+
1. Open `Catalog > Category Attributes`
|
|
374
|
+
2. Pick the category and the attribute
|
|
375
|
+
3. Configure required/highlight/filter/comparison behavior
|
|
376
|
+
4. Save ordering and facet mode
|
|
377
|
+
|
|
378
|
+
### 5. Create a product
|
|
379
|
+
|
|
380
|
+
1. Open `Catalog > Products`
|
|
381
|
+
2. Fill the base product fields
|
|
382
|
+
3. Choose brand and category
|
|
383
|
+
4. Save the product
|
|
384
|
+
|
|
385
|
+
### 6. Save product technical attributes
|
|
386
|
+
|
|
387
|
+
1. In the same product sheet, after category selection, the technical fields load automatically
|
|
388
|
+
2. Fill typed values
|
|
389
|
+
3. Save the product
|
|
390
|
+
4. The admin persists values to `catalog_product_attribute_value`
|
|
391
|
+
5. Snapshots are materialized as derived JSON
|
|
392
|
+
|
|
393
|
+
## Starting with GPU and CPU
|
|
394
|
+
|
|
395
|
+
The seeded data is enough to bootstrap a real first catalog slice:
|
|
396
|
+
|
|
397
|
+
1. create brands like `NVIDIA`, `AMD`, `Intel`
|
|
398
|
+
2. create products under `gpu` and `cpu`
|
|
399
|
+
3. fill structured attributes
|
|
400
|
+
4. optionally attach product images
|
|
401
|
+
5. use comparison payload endpoints to power future storefront or editorial flows
|
|
402
|
+
|
|
403
|
+
## Future improvements
|
|
404
|
+
|
|
405
|
+
- automatic snapshot materialization on more product lifecycle events
|
|
406
|
+
- richer import pipelines that map external payloads directly to structured attributes
|
|
407
|
+
- category-specific admin presets and product templates
|
|
408
|
+
- storefront queries optimized for faceting and comparison at scale
|
|
409
|
+
- deeper read models for SEO, rankings, and recommendation features
|