@hed-hog/catalog 0.0.294 → 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.
Files changed (55) hide show
  1. package/README.md +409 -409
  2. package/dist/catalog.service.d.ts.map +1 -1
  3. package/dist/catalog.service.js +14 -6
  4. package/dist/catalog.service.js.map +1 -1
  5. package/hedhog/data/catalog_attribute.yaml +202 -202
  6. package/hedhog/data/catalog_attribute_option.yaml +109 -109
  7. package/hedhog/data/catalog_category.yaml +47 -47
  8. package/hedhog/data/catalog_category_attribute.yaml +209 -209
  9. package/hedhog/data/menu.yaml +133 -133
  10. package/hedhog/data/role.yaml +7 -7
  11. package/hedhog/data/route.yaml +72 -72
  12. package/hedhog/frontend/app/[resource]/page.tsx.ejs +391 -391
  13. package/hedhog/frontend/app/_components/catalog-ai-form-assist-dialog.tsx.ejs +340 -340
  14. package/hedhog/frontend/app/_components/catalog-resource-form-sheet.tsx.ejs +907 -907
  15. package/hedhog/frontend/app/_lib/catalog-resources.tsx.ejs +929 -929
  16. package/hedhog/frontend/app/dashboard/page.tsx.ejs +14 -83
  17. package/hedhog/frontend/messages/en.json +389 -389
  18. package/hedhog/frontend/messages/pt.json +389 -389
  19. package/hedhog/table/catalog_affiliate_program.yaml +41 -41
  20. package/hedhog/table/catalog_attribute.yaml +67 -67
  21. package/hedhog/table/catalog_attribute_group.yaml +18 -18
  22. package/hedhog/table/catalog_attribute_option.yaml +40 -40
  23. package/hedhog/table/catalog_brand.yaml +34 -34
  24. package/hedhog/table/catalog_category.yaml +40 -40
  25. package/hedhog/table/catalog_category_attribute.yaml +37 -37
  26. package/hedhog/table/catalog_click_event.yaml +50 -50
  27. package/hedhog/table/catalog_comparison.yaml +19 -19
  28. package/hedhog/table/catalog_comparison_highlight.yaml +39 -39
  29. package/hedhog/table/catalog_comparison_item.yaml +30 -30
  30. package/hedhog/table/catalog_content_relation.yaml +42 -42
  31. package/hedhog/table/catalog_import_run.yaml +33 -33
  32. package/hedhog/table/catalog_import_source.yaml +24 -24
  33. package/hedhog/table/catalog_merchant.yaml +29 -29
  34. package/hedhog/table/catalog_offer.yaml +83 -83
  35. package/hedhog/table/catalog_price_history.yaml +34 -34
  36. package/hedhog/table/catalog_product.yaml +30 -30
  37. package/hedhog/table/catalog_product_attribute_value.yaml +44 -44
  38. package/hedhog/table/catalog_product_category.yaml +13 -13
  39. package/hedhog/table/catalog_product_image.yaml +34 -34
  40. package/hedhog/table/catalog_product_score.yaml +38 -38
  41. package/hedhog/table/catalog_product_site.yaml +47 -47
  42. package/hedhog/table/catalog_product_tag.yaml +19 -19
  43. package/hedhog/table/catalog_score_criterion.yaml +42 -42
  44. package/hedhog/table/catalog_seo_page_rule.yaml +10 -10
  45. package/hedhog/table/catalog_similarity_rule.yaml +33 -33
  46. package/hedhog/table/catalog_site.yaml +21 -21
  47. package/hedhog/table/catalog_site_category.yaml +12 -12
  48. package/package.json +6 -6
  49. package/src/catalog-resource.config.ts +132 -132
  50. package/src/catalog.controller.ts +91 -91
  51. package/src/catalog.module.ts +16 -16
  52. package/src/catalog.service.ts +1585 -1573
  53. package/src/index.ts +1 -1
  54. package/src/language/en.json +4 -4
  55. package/src/language/pt.json +4 -4
package/README.md CHANGED
@@ -1,409 +1,409 @@
1
- # @hed-hog/catalog
2
-
3
- ## Overview
4
-
5
- `@hed-hog/catalog` is the central catalog module for HedHog. It manages:
6
-
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
12
-
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.
14
-
15
- ## Core architecture
16
-
17
- ### Primary domain entities
18
-
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.
31
-
32
- ### Supporting entities
33
-
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.
44
-
45
- ## Structured attributes vs JSON snapshots
46
-
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:
226
-
227
- - GPU memory types like `GDDR6`, `GDDR6X`, `GDDR7`, `HBM3`, `HBM3E`
228
- - CPU sockets like `AM4`, `AM5`, `LGA1700`, `LGA1851`, `sTR5`
229
-
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
338
- ```
339
-
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.
341
-
342
- ### Build checks
343
-
344
- ```powershell
345
- pnpm --filter api build
346
- pnpm --filter @hed-hog/catalog build
347
- pnpm --filter admin build
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
1
+ # @hed-hog/catalog
2
+
3
+ ## Overview
4
+
5
+ `@hed-hog/catalog` is the central catalog module for HedHog. It manages:
6
+
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
12
+
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.
14
+
15
+ ## Core architecture
16
+
17
+ ### Primary domain entities
18
+
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.
31
+
32
+ ### Supporting entities
33
+
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.
44
+
45
+ ## Structured attributes vs JSON snapshots
46
+
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:
226
+
227
+ - GPU memory types like `GDDR6`, `GDDR6X`, `GDDR7`, `HBM3`, `HBM3E`
228
+ - CPU sockets like `AM4`, `AM5`, `LGA1700`, `LGA1851`, `sTR5`
229
+
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
338
+ ```
339
+
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.
341
+
342
+ ### Build checks
343
+
344
+ ```powershell
345
+ pnpm --filter api build
346
+ pnpm --filter @hed-hog/catalog build
347
+ pnpm --filter admin build
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