Repository navigation
fix(web,importer): per-category spec templates for earbuds and watches (#1142) - #1163
Conversation
#1142) Earbuds rendered with the phone spec template, filled by the AI enricher with a 1 inch screen, 1GB RAM, 64GB ROM and microphones as cameras; a cellular Apple Watch showed 5G. - @repo/shared-data/rules: getCategorySpecTemplate / applyCategorySpecTemplate list the groups, fields, key_specs and flat columns each category has. Earbuds: audio, battery, connectivity, design, availability, regulatory. Smartwatches: no camera, no 5G. Laptops: no 5G. Phones and tablets keep all ten groups. - Product page: groups render in the category's order with renumbered headings and a matching group count; the memory and camera tiles and rows a category lacks are gone. The page, its peers, catalogue cards and home cards all read the product through the template. - Importer: the enricher result and every master insert/update go through the template, so new and re-enriched masters stop storing phone fields for earbuds and 5G for watches. The prompt says so too. - Tests: shared-data per-category templates, web headings and wiring, EntityMatcher writes, and a hermetic E2E with a phone-shaped seeded bud. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.
Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. Walkthrough
Merge Risk:
|
…-fe031c # Conflicts: # apps/web/src/app/[locale]/smartphones/page.tsx
- Watches: hiding 5G is display-only. The importer keeps a stored 5G flag and 5G bands (a few watches carry RedCap 5G) and drops only the camera. The bands row stays on watches as "4G bands". - Earbuds keep the hardware group with the chipset row only, so the audio chip (H2, BES2700) survives writes and the chip tile has a fallback. - Laptops stay on the full template (some ship with 5G modems). - Re-enrichment scopes the write by the stored category only, never by a guess from the title. - Watches keep key_specs.ram; the generic's narrowing is documented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-fe031c # Conflicts: # apps/web/package.json # apps/web/src/app/[locale]/page.tsx
…-fe031c # Conflicts: # apps/web/package.json # apps/web/src/app/[locale]/smartphones/[slug]/page.tsx # packages/shared-data/src/__tests__/shared-data.test.ts # packages/shared-data/src/rules/index.ts
Closes #1142
What changes for users
/earbuds/<slug>): the spec section opens on "1. Audio & multimedia" and shows 7 groups (audio, battery, connectivity, hardware with the chipset row only, design, availability, regulatory). No screen size, refresh rate, ppi, RAM, ROM, CPU, GPU, OS, camera or cellular rows. The "Memory (RAM/ROM)" tile is gone; the first tile reads "Driver" (was "Driver / Display"). Cards on/earbudsand the home page no longer carry "xGB RAM · yGB".Root cause and the writer
The bad values are written by this repo's importer, not by Rate-Services code:
AiSpecEnricher(apps/importer/src/shopee/ai-spec-enricher.ts) asks the LLM for the phone-shaped ten-group schema for every category, andEntityMatcherstores the result (Tier 3 insert,enrichMasterProductByIdupdate). Rate-Services only runs this engine throughvendor/rate(ShopeeIngestionPipelineinsrc/index.ts); a read-only grep found no own writer ofram_gb/has_5g/ specs there. So the fix lands here and reaches the crawler on the nextvendor/ratebump. The:64gbmodel key on Buds3 Pro is the normalizer's title parse, tracked in #1123.How
@repo/shared-data/rules:getCategorySpecTemplate/applyCategorySpecTemplate/specFieldApplies: groups, hidden fields,key_specsand flat columns (ram_gb,storage_gb,screen_size_inch,has_5g) per category. Pure, idempotent, never mutates; unknown slugs keep the phone template. No imports, so the rules bundle stays browser-safe.forStorage), so new and re-enriched earbud masters stop storing screen, RAM, ROM, camera and cellular fields, and watches stop storing a camera. Hiding 5G on watches is display-only: a stored 5G flag is kept, since a few watches (Apple Watch Series 11, RedCap) really have 5G. Re-enrichment scopes the write by the stored category only, never by a guess from the title. The prompt now says earbuds have no display/RAM/camera and a cellular watch is LTE unless it really supports 5G.Tests
packages/shared-data: per-category templates (phones/tablets/unknown untouched, laptops, earbuds, smartwatches, idempotence, no mutation).apps/web/src/lib/__tests__/spec-template.test.ts: headings per category in vi/en/cs (earbuds 6 groups starting with audio, watches 9 groups with no "5G"), card scrub, and that the page/cards apply the template.apps/importerentity-matcher.test.ts[10/10]: earbud and watch re-enrich, earbud Tier 3 insert.category-spec-template.spec.tswith a seeded bud stored with phone-shaped specs (e2e-bud-002): page, card, and a watch-template page. Docker is not available on this machine, so this runs in CI only.tsc, eslint on all three packages: green.Data cleanup (proposal only, not run)
The display fix makes this optional; re-enrichment also cleans masters now. If JOY approves a one-off cleanup of stored rows (data, no DDL), the PO would run:
Check first with the same
WHEREas aSELECT count(*); rollback is a restore from aSELECT id, ram_gb, storage_gb, screen_size_inch, has_5g, specifications, key_specssnapshot taken before.Review
key_specs.ram; P3 the generic's narrowing is now documented. Not changed: P3 the template follows the route (as the page and breadcrumbs already do).has_5g=falseon real 5G watches and laptops (hiding 5G is now display-only); the watch bands row lost the 4G bands (now shown as "4G bands"). Residual, classed P2: a free-textcellular.technologysuch as "LTE / 5G" stored by the enricher would still render on a watch; the prompt change and the data cleanup above address that at the source.Not in scope (follow-ups)
/compare(CompareWorkspace.tsx) and the spec export toolbar still read raw stored fields.Screenshots
Before shots were taken from production (Buds3 Pro, AirPods Pro 3, Watch Ultra 2, /earbuds; 1280 and 390 wide) and are kept out of the repo. The PR's Vercel preview is behind SSO, so per the PO the evidence for this PR is the hermetic E2E (
category-spec-template.spec.ts), which seeds an earbud with the exact phone-shaped values from the issue and checks the DOM shows none of them, plus a 5G product read through the watch template. After screenshots of rate.vn will be added after the promote tomain.🤖 Generated with Claude Code
📌 TL;DR
This PR introduces a category-specific specification template system to ensure that product data (e.g., earbuds, smartwatches) only displays and stores relevant fields, preventing nonsensical data like "RAM" or "5G" from appearing on non-phone devices. It updates the AI enrichment pipeline, database write logic, and frontend rendering to filter out irrelevant specs based on the product's category.
🎯 Type of Change
🔍 Changes Walkthrough
apps/importer/src/shopee/__tests__/entity-matcher.test.tsEntityMatchercorrectly strips irrelevant fields (RAM, screen, 5G) for earbuds and watches during enrichment and creation.apps/importer/src/shopee/ai-spec-enricher.tsapplyCategorySpecTemplateto filter AI-generated specs. Updated AI prompt rules to explicitly instruct the model to omit fields not applicable to the device category.apps/importer/src/shopee/entity-matcher.tsapplyCategorySpecTemplateto both the initialmaster_productsinsert and the subsequent update operations to ensure database consistency with category-specific constraints.apps/web/e2e/category-spec-template.spec.tsapps/web/e2e/seed.sqlapps/web/locales/*.jsonquickSpecDriverto remove "Display" references, as earbuds do not have displays.apps/web/package.jsonspec-template.test.tsto the test execution pipeline.apps/web/src/app/[locale]/page.tsxapplyCategorySpecTemplateto product cards on the homepage to ensure quick-spec chips (RAM/Storage) are hidden for non-phone categories.apps/web/src/app/[locale]/smartphones/[slug]/page.tsxgetCategorySpecTemplate. Removed hardcoded spec groups and replaced them with conditional rendering logic that respects the category template.📊 Architectural Flow
flowchart TD A[AI Enricher] -->|Raw Specs| B{applyCategorySpecTemplate} C[Entity Matcher] -->|DB Write| B B -->|Filtered Specs| D[Database: master_products] D -->|Stored Data| E[Web Frontend] E -->|getCategorySpecTemplate| F{Render Logic} F -->|Category: Earbuds| G[Hide RAM, Screen, Camera] F -->|Category: Watch| H[Hide 5G, Camera] F -->|Category: Phone| I[Show All Specs]Summary by CodeRabbit