302 lines
14 KiB
Markdown
302 lines
14 KiB
Markdown
# Website SSR Migration Plan
|
|
|
|
## Goal
|
|
Upgrade `apps/website` into the Next.js SSR/SEO version of the current `apps/spa` experience.
|
|
|
|
`apps/spa` is the UX and behavior reference. `apps/website` is the only application implementation target for this migration.
|
|
|
|
## Scope
|
|
Allowed edit scope:
|
|
- `apps/website/**`
|
|
- planning or handoff documents such as this file
|
|
|
|
Do not edit without separate approval:
|
|
- `apps/spa/**`
|
|
- `apps/backend/**`
|
|
- `apps/admin/**`
|
|
- `packages/types/**`
|
|
|
|
If a correct fix requires backend, schema, shared types, admin CMS, or SPA changes, stop and report it as a blocker instead of using a workaround.
|
|
|
|
## Source Of Truth
|
|
- SPA route map: `apps/spa/src/App.tsx`
|
|
- SPA home CMS usage: `apps/spa/src/pages/HomePage.tsx`
|
|
- SPA dynamic renderer: `apps/spa/src/components/DynamicPageRenderer.tsx`
|
|
- Website API resolver: `apps/website/src/lib/api.ts`
|
|
- Website App Router pages: `apps/website/src/app/**`
|
|
- Backend CMS API contract, read-only reference only: `apps/backend/src/routes/pages.ts`
|
|
- Shared CMS payload schemas, read-only reference only: `packages/types/index.ts`
|
|
|
|
## Important Clarification
|
|
The SPA does not expose a frontend route named `/pages/home`.
|
|
|
|
The SPA route `/` renders `HomePage`, and `HomePage` calls the backend CMS API endpoint:
|
|
|
|
```text
|
|
GET ${API_BASE}/pages/home?lang=<current-language>
|
|
```
|
|
|
|
For `apps/website`, this means Next pages should keep public SEO routes such as `/`, `/shop`, `/category/[id]`, and `/products/[id]`, while using the same CMS API endpoint internally when needed.
|
|
|
|
## Migration Strategy
|
|
Do not copy the SPA wholesale into Next.
|
|
|
|
Use this pattern instead:
|
|
- Server components fetch SEO-critical data and render meaningful initial HTML.
|
|
- Client components handle cart, wishlist, LIFF, forms, filters, payment, and localStorage state.
|
|
- CMS section rendering is adapted into website-local components.
|
|
- Published single-segment CMS pages can be rendered through `/:slug` without replacing route-specific commerce pages.
|
|
- API/environment logic stays website-local and uses `NEXT_PUBLIC_API_URL` / `INTERNAL_API_URL`.
|
|
|
|
## Phase 1: Website Foundation Audit
|
|
Owner: `frontend-code-auditor` review only
|
|
|
|
Tasks:
|
|
- Build a route parity matrix between `apps/spa/src/App.tsx` and `apps/website/src/app/**`.
|
|
- Classify routes as `exists-needs-upgrade`, `missing`, `client-only-ok`, or `seo-critical`.
|
|
- Identify legacy classes, missing design tokens, hardcoded URLs, and silent API fallbacks in `apps/website`.
|
|
- Identify pages where current client-only fetch weakens SEO.
|
|
|
|
Deliverables:
|
|
- Route parity matrix.
|
|
- Website-only risk list.
|
|
- Recommended migration order.
|
|
|
|
## Phase 2: Website Data Layer
|
|
Owner: `frontend-lead`
|
|
Review: `frontend-code-auditor`
|
|
|
|
Tasks:
|
|
- Create or refine website-local fetch helpers under `apps/website/src/lib`.
|
|
- Separate server-safe fetch helpers from client-only fetch helpers.
|
|
- Ensure all helpers use `apps/website/src/lib/api.ts`.
|
|
- Add consistent handling for API failures without hiding important contract issues.
|
|
- Avoid importing runtime API constants from `@homaroma/types`.
|
|
|
|
Acceptance criteria:
|
|
- No edits outside allowed scope.
|
|
- API helpers work in Next server and client contexts as intended.
|
|
- No silent empty fallback on SEO-critical pages without an explicit fallback reason.
|
|
|
|
## Phase 3: CMS Dynamic Renderer For Website
|
|
Owner: `frontend-lead`
|
|
Review: `frontend-code-auditor`
|
|
|
|
Tasks:
|
|
- Create website-local dynamic page renderer under `apps/website/src/components`.
|
|
- Adapt these section types first:
|
|
- `ANNOUNCEMENT_BAR`
|
|
- `HERO`
|
|
- `CATEGORIES`
|
|
- `PRODUCTS`
|
|
- `PRODUCT_CAROUSEL`
|
|
- `BANNER`
|
|
- `TEXT`
|
|
- `NEWSLETTER`
|
|
- Split SSR-capable sections from client-only interactive sections.
|
|
- Keep payload guards local to `apps/website` if shared types are insufficient.
|
|
|
|
Acceptance criteria:
|
|
- Home page can render CMS sections from `/pages/home`.
|
|
- Unknown section types fail visibly in development logs, but do not crash production rendering.
|
|
- No broad `any` use unless isolated and documented as a temporary contract gap.
|
|
|
|
## Phase 4: SEO Route Migration
|
|
Owner: `frontend-lead`
|
|
Review: `frontend-code-auditor`
|
|
|
|
Migration order:
|
|
1. `/`
|
|
2. `/products/[id]`
|
|
3. `/shop`
|
|
4. `/category/[id]`
|
|
5. `/article/[slug]`
|
|
6. `/search`
|
|
|
|
Tasks:
|
|
- Convert SEO-critical content to server-rendered data where possible.
|
|
- Keep interactive controls as client islands.
|
|
- Add or preserve `generateMetadata` for pages that can provide SEO data.
|
|
- Generate fallback metadata from available API data if CMS metadata is unavailable.
|
|
|
|
Acceptance criteria:
|
|
- Initial HTML contains meaningful product/category/article/page content.
|
|
- Cart, wishlist, filters, and add-to-cart remain interactive.
|
|
- Build and lint pass for `apps/website`.
|
|
|
|
## Phase 5: Commerce And LINE OA Client Features
|
|
Owner: `frontend-lead`
|
|
Review: `frontend-code-auditor`
|
|
Flow QA: `qa-product-auditor`
|
|
|
|
Routes:
|
|
- `/checkout`
|
|
- `/order/[id]`
|
|
- `/track`
|
|
- `/wishlist`
|
|
- `/profile`
|
|
|
|
Tasks:
|
|
- Adapt SPA behavior into website-local client components.
|
|
- Add website-local auth, wishlist, and LIFF integration if needed.
|
|
- Keep SEO shell/server rendering where useful, but treat these routes as client-heavy flows.
|
|
- Use existing backend API endpoints without changing backend contracts.
|
|
|
|
Acceptance criteria:
|
|
- Checkout, order success, tracking, wishlist, and profile flows work on desktop and mobile.
|
|
- LINE OA/LIFF logic only runs on the client.
|
|
- Payment/order/upload failures have visible error states.
|
|
|
|
## Phase 6: Visual And UX Alignment
|
|
Owner: `ux-ui-designer` + `frontend-lead`
|
|
Review: `design-system-auditor`
|
|
|
|
Tasks:
|
|
- Align website visual language with SPA source of truth.
|
|
- Remove or replace legacy website-only classes that are not defined.
|
|
- Align navbar, footer, cart drawer, product card, loading states, empty states, and error states.
|
|
- Check mobile layout and common shopping flows.
|
|
|
|
Acceptance criteria:
|
|
- Website no longer mixes incompatible legacy dark/cyan styling with SPA visual language.
|
|
- UI remains readable and responsive.
|
|
- Design token usage is consistent within `apps/website`.
|
|
|
|
## Phase 7: Verification And Release Gate
|
|
Owner: `orchestrator`
|
|
Reviewers:
|
|
- `frontend-code-auditor`
|
|
- `qa-product-auditor`
|
|
- `design-system-auditor`
|
|
|
|
Required checks:
|
|
- `npm run build -w apps/website`
|
|
- `npm run lint -w apps/website`
|
|
- Manual route smoke test for SEO routes.
|
|
- Manual commerce flow test for checkout/order/tracking.
|
|
- Verify no files outside allowed scope were edited.
|
|
- Verify all blockers requiring backend/schema/shared type changes are reported, not worked around.
|
|
|
|
## Delegation Prompts
|
|
|
|
### Frontend Code Auditor
|
|
Use for Phase 1 and review gates.
|
|
|
|
```text
|
|
Review only. Do not edit files.
|
|
Inspect apps/website against apps/spa as reference.
|
|
Focus on route parity, SSR SEO risks, type/API contract risks, hardcoded values, missing states, and anti-patterns.
|
|
Allowed output: findings, severity, file paths, recommended fix, escalation needed.
|
|
```
|
|
|
|
### Frontend Lead
|
|
Use for implementation phases.
|
|
|
|
```text
|
|
Implement only inside apps/website.
|
|
Use apps/spa as UX and behavior reference, but do not edit apps/spa.
|
|
Do not edit apps/backend, apps/admin, or packages/types.
|
|
If the correct fix requires those areas, stop and report a blocker.
|
|
Preserve Next.js SSR for SEO-critical content and use client islands for interactive behavior.
|
|
```
|
|
|
|
### QA Product Auditor
|
|
Use after route groups are implemented.
|
|
|
|
```text
|
|
Review user flows only. Do not edit files unless explicitly asked.
|
|
Check broken flows, empty states, error states, mobile behavior, checkout/order/tracking/profile/wishlist flows, and release readiness.
|
|
```
|
|
|
|
### Design System Auditor
|
|
Use after visual alignment.
|
|
|
|
```text
|
|
Review visual consistency only. Do not edit files unless explicitly asked.
|
|
Check contrast, readability, missing classes, token usage, responsive layout, and SPA/website visual alignment.
|
|
```
|
|
|
|
## Open Decisions
|
|
- Should `apps/website` support locale-specific URL paths later, or keep language state client-side for now?
|
|
- Should dynamic CMS SEO metadata be generated only from existing API data in this migration?
|
|
- Should `apps/spa` remain the dedicated LINE OA app after website reaches parity, or should website eventually replace it?
|
|
- Should shared type/runtime cleanup be scheduled as a separate project after website-only migration?
|
|
- Should nested CMS paths such as `/campaign/summer` be supported, and if yes, what slug policy should admin/backend enforce to avoid collisions with business routes?
|
|
|
|
## Decisions
|
|
|
|
### Image Optimization
|
|
Decision: defer migration from `<img>` to `next/image` for M1.
|
|
|
|
Reason:
|
|
- Current image sources are mixed between local public paths such as `/images/...` and backend/CMS upload paths such as `/uploads/...`.
|
|
- `apps/website/next.config.ts` does not yet define final `images.remotePatterns` or production CDN/image host policy.
|
|
- Changing to `next/image` before image hosting is finalized can create broken remote image rendering or force premature config assumptions.
|
|
- M1 priority is SSR/SEO route parity, API correctness, visual cleanup, and build stability.
|
|
|
|
Current policy:
|
|
- Keep existing `<img>` usage for now.
|
|
- Treat `@next/next/no-img-element` warnings as accepted M1 optimization debt, not a release blocker.
|
|
- Revisit after production image domain/CDN and upload URL strategy are finalized.
|
|
|
|
Future acceptance criteria for `next/image` migration:
|
|
- Production image host or CDN is known.
|
|
- `apps/website/next.config.ts` has explicit `images.remotePatterns` for allowed hosts.
|
|
- Hero, product primary image, category hero, article cover, and CMS hero/banner images are migrated first.
|
|
- Lint still passes and images render in dev, preview, and production-like environments.
|
|
|
|
## Initial Milestone
|
|
M1: Website SEO Foundation
|
|
|
|
Target:
|
|
- Phase 1 complete.
|
|
- Phase 2 complete.
|
|
- Basic CMS renderer exists in `apps/website`.
|
|
- `/` and `/products/[id]` upgraded first.
|
|
- `apps/website` build/lint pass.
|
|
- `frontend-code-auditor` review passed.
|
|
|
|
Current status:
|
|
- Phase 1 route parity audit documented in `docs/website-route-parity-audit.md`.
|
|
- Website-local data helpers added under `apps/website/src/lib`.
|
|
- Website-local result helpers now expose API errors instead of converting every failure into an empty list.
|
|
- CMS layout parity is now wired for `/`, `/shop`, `/category/[id]`, `/products/[id]`, `/search`, `/scent-guide`, and `/coupons`.
|
|
- Published single-segment CMS pages now render through `apps/website/src/app/[slug]/page.tsx` with reserved slug collision protection.
|
|
- Website CMS renderer now supports SSR-safe editorial, article hero, scent notes, quote, FAQ accordion, and contact info blocks.
|
|
- `CONTACT_FORM` is intentionally deferred until a real submission endpoint/API owner is ready. Do not implement simulated submit behavior.
|
|
- `/track` now renders the tracking form, calls `POST /orders/track`, stores validated local order history, and consumes `track-order-layout` CMS sections.
|
|
- `/order/[id]` now exists as a read-only SSR order status page with item summary, status, tracking number, metadata, and `order-success-layout` CMS sections.
|
|
- `/wishlist` now exists with a website-local persisted wishlist store compatible with SPA `homaroma-wishlist`, product-card heart toggles, navbar counters, and `wishlist-layout` CMS sections.
|
|
- `/profile` now exists with SPA-compatible `homaroma-auth-storage`, member dashboard, order history, address CRUD, contact settings, navbar profile entry, and `profile-layout` CMS sections.
|
|
- `/checkout` now exists with a website-local client checkout, SPA-compatible cart/auth storage, `GET /settings`, `GET /promotions/active`, `POST /coupons/validate`, saved address support, points redemption, `POST /orders`, local order history, cart clearing, redirect to `/order/[id]`, and `checkout-layout` CMS sections.
|
|
|
|
Next implementation target:
|
|
- Run manual commerce QA for `/shop -> cart -> /checkout -> /order/[id] -> /track`.
|
|
- Confirm website LIFF initialization/auth bootstrap before treating `/profile` as fully production-ready.
|
|
- Extend `/order/[id]` payment/slip upload only after explicitly planning the payment side effects and QA gate.
|
|
- Escalate backend server-side pricing validation before treating checkout/payment as production-grade, because the current `POST /orders` contract trusts client totals, discounts, points, and shipping values.
|
|
- Basic website CMS renderer added under `apps/website/src/components/dynamic`.
|
|
- `/` now renders CMS sections from `/pages/home` internally.
|
|
- `/products/[id]` now uses website-local data helpers and avoids duplicate product fetch.
|
|
- `/shop`, `/category/[id]`, and `/search` now render product/category content on the server instead of client-only fetch.
|
|
- `/shop` preserves coupon handoff with a client-only localStorage bridge while keeping product content server-rendered.
|
|
- `/search` guards repeated `q` query params before trimming.
|
|
- Navbar, Footer, CartDrawer, and legacy content pages now use the website `pop-*` visual language instead of undefined dark/cyan/glass classes.
|
|
- CMS parity planning added in `docs/website-cms-parity-plan.md`.
|
|
- `/shop` now consumes `shop-layout` CMS sections for announcement, hero/banner, and bottom content around the SSR product grid.
|
|
- `/category/[id]` now consumes `category-layout` CMS sections for announcement, hero/banner, and bottom content around the SSR category product grid.
|
|
- `/products/[id]` now consumes `product-detail-layout` CMS sections for announcement and bottom content around the SSR product detail.
|
|
- `/search` now consumes `search-layout` CMS sections for announcement, hero/banner, and bottom content around SSR search results.
|
|
- `/scent-guide` now consumes `scent-guide-layout` CMS sections for announcement, hero/banner, and bottom content around the article index.
|
|
- `/scent-guide` now exposes article API failure separately from a legitimate empty article list.
|
|
- `/coupons` now consumes `coupons-layout` CMS sections for announcement, hero/banner, and bottom content around the SSR coupon grid.
|
|
- `/coupons` now exposes coupon API failure separately from a legitimate empty coupon list.
|
|
- `apps/website` CMS parity next target is CMS catch-all custom page support and remaining SPA-only block renderer parity.
|
|
- `apps/website` lint passes with warnings only.
|
|
- `apps/website` production build passes.
|
|
|
|
Remaining M1 follow-up:
|
|
- Final manual browser smoke test of upgraded website routes.
|
|
- Keep remaining `<img>` usage for M1 and schedule `next/image` as a later optimization pass after image hosting is finalized.
|
|
- Continue manual visual QA for Navbar, Footer, CartDrawer, and content pages across desktop/mobile breakpoints.
|