feat(product-center): implement Figma-designed UI, upload module, and website tests

- Redesign website homepage & product-center per Figma (fonts, logos, hero/footer/customer cases)
- Add API upload module (multer) with static serving for uploads/public assets
- Add OriginGood.delisted flag and SDS request retry logic
- Add admin ImageUpload component and goods import/upload flows
- Add vitest suite for website components and composables (32 tests)
- Add skills, docs, plans and PRODUCT.md
This commit is contained in:
yeuimu
2026-08-20 14:32:03 +08:00
parent b5fc88f3fa
commit 79fabd85f7
107 changed files with 5364 additions and 2601 deletions
@@ -0,0 +1,30 @@
# Homepage Navigation and Carousel Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make homepage CTAs navigate in the current page, automate homepage carousels, preserve the hero visual as a capped image, add product-center gutters, and temporarily hide two navigation menus.
**Architecture:** Keep behavior local to the existing Vue components. Use native current-page navigation for CTAs, lifecycle-managed timers for the step carousel, a duplicated CSS marquee for customer cases, and responsive CSS custom properties for product-center gutters.
**Tech Stack:** Nuxt 4, Vue 3 Composition API, Tailwind CSS, Vitest, Vue Test Utils
---
### Task 1: Lock navigation behavior
- [x] Add failing tests for current-page CTA and product-card navigation.
- [x] Replace new-tab links and inert CTA buttons with immediate current-page navigation.
- [x] Hide recommendation and solution navigation entries while retaining implementation code.
### Task 2: Automate homepage rotating content
- [x] Add failing timer tests for the four-step carousel.
- [x] Add a lifecycle-managed automatic step interval.
- [x] Animate the duplicated customer-case track continuously and add reduced-motion behavior.
### Task 3: Improve desktop layout
- [x] Keep `/主视觉.png` as an `<img>` with intrinsic aspect ratio and a capped rendered size.
- [x] Add responsive left and right gutters to the product center.
### Task 4: Verify and document
- [x] Update homepage, product-center, structure, and Agent reference docs.
- [x] Run website tests and production build.
- [x] Skip browser automation per the user's request; cover navigation, timing, hero image, country alignment, and bottom action behavior with Vitest.
@@ -0,0 +1,34 @@
# Product Center Backend Integration Implementation Plan
**Goal:** Make product-center countries, categories, tag filters, and their icons fully driven by backend configuration while matching the Figma assets.
**Architecture:** Store Figma-derived icons as versioned API static assets and update existing database records through an idempotent Prisma script. The website resolves backend-relative asset URLs against `backendUrl` and builds filter controls from backend tag-group membership rather than sort-order guesses or tag-name allowlists.
**Tech Stack:** NestJS, Prisma, PostgreSQL, Nuxt 4, Vue 3, Vitest, Jest.
---
### Task 1: Backend Icon Assets and Configuration Script
- [x] Export country and top-level category icons from Figma into `apps/api/public/product-center`.
- [x] Serve committed API assets under `/assets/`.
- [x] Add an idempotent Prisma script that updates matching country/category icon fields.
- [x] Run the script against the configured database and verify public API values.
### Task 2: Website Backend Asset Resolution
- [x] Add failing tests for absolute, data, and backend-relative icon URLs.
- [x] Implement URL resolution in the product-center data mapper.
- [x] Verify country and category components render API-provided icons.
### Task 3: Backend-Driven Tag Groups
- [x] Add failing component tests using backend group IDs/names.
- [x] Pass tag groups into `ProductTagFilter` and derive logistics/process groups from backend metadata.
- [x] Remove sort-order and tag-name allowlist assumptions.
### Task 4: End-to-End Verification
- [x] Run website and API tests/builds.
- [x] Verify public endpoints contain durable icon URLs.
- [x] Browser-test icons and filters on `/product-center`.
@@ -0,0 +1,75 @@
# 产品中心 Figma 还原功能规格
## 目标
将 Figma 文件“印美达官网 Copy”节点 `1124:1633` 的产品中心页面还原到 `apps/website``/product-center` 二级页。1440px 桌面视口以设计稿为唯一视觉基准,同时保留现有真实商品接口和完整筛选交互。
## 设计基准
- Figma 文件 key`c967cRetA5krsgM5jh0XPo`
- 节点:`1124:1633`
- 设计画布:1440 x 1602
- 桌面结构:80px 顶部导航、240px 左侧分类栏、1200px 主内容区。
- 主内容依次包含国家筛选、物流/工艺筛选与搜索、已选标签、四列商品网格和底部分页。
- 颜色、字号、间距、边框、圆角、图标尺寸、卡片比例和选中态按 Figma 设计上下文实现。
## 页面行为
### 默认状态
- 页面首次进入时呈现设计稿中的示例选择状态:女式服装、T恤、包邮、烫画。
- 国家筛选默认选中“全部”。
- 默认筛选必须映射到后端返回的真实分类和标签,不使用硬编码 ID;当目标项不存在时保持对应维度未选择,页面仍可正常加载。
### 筛选与搜索
- 分类、国家、物流、工艺、关键词搜索和已选标签删除继续驱动现有商品查询。
- 分类树支持展开与收起;桌面保持常驻侧栏。
- 国家选项显示接口返回的国旗和名称。
- 搜索支持按钮提交和回车提交。
- 改变筛选条件后回到第一页并刷新商品数据。
### 商品与分页
- 商品卡片展示真实商品图、名称、标签、国家和起售价。
- 1440px 视口每行展示四张卡片,尺寸与设计稿一致。
- 分页展示总数、页码、前后翻页、每页数量和页码跳转;所有操作使用真实分页状态。
- 加载、空数据和接口失败时保留明确且不破坏布局的反馈状态。
## 响应式规则
- 1440px 及常规桌面宽度优先保证设计稿还原精度。
- 平板和移动端将左侧分类栏折叠为可开关抽屉。
- 国家和筛选项允许横向浏览,不压缩到不可读尺寸。
- 商品网格根据容器宽度从四列降为三列、两列和单列。
- 移动端分页允许换行或精简页码,但保留总数、翻页与当前页信息。
- 任意支持宽度下不得出现文字、控件或商品卡片重叠。
## 组件边界
- `product-center.vue`:页面数据编排、默认筛选初始化和响应式侧栏状态。
- `ProductSidebar.vue`:桌面分类树与移动端抽屉内容。
- `ProductCountryFilter.vue`:国家胶囊筛选。
- `ProductTagFilter.vue` / `ProductFilterBar.vue`:下拉筛选、已选标签和搜索。
- `ProductGrid.vue` / `ProductCard.vue`:商品网格、卡片和状态反馈。
- `ProductPagination.vue`:分页、每页数量和跳转。
- `useProductCenter.ts`:真实接口状态、筛选映射和查询行为;不承载视觉规则。
## 权限评估
本功能是公开官网页面的视觉和交互调整,不新增管理操作、不读取额外敏感数据,也不改变后端资源权限,因此无需新增权限控制。
## 测试与验收
- 使用 Vitest/Vue Test Utils 覆盖默认筛选映射、筛选交互、搜索提交、标签删除和分页事件。
- 运行 website 的类型检查、测试和生产构建。
- 启动本地 Nuxt 服务,在 1440px 桌面及典型平板、手机视口进行浏览器截图验证。
- 桌面截图与 Figma 对照检查导航、侧栏、筛选区、商品卡片、分页的尺寸和位置。
- 验证真实接口成功、空数据和失败场景不破坏页面结构。
## 文档范围
- 更新 `docs/references/structs.md` 记录产品中心组件职责。
- 更新产品中心详细参考文档和 `skills` 中对应项目使用说明。
- 本次为已有功能的设计调整,不更新 README 快速开始。
@@ -0,0 +1,110 @@
# Product Center Figma Restoration Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Restore Figma node `1124:1633` in the Nuxt product center while preserving real API-backed filtering, search, and pagination.
**Architecture:** Keep `useProductCenter` as the query/state boundary and restyle the existing focused Vue components. Add a small default-filter resolver so Figma's initial state is derived from backend names rather than IDs, then compose desktop and mobile layouts in the existing page.
**Tech Stack:** Nuxt 4, Vue 3 Composition API, TypeScript, CSS, Vitest, Vue Test Utils, Playwright/browser QA.
---
### Task 1: Add Website Test Infrastructure
**Files:**
- Modify: `apps/website/package.json`
- Create: `apps/website/vitest.config.ts`
- Create: `apps/website/test/setup.ts`
- [ ] Add Vitest, Vue Test Utils, happy-dom, and a scoped `test` script.
- [ ] Configure Nuxt aliases and the Vue plugin for component tests.
- [ ] Run `pnpm --filter @inkreach/website test --run` and confirm the empty suite exits successfully.
### Task 2: Resolve the Figma Default Filter State
**Files:**
- Modify: `apps/website/app/composables/useProductCenter.ts`
- Create: `apps/website/test/useProductCenter.test.ts`
- [ ] Write failing tests for resolving `女式服装`, `T恤`, `包邮`, and `烫画` by display name while tolerating missing entries.
- [ ] Run the focused test and confirm it fails because the resolver does not exist.
- [ ] Implement and export a pure name-based resolver, then apply its result once after categories and tags load.
- [ ] Run the focused test and confirm it passes without hard-coded backend IDs.
### Task 3: Restore Sidebar and Filter Controls
**Files:**
- Modify: `apps/website/app/components/product/ProductSidebar.vue`
- Modify: `apps/website/app/components/product/ProductCountryFilter.vue`
- Modify: `apps/website/app/components/product/ProductTagFilter.vue`
- Modify: `apps/website/app/components/product/ProductFilterBar.vue`
- Create: `apps/website/test/product-filters.test.ts`
- [ ] Write failing component tests for category selection, country selection, dropdown selection, tag removal, and enter/button search submission.
- [ ] Run the focused tests and verify the missing Figma interactions or markup cause failure.
- [ ] Implement the 240px category tree, country pills, filter pills, selected chips, and 220px search control using semantic buttons and inputs.
- [ ] Add mobile drawer-compatible props and events without duplicating sidebar content.
- [ ] Run focused tests and confirm all filter events pass.
### Task 4: Restore Product Cards and Grid
**Files:**
- Modify: `apps/website/app/components/product/ProductCard.vue`
- Modify: `apps/website/app/components/product/ProductCardSkeleton.vue`
- Modify: `apps/website/app/components/product/ProductGrid.vue`
- Create: `apps/website/test/product-grid.test.ts`
- [ ] Write failing tests for image/name/tag/country/price rendering and loading/empty states.
- [ ] Run the focused tests and confirm Figma card structure expectations fail.
- [ ] Implement 270 x 382 desktop cards with 270px media, 18px title, grouped tag colors, and Figma price typography.
- [ ] Implement the four/three/two/one-column responsive grid and stable skeleton/empty layouts.
- [ ] Run focused tests and confirm all card and grid states pass.
### Task 5: Restore Pagination
**Files:**
- Modify: `apps/website/app/components/product/ProductPagination.vue`
- Create: `apps/website/test/product-pagination.test.ts`
- [ ] Write failing tests for total text, visible page tokens, previous/next, page-size selection, and page jump.
- [ ] Run the focused test and verify the expected Figma pagination controls fail.
- [ ] Implement the compact 32px pagination controls with responsive wrapping and real emitted values.
- [ ] Run focused tests and confirm pagination behavior passes.
### Task 6: Compose the Restored Page
**Files:**
- Modify: `apps/website/app/pages/product-center.vue`
- Modify: `apps/website/app/components/AppHeader.vue`
- Create: `apps/website/test/product-center-page.test.ts`
- [ ] Write a failing page-level test for Figma section order, default-state initialization, sidebar drawer trigger, and clearing filters.
- [ ] Run the focused test and confirm the current layout fails.
- [ ] Compose the 80px header, fixed-width desktop sidebar, 1200px content region, filter rows, four-column grid, ad capsule, and pagination.
- [ ] Add the mobile category drawer with backdrop, close button, escape handling, and body-scroll-safe layout.
- [ ] Run the page test and full website test suite.
### Task 7: Documentation and Structural References
**Files:**
- Modify: `docs/references/structs.md`
- Create: `docs/references/product-center.md`
- Create: `skills/inkreach-official-website/SKILL.md`
- [ ] Document the product center route, real-data filters, default state, responsive behavior, and component responsibilities.
- [ ] Update the structure reference with all created and modified website modules.
- [ ] Add the agent-facing usage workflow and verification commands.
### Task 8: Verification and Visual Comparison
**Files:**
- Modify only files required by issues found during verification.
- [ ] Run `pnpm --filter @inkreach/website test --run` and confirm zero failures.
- [ ] Run `pnpm --filter @inkreach/website build` and confirm a successful Nuxt production build.
- [ ] Start the website dev server on an available port.
- [ ] Capture `/product-center` at 1440px desktop, tablet, and mobile widths with browser automation.
- [ ] Compare desktop geometry against Figma node `1124:1633`; correct visible spacing, sizing, overflow, and overlap defects.
- [ ] Re-run tests and build after visual corrections.
@@ -0,0 +1,12 @@
# Product Center Navigation Feature Plan
**Goal:** Improve product filter interactions and connect homepage/product cards to their target routes.
**Architecture:** Keep backend IDs as the shared routing contract. Product filter state remains in `useProductCenter`; presentational components emit typed events and own only dropdown visibility.
- [x] Add failing tests for grouped selected tags, dropdown closing, product detail URLs, and country query validation.
- [x] Close filter menus after selection and outside clicks; render selected tags under their owning groups.
- [x] Link product cards to Inkpod detail pages.
- [x] Link all homepage free-trial actions to Inkpod login.
- [x] Render all backend-configured countries in a balanced two-row grid; switch countries in place and link category cards to combined country/category filters.
- [x] Run the full website test suite, production build, and browser interaction verification.
+38
View File
@@ -0,0 +1,38 @@
# Product External ID Fix Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make the public goods API return the SDS/InkPOD product ID as `id`, so product-card links target the correct InkPOD detail page.
**Architecture:** Keep the local `goods.good_id` as the database identity used by admin and relations. At the public API serialization boundary, map `originGood.sdsGoodId` to the existing response `id` field; the website can continue consuming `product.id` without a second identifier.
**Tech Stack:** NestJS, Prisma, Jest, Nuxt/Vue
---
### Task 1: Lock the public API ID contract
**Files:**
- Modify: `apps/api/src/public/public.service.spec.ts`
- [x] Add an assertion that a listed public product has `id` equal to the seeded `OriginGood.sdsGoodId`, not the local `Good.id`.
- [x] Run the focused API test and confirm it fails because the service currently returns `good.id`.
### Task 2: Return the SDS product ID
**Files:**
- Modify: `apps/api/src/public/public.service.ts`
- [x] Include `sdsGoodId` in the serializer's `originGood` type.
- [x] Return `good.originGood.sdsGoodId` as `PublicGoodDto.id`.
- [x] Run the focused API test and confirm it passes.
### Task 3: Verify consumers and document the contract
**Files:**
- Modify: `docs/references/product-center.md`
- Modify: `docs/references/structs.md`
- [x] Document that public product `id` is the SDS/InkPOD external product ID.
- [x] Run API public-service tests, website tests, and the website build.
- [ ] Verify the running public endpoint returns `126673` for `200g纯棉T恤KRTM001` when local services are available. (The API was not listening on port 3001 during verification.)
@@ -0,0 +1,33 @@
# Product Filter Selected Tags Fix Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Show selected logistics and process filters as removable gray tags matching Figma.
**Architecture:** Keep `selectedTagIds` as the only source of truth. `ProductTagFilter` derives the selected `Tag` objects from its props and emits the existing `toggle-tag` event when a selected tag is removed.
**Tech Stack:** Nuxt 4, Vue 3 Composition API, TypeScript, scoped CSS, Vitest, Vue Test Utils.
---
### Task 1: Selected Filter Tags
**Files:**
- Modify: `apps/website/app/components/product/ProductTagFilter.vue`
- Create: `apps/website/test/ProductTagFilter.test.ts`
- [x] **Step 1: Write the failing component test**
Mount the component with selected logistics and process IDs. Assert that `包邮` and `烫画` appear in `.selected-tag`, and clicking the first `.selected-tag-remove` emits `toggle-tag` with the selected ID.
- [x] **Step 2: Run the focused test and verify RED**
Run `pnpm --filter @inkreach/website test -- --run test/ProductTagFilter.test.ts` and expect failure because `.selected-tag` does not exist.
- [x] **Step 3: Implement the selected tag row**
Add a pure `computed` mapping from `selectedTagIds` to tags. Render each selected tag below the dropdown row with `#f2f2f2` background, 30px height, 16px horizontal padding, 6px gap, 60px radius, 16px black text, and a 20px remove icon button.
- [x] **Step 4: Verify GREEN and regressions**
Run the focused test, the complete website test suite, and the website production build. Browser-test selection and removal at `http://localhost:3000/product-center`.