# frontend-mall Specification ## Purpose The buyer-facing storefront: shell, home page, discovery, shopping and transaction flows, and the buyer center. ## Requirements ### Requirement: Localized storefront The mall SHALL render every UI string and all catalog/store/marketing mock content in en or zh from one switcher, defaulting to en. Switching locale SHALL update the desktop shell and current page without a full reload. #### Scenario: switch to Chinese - **WHEN** a shopper switches locale to zh - **THEN** navigation, buttons, product/store names and mock marketing content render in Chinese without reload errors ### Requirement: Multi-currency display The mall SHALL offer a currency switcher sourced from the selected API adapter and SHALL convert product, cart and order prices through integer minor units and each currency's exponent. Mock mode SHALL use deterministic fixed conversion rates; live mode SHALL use the API. #### Scenario: switch currency - **WHEN** a shopper switches from USD to JPY on a product priced $10.00 - **THEN** the displayed price reflects the selected adapter's conversion rate with JPY exponent 0 and no floating-point money arithmetic ### Requirement: Shopping flow A shopper SHALL be able to browse, view detail, add to cart, checkout with a shipping address, pay, track orders/shipments, confirm delivery, and request an invoice against the live API. Cart, order, shipment and invoice state SHALL be the backend's rather than the browser's, and the fixed-data adapter SHALL remain available as a configured fallback rather than the default. Adding to the cart SHALL require an authenticated shopper: an anonymous add SHALL send the shopper to sign in and return them to where they left off. #### Scenario: end-to-end purchase - **WHEN** a shopper completes checkout on a non-empty cart - **THEN** the resulting order appears in the buyer center and the cart is empty #### Scenario: anonymous add prompts sign-in - **WHEN** a signed-out shopper adds an in-stock SKU from a product page - **THEN** they are sent to sign in and, once signed in, returned to that product page ### Requirement: B2B2C mall-style PC storefront shell The mall SHALL render a buyer-facing desktop shell modeled on a classic B2B2C PC mall: a 30px utility bar, logo/search/cart header, dark primary navigation with a hover category mega-menu, a 1200px content grid, and a value-proposition footer. The header SHALL remain fully visible while scrolling; no part of the shell SHALL auto-hide based on scroll position. The category mega-menu SHALL appear as a hover dropdown under the navigation "All Categories" entry on every page except the home page, where it is instead pinned in the hero row. The visual language SHALL use #ca151e for brand/price/active states, #f5f5f5 section backgrounds, gray hairline borders, compact controls, and product-card hover lift/shadow. The implementation SHALL use Nuxt-native semantic components and SHALL NOT depend on Element Plus. #### Scenario: shopper opens any mall page - **WHEN** a shopper navigates to a buyer-facing route - **THEN** the shared desktop shell wraps the route content and its nav/search/cart controls are usable #### Scenario: header persists while scrolling - **WHEN** a shopper scrolls any mall page - **THEN** the logo/search/cart header bar remains visible and is never collapsed or hidden by scroll position #### Scenario: hover categories on a non-home page - **WHEN** a shopper hovers "All Categories" in the navigation on a page other than `/` - **THEN** the category mega-menu dropdown appears below the navigation and hides again on mouse leave ### Requirement: Mock API adapter The mall SHALL select its API adapter per domain, so one domain can be served by the live backend while the others stay on fixed data. The mall SHALL still ship a fixed-data adapter implementing the whole `@vmall/shared` API client surface, and the live/fixed choice SHALL be configurable per domain without changing page call sites. The fixed-data adapter SHALL remain able to serve every domain when the live backend is unavailable. #### Scenario: mall runs without backend - **WHEN** the mall starts with the API service unavailable and every domain configured to fixed data - **THEN** browsing, cart, checkout, payment, orders and invoice pages return deterministic fixed data and remain functional #### Scenario: domains migrate independently - **WHEN** the live backend serves the catalog and currency domains while auth, cart, orders, shipments and invoices remain on fixed data - **THEN** browsing and prices come from the backend while those other flows keep working against fixed data ### Requirement: Mock PC home page The mall home page SHALL render a hero row composed of a pinned 240px category sidebar on the left and a hero carousel filling the remainder of the 1200px grid, both 450px tall and occupying layout space (not overlaid). The sidebar SHALL list the catalog's top-level categories with up to three child links each; hovering a top-level category SHALL expand the mega-menu panel to the right over the carousel. Banner images SHALL render at fixed 450px height, center-cropped horizontally to the narrower carousel width. Below the hero, the page SHALL render a six-item quick-link strip with promotion tiles and bilingual product floors, where each floor's products come from the catalog API and the banners, promotions, quick links and floor advert art come from the content API. #### Scenario: shopper lands on home - **WHEN** `/` loads - **THEN** the category sidebar is visible to the left of the carousel without any hover or click, the carousel renders center-cropped banners at 450px height, and the quick links, promotions and every non-empty product floor render, with floor products from the catalog API and the marketing content from the content API #### Scenario: sidebar stays while scrolling - **WHEN** a shopper scrolls the home page beyond 200px - **THEN** the category sidebar remains rendered in the hero row and does not auto-hide #### Scenario: expand a category - **WHEN** a shopper hovers a top-level category in the pinned sidebar - **THEN** the mega-menu panel expands to the right, overlaying the carousel with that category's child and grandchild links from the catalog API #### Scenario: home renders with no content published - **WHEN** the content API returns empty lists - **THEN** the page still renders its category sidebar and product floors instead of failing ### Requirement: Product discovery pages The mall SHALL provide `/search` with breadcrumb, category, brand and sort controls, a five-column desktop product grid, pagination and an empty state, listing products from the catalog API filtered by the selected category's subtree and brand. The sort control SHALL offer newest-first, price and sales. It SHALL provide `/goods/[id]` rendering product and SKU data from the catalog API with image gallery/zoom, bilingual name/subtitle, integer-minor-unit prices, attribute and SKU selection, stock-aware quantity, store card, and detail/after-sale tabs. Product cards and the product detail page SHALL show the product's real sold count, and the mall SHALL NOT present reviews, ratings or reviewer comments while no reviews capability exists. #### Scenario: filter and inspect a product - **WHEN** a shopper filters the search page by a parent category and a brand, then opens a product - **THEN** products from that category's subtree matching the brand are listed, and selecting an in-stock SKU updates the displayed price, stock and cart target from the catalog API #### Scenario: sort by sales - **WHEN** a shopper sorts the search results by sales - **THEN** the order follows the products' reported sold counts #### Scenario: no invented reviews - **WHEN** a shopper opens a product - **THEN** the page shows the shop's own after-sale copy and no rating, review count or reviewer comment ### Requirement: Mock transaction flow The mall SHALL provide a store-grouped cart, address-selecting checkout preview, payment selection and payment-success result, all reading and writing the live cart and order APIs. Quantity changes, removals, selection totals, checkout and payment SHALL be persisted by the backend for the signed-in shopper, so they survive a page reload. #### Scenario: complete mock purchase - **WHEN** a shopper adds an in-stock SKU, checks out with a mock address and confirms a payment - **THEN** the cart is cleared, the success page is shown and the new order appears in the user order list #### Scenario: cart survives a reload - **WHEN** a signed-in shopper adds an item and then reloads the page - **THEN** the cart still holds that item, priced and stocked from the catalog ### Requirement: Auth and buyer center The mall SHALL provide B2B2C mall-style login, register and forgot-password panels backed by the live auth API, so credentials, roles and tokens belong to the real user rather than a fixed demo account. Registration SHALL require a password of at least 8 characters, matching the API's rule, and the register panel SHALL NOT ask for a verification code because no endpoint issues one. Failures SHALL be reported distinctly: invalid credentials on sign-in, and an already-registered email on registration. A token restored from storage SHALL be validated against the auth API on load, and a rejected token SHALL clear the session and return the shopper to sign-in. `/user` SHALL render a two-column buyer center with dashboard, order list/detail, addresses, favorites, coupons and invoices. #### Scenario: sign in and inspect buyer data - **WHEN** a shopper signs in with valid credentials and opens `/user` - **THEN** the session carries the authenticated user, and the buyer-center shell and its fixed account/order/address/favorite/coupon/invoice data render #### Scenario: wrong password rejected - **WHEN** a shopper submits a password that does not match the account - **THEN** sign-in fails with an invalid-credentials message and no session is established #### Scenario: short password refused before the API call - **WHEN** a shopper submits a registration password shorter than 8 characters - **THEN** the panel asks for at least 8 characters without calling the API #### Scenario: duplicate email reported - **WHEN** a shopper registers an email that already has an account - **THEN** the panel reports that the email is already registered rather than a generic failure #### Scenario: rejected token clears the session - **WHEN** a token restored from storage is rejected by the auth API - **THEN** the stored session is cleared and the shopper is returned to sign-in ### Requirement: Store and marketing pages The mall SHALL provide a store directory and store home reading live shops from the shop API, with each store's products coming from the catalog API, and SHALL keep the timed seckill page, collective-buy list and points-mall home on fixed bilingual content for now. The cart, payment and order surfaces SHALL show each order's shop by name from the shop API rather than a generic label. Marketing pages MAY be display-only except navigation to product detail. #### Scenario: navigate storefront discovery channels - **WHEN** a shopper opens stores, seckill, collective or integral routes - **THEN** each page renders the appropriate B2B2C mall-style banner/filter/session/card layout, the store directory and store home list real shops, and product links resolve to product detail #### Scenario: order surfaces name the shop - **WHEN** a shopper views a cart line, a payment group or an order card - **THEN** the shop is shown by its name, not a placeholder #### Scenario: a shop without a profile still renders - **WHEN** a shop has no profile row - **THEN** the directory and store home render it without inventing logo, scores or copy ### Requirement: Address book management The mall buyer center SHALL list, add, edit, delete and set-default the signed-in customer's saved addresses through the selected API adapter, replacing the previous fixed mock list. #### Scenario: manage addresses in the buyer center - **WHEN** a signed-in shopper opens `/user/addresses` and adds or edits an address - **THEN** the change persists through the adapter and survives a full page reload ### Requirement: Checkout address selection Checkout SHALL offer the customer's saved addresses for selection, defaulting to their default address, and SHALL submit the selected address with the order. When the customer has no saved address, checkout SHALL provide an inline manual address form instead. #### Scenario: checkout with a saved address - **WHEN** a signed-in shopper with saved addresses checks out - **THEN** the default address is preselected and the created order carries the chosen address ### Requirement: Live customer account statistics The buyer-center dashboard and points surfaces SHALL load the signed-in customer's balance, frozen balance, currency, and points from the shared account API rather than `USER_STATS`. #### Scenario: account summary reload - **WHEN** a signed-in shopper reloads the buyer center - **THEN** displayed account statistics come from the selected API adapter and survive browser state loss ### Requirement: Live shop coupon surfaces The mall SHALL load a product's currently claimable shop coupons and the signed-in customer's coupon list through the shared API client, replacing direct coupon fixtures. Checkout SHALL present eligible owned coupons separately for each shop order and show the server-returned discount after submission. Payment and order-detail views SHALL show the persisted `discount_minor` and post-discount total from the order payload. #### Scenario: claim from a product page - **WHEN** a signed-in shopper claims an active coupon with remaining stock from a product page - **THEN** it appears in the shopper's coupon list without a fixture import #### Scenario: select coupon by shop - **WHEN** a cart spans two shops and the shopper selects a coupon for one shop - **THEN** checkout submits the choice only for that shop and the other shop remains undiscounted #### Scenario: order shows realized discount - **WHEN** checkout succeeds with a coupon on one shop order - **THEN** that order's payment and detail views show the server discount and reduced total without a client-calculated amount ### Requirement: Live points mall The mall SHALL display live published points products and the signed-in customer's live points balance, allow redemption with a shipping address, and show the customer's redemption history on the points route. It SHALL replace `INTEGRAL_PRODUCTS` and `USER_STATS` imports on the points route. #### Scenario: redeem available product - **WHEN** a signed-in shopper with enough points redeems a published in-stock product - **THEN** the mall shows the created redemption order and refreshed points balance ### Requirement: Live flash-sale page The mall SHALL render active flash-sale sessions and activity products from the shared API client, including authoritative activity price, remaining stock, and sell-through. It SHALL replace `SECKILL_SESSIONS` and derived fixed product data. Shoppers SHALL purchase through the existing cart and checkout; payment and order-detail views SHALL show the snapshotted activity unit price from the order payload. This change SHALL NOT add flash-sale prices, badges, or claim CTAs to the catalog product-detail page (`/goods/[id]`); that page keeps catalog SKU pricing. #### Scenario: choose live session - **WHEN** a shopper selects an active flash-sale session - **THEN** its live eligible products and countdown render without direct fixture imports #### Scenario: order shows flash snapshot - **WHEN** checkout applies a flash-sale price - **THEN** payment and order-detail views show that unit price from the order payload #### Scenario: product detail stays on catalog price - **WHEN** a shopper opens a catalog product that is also in an active flash-sale session - **THEN** the product-detail page still shows catalog SKU prices and does not require a flash-sale overlay in this change