# Shop-Storefront — Current State Document

> **Generated:** 2026-03-27
> **Role:** Brownfield analysis by Senior Staff Engineer / Technical Lead
> **Branch at time of analysis:** `namds/refactor-base-layout`

---

## Table of Contents

1. [Phase 1 — Discovery & Scaffolding](#phase-1--discovery--scaffolding)
2. [Phase 2 — Codebase Triage](#phase-2--codebase-triage)
3. [Phase 3 — Documentation](#phase-3--documentation)
   - [High-Level System Overview](#1-high-level-system-overview)
   - [Architecture Diagram](#2-architecture-diagram)
   - [Local Setup Guide](#3-local-setup-guide)
   - [Key Domain Entities](#4-key-domain-entities)
4. [Phase 4 — Maintenance Strategy](#phase-4--maintenance-strategy)
   - [Immediate Risks](#1-immediate-risks)
   - [Refactoring Opportunities](#2-refactoring-opportunities-low-hanging-fruit)
   - [Testing Strategy](#3-testing-strategy)

---

## Phase 1 — Discovery & Scaffolding

### Tech Stack

| Layer                   | Technology                                                |
| ----------------------- | --------------------------------------------------------- |
| **Framework**           | Next.js 15 (App Router, Turbopack)                        |
| **Language**            | TypeScript 5.3                                            |
| **UI Runtime**          | React 19 RC (`19.0.0-rc-66855b96-20241106`)               |
| **Styling**             | Tailwind CSS 3 + `@medusajs/ui` + Radix UI                |
| **Commerce Backend**    | Medusa V2 (`@medusajs/js-sdk`)                            |
| **Payments**            | Stripe (`@stripe/react-stripe-js`), PayPal                |
| **Package Manager**     | Yarn 3 (Berry)                                            |
| **Database (indirect)** | `pg` listed as dependency — Medusa owns the DB connection |

### Entry Points & Critical Files

| File                                      | Role                                                                                                                                       |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `src/middleware.ts`                       | **True entry point.** Intercepts every request, resolves country code from Vercel geo-header or URL, and redirects to `/{countryCode}/...` |
| `src/app/layout.tsx`                      | Root Next.js layout (HTML shell, global styles)                                                                                            |
| `src/app/[countryCode]/(main)/layout.tsx` | **Primary layout shell.** Loads the JSON design config, fetches cart + customer, and hands everything to `DynamicLayoutRenderer`           |
| `src/vibentec/configloader.ts`            | Reads active design JSON from `config/` at request time                                                                                    |
| `src/vibentec/devJsonFileNames.ts`        | Hardcoded switch that selects which tenant/design JSON is active                                                                           |
| `src/lib/config.ts`                       | Instantiates the Medusa JS SDK singleton                                                                                                   |
| `src/lib/data/`                           | All server-side data fetching (cart, customer, products, orders, regions, etc.)                                                            |

---

## Phase 2 — Codebase Triage

### Architectural Pattern

This is a **modular monolith storefront** using Next.js App Router with a custom **JSON-driven dynamic layout layer** on top. There are two distinct halves:

1. **Standard Medusa Storefront** — The base is a Medusa V2 Next.js starter. It follows a clear `modules/` → `templates/` → `components/` hierarchy, with server-side data fetching in `lib/data/` feeding React Server Components.

2. **Vibentec UI Builder** — A bespoke system in `src/vibentec/` that allows the entire page shell (header, nav, footer, banners) to be defined declaratively in JSON config files (`/config/`). At runtime, `DynamicLayoutRenderer` walks the JSON node tree and maps keys to React components via `componentMap`. Multiple tenant/brand configs exist: `3bear`, `drsquatch`, `vibentec`, `mds-starter`, `medusa-starter`, `playground`.

### Core Domain

`src/lib/data/` is the data layer. `src/modules/` holds all business-domain UI (account, cart, checkout, products, orders, collections, store). The custom differentiator is `src/vibentec/` — this is Vibentec's intellectual property layered on top of the OSS Medusa starter.

### Technical Debt & Health Assessment

| Severity   | Issue                                                                                                             |
| ---------- | ----------------------------------------------------------------------------------------------------------------- |
| **High**   | `REVALIDATE_SECRET=supersecret` is committed in `.env` — must be rotated before production                        |
| **High**   | A real publishable API key (`pk_65b8a...`) is hardcoded in the committed `.env` file                              |
| **Medium** | React 19 **RC** (release candidate) is pinned — not a stable release                                              |
| **Medium** | Active tenant config is hardcoded in `src/vibentec/devJsonFileNames.ts` — switching brands requires a code change |
| **Medium** | `LayoutContext` uses `any` for `customer` and `cart` — kills type safety at the renderer boundary                 |
| **Medium** | `pg` is listed as a direct dependency but no direct DB usage exists in this storefront                            |
| **Low**    | Zero test files found anywhere in the project                                                                     |
| **Low**    | `@types/react-instantsearch-dom` in devDeps but no instantsearch code exists — unused dependency                  |

---

## Phase 3 — Documentation

### 1. High-Level System Overview

This is a **multi-tenant e-commerce storefront** built on Next.js 15 (App Router), serving as the customer-facing frontend for a Medusa V2 headless commerce backend. The application handles the full retail customer journey: browsing products by category or collection, managing a cart, checking out with Stripe or PayPal, and managing account details and order history. All routes are prefixed with a country code (e.g., `/us/`, `/de/`), which is resolved at the edge by middleware that consults Medusa's region API and Vercel's geo-IP headers to deliver region-appropriate pricing and shipping rules.

The most significant custom layer is the **Vibentec UI Builder** (`src/vibentec/`). Rather than hard-coding the page shell (header, nav, banner bar, footer), the entire layout is declared as a JSON component tree stored in `config/*.design.json` files. At request time, the primary layout server component reads the active JSON file, passes it to `DynamicLayoutRenderer`, which walks the tree and resolves component names to actual React components via `componentMap`. This makes the full layout of the storefront configurable without touching React code — a white-label capability designed to serve multiple brands from a single codebase.

Data flows unidirectionally from the Medusa backend through server-side `"use server"` functions in `src/lib/data/`, which use the Medusa JS SDK with Next.js `force-cache` and tag-based revalidation. There is no client-side global state management (no Redux, Zustand, or React Query). The only React Context used is a minimal `ModalContext` scoped to the modal component. All authentication state is conveyed via cookies and forwarded as HTTP headers to Medusa.

---

### 2. Architecture Diagram

```mermaid
graph TD
    subgraph "Browser"
        Browser["Customer Browser"]
    end

    subgraph "Next.js 15 App (Port 8000)"
        Middleware["src/middleware.ts\n(Edge Runtime)\nGeo-IP → Country Code\nRegion redirect"]

        subgraph "App Router"
            RootLayout["app/layout.tsx\nRoot HTML shell"]
            MainLayout["app/[countryCode]/(main)/layout.tsx\nLoads design JSON\nFetches cart + customer\nDynamicLayoutRenderer"]
            CheckoutLayout["app/[countryCode]/(checkout)/layout.tsx\nCheckout shell"]

            subgraph "Pages"
                Home["/ (main) page.tsx"]
                Store["/store page.tsx"]
                Product["/products/[handle]"]
                Cart["/cart"]
                Checkout["/checkout"]
                Account["/account (Parallel Routes)"]
                Order["/order/[id]"]
                Collections["/collections/[handle]"]
                Categories["/categories/[...category]"]
            end
        end

        subgraph "Vibentec UI Builder (src/vibentec/)"
            ConfigLoader["configloader.ts\nReads config/*.design.json"]
            Renderer["renderer.tsx\nDynamicLayoutRenderer"]
            ComponentMap["component-map.tsx\ncomponentMap registry"]
        end

        subgraph "Modules (src/modules/)"
            Layout["layout/\nHeader, Nav, Footer,\nCart Button, Side Menu,\nMega Menu, Country Select"]
            Products["products/\nGallery, Actions, Price,\nPreview, Tabs, Related"]
            CartMod["cart/\nItems, Summary, Preview"]
            CheckoutMod["checkout/\nAddresses, Payment,\nShipping, Review"]
            AccountMod["account/\nProfile, Orders,\nAddresses, Login"]
            OrderMod["order/\nConfirmation, Details,\nTransfer"]
        end

        subgraph "Data Layer (src/lib/data/)"
            DataFunctions["cart.ts · products.ts\ncustomer.ts · orders.ts\ncategories.ts · collections.ts\nregions.ts · payment.ts\nfulfillment.ts · cookies.ts"]
        end

        subgraph "Config Files (config/)"
            DesignJSON["nam.vibentec.design.json\nnam.3bear.design.json\nnam.drsquatch.design.json\nste.medusa-starter.design.json\n(+ others)"]
        end
    end

    subgraph "External Services"
        MedusaBackend["Medusa V2 Backend\n(localhost:9000)\nProducts, Orders, Carts,\nCustomers, Regions,\nPayment Sessions"]
        Stripe["Stripe\nPayment Processing"]
        PayPal["PayPal\nPayment Processing"]
        S3["AWS S3\nProduct Images"]
    end

    Browser -->|"HTTP Request"| Middleware
    Middleware -->|"Resolves region\nSets _medusa_cache_id cookie"| RootLayout
    RootLayout --> MainLayout
    MainLayout --> ConfigLoader
    ConfigLoader -->|"Reads active .design.json"| DesignJSON
    MainLayout --> Renderer
    Renderer --> ComponentMap
    ComponentMap --> Layout
    MainLayout -->|"props.children"| Pages
    Pages --> Products
    Pages --> CartMod
    Pages --> CheckoutMod
    Pages --> AccountMod
    Pages --> OrderMod
    DataFunctions -->|"Medusa JS SDK\nforce-cache + tag revalidation"| MedusaBackend
    CheckoutMod -->|"Stripe Elements"| Stripe
    CheckoutMod -->|"PayPal SDK"| PayPal
    MedusaBackend -->|"Image URLs"| S3
    Products & CartMod & CheckoutMod & AccountMod & OrderMod --> DataFunctions
```

---

### 3. Local Setup Guide

**Prerequisites:** Node.js ≥ 20, Yarn 3 (Berry), a running Medusa V2 backend with at least one Region configured.

```bash
# 1. Clone and install
git clone <repo-url>
cd Shop-Storefront
yarn install

# 2. Configure environment
cp .env .env.local
# Edit .env.local and set:
#   MEDUSA_BACKEND_URL=http://localhost:9000
#   NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=<your key from Medusa Admin>
#   NEXT_PUBLIC_BASE_URL=http://localhost:8000
#   NEXT_PUBLIC_DEFAULT_REGION=us
#   REVALIDATE_SECRET=<generate a strong random string — never use "supersecret">

# 3. Select the active brand design (defaults to namVibentec)
# Edit src/vibentec/devJsonFileNames.ts:
#   const fileName = jsonFileNames.namVibentec;  ← change to desired brand key

# 4. Run development server (Turbopack, port 8000)
yarn dev

# 5. Open browser — middleware will redirect to /{DEFAULT_REGION}/
open http://localhost:8000
```

**Build for production:**

```bash
yarn build
yarn start
```

> `next.config.js` calls `checkEnvVariables()` at startup. Missing required env vars will cause a descriptive build failure.

---

### 4. Key Domain Entities

Inferred from `@medusajs/types` (`HttpTypes`) as used by the data layer, plus local types in `src/types/global.ts`.

| Entity                               | Key Fields                                                                                                                               | Source                                                   |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| **StoreProduct**                     | `id`, `title`, `handle`, `thumbnail`, `variants[]`, `tags[]`, `metadata`, `collection`, `categories[]`                                   | `lib/data/products.ts`                                   |
| **StoreProductVariant**              | `id`, `calculated_price`, `inventory_quantity`, `options[]`                                                                              | Fetched with `*variants.calculated_price` field selector |
| **StoreCart**                        | `id`, `region_id`, `items[]`, `shipping_address`, `billing_address`, `email`, `promotions[]`, `shipping_methods[]`, `payment_collection` | `lib/data/cart.ts`                                       |
| **StoreLineItem**                    | `id`, `variant_id`, `quantity`, `total`, `product`, `variant`, `thumbnail`, `metadata`                                                   | Part of StoreCart                                        |
| **StoreRegion**                      | `id`, `countries[]` (each with `iso_2`)                                                                                                  | `middleware.ts`, `lib/data/regions.ts`                   |
| **StoreCustomer**                    | `id`, `email`, `first_name`, `last_name`, `addresses[]`, `phone`                                                                         | `lib/data/customer.ts`                                   |
| **StoreOrder**                       | `id`, `status`, `items[]`, `shipping_address`, `payment_collections[]`, `fulfillments[]`                                                 | `lib/data/orders.ts`                                     |
| **StoreCartShippingOption**          | `id`, `name`, `amount`, `provider_id`                                                                                                    | `lib/data/cart.ts` → `listCartOptions()`                 |
| **FeaturedProduct** _(local)_        | `id`, `title`, `handle`, `thumbnail`                                                                                                     | `src/types/global.ts`                                    |
| **VariantPrice** _(local)_           | `calculated_price`, `original_price`, `currency_code`, `price_type`, `percentage_diff`                                                   | `src/types/global.ts`                                    |
| **StoreFreeShippingPrice** _(local)_ | extends `StorePrice` + `target_reached`, `target_remaining`, `remaining_percentage`                                                      | `src/types/global.ts`                                    |
| **LayoutComponentNode** _(local)_    | `{ [ComponentName]: { config?, children? } }`                                                                                            | `src/vibentec/component-map.tsx`                         |

---

## Phase 4 — Maintenance Strategy

### 1. Immediate Risks

| Severity     | Risk                                           | Location                       | Action Required                                                                                                                                                                                                                                         |
| ------------ | ---------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Critical** | Secrets committed to repository                | `.env`                         | Rotate `NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY` immediately. Regenerate `REVALIDATE_SECRET`. Use `.env.local` (already `.gitignore`'d) for real values.                                                                                                     |
| **Critical** | Silent bug: missing `await`                    | `src/lib/data/cart.ts:339`     | `const cartId = getCartId()` is missing `await`. `cartId` resolves to a `Promise` (truthy), so the guard `if (!cartId)` never fires. `setAddresses` silently fails to validate cart existence on cold sessions. Fix: `const cartId = await getCartId()` |
| **High**     | Empty stub functions silently do nothing       | `src/lib/data/cart.ts:279–318` | `applyGiftCard`, `removeDiscount`, `removeGiftCard` are exported but completely empty. Any UI that calls them will silently succeed with no effect. Either implement for Medusa V2 or remove the exports.                                               |
| **High**     | TypeScript & ESLint errors suppressed at build | `next.config.js:24–27`         | `ignoreBuildErrors: true` and `ignoreDuringBuilds: true` ship broken types silently. Re-enable both before production and resolve resulting errors.                                                                                                     |
| **Medium**   | React 19 RC pinned                             | `package.json`                 | Pre-release runtime in production. Migrate to stable React 19 once available, or document the risk explicitly.                                                                                                                                          |

---

### 2. Refactoring Opportunities (Low-Hanging Fruit)

Listed in priority order — highest impact for lowest effort first.

**1. Externalize active brand config to an environment variable**

- File: `src/vibentec/devJsonFileNames.ts`
- Change the hardcoded `const fileName = jsonFileNames.namVibentec` to read from `process.env.VIBENTEC_DESIGN_CONFIG`.
- Impact: Enables true multi-tenant deployment (different brands on different deployments) without any code changes. Currently requires a code commit to switch brands.

**2. Type the `LayoutContext` properly**

- File: `src/vibentec/component-map.tsx:34–38`
- Replace `customer: any` and `cart: any` with `HttpTypes.StoreCustomer | null` and `HttpTypes.StoreCart | null`.
- Impact: Propagates type safety through the entire renderer pipeline, catching prop mismatches at compile time.

**3. Remove dead dependencies**

- `pg` and `@types/pg` — no direct DB access exists in this storefront.
- `@types/react-instantsearch-dom` — no instantsearch code exists.
- Run: `yarn remove pg @types/pg @types/react-instantsearch-dom`
- Impact: Cleaner dependency graph, faster installs, less confusion for new developers.

**4. Fix hardcoded `data-mode="light"`**

- File: `src/app/layout.tsx:11`
- If dark mode is planned: drive this from a cookie or `prefers-color-scheme`. If not: add a comment documenting the intentional lock to light mode.

**5. Delete commented-out V1 cart code**

- File: `src/lib/data/cart.ts:279–318`
- The commented blocks are Medusa V1 patterns. They will never be un-commented as-is. Remove them to reduce noise and cognitive load.

---

### 3. Testing Strategy

Given zero existing tests and the RSC-heavy architecture, the recommended approach is **outside-in, starting at the data boundaries**.

**Step 1 — Integration tests for the data layer** (`src/lib/data/`)

These are pure server functions with no React involved — the easiest place to start. Use **Vitest** with a real (or Docker-composed) Medusa backend in test mode.

```
Priority order: cart.ts → customer.ts → products.ts → regions.ts
```

Start with `cart.ts` — it has the most mutations, the most business logic, and already has a confirmed `await` bug. Writing a test for `setAddresses` would have caught that immediately.

**Step 2 — Component tests for the Vibentec renderer** (`src/vibentec/`)

The `DynamicLayoutRenderer` and `componentMap` are the highest-risk custom code with no safety net. Use **React Testing Library** with `@testing-library/react` (RSC-compatible via Next.js jest transform).

Write snapshot tests that pass a known JSON fixture through the full renderer pipeline. This creates a regression guard before any design JSON changes.

```typescript
// Example fixture test shape
it("renders a Header with a Banner child from JSON", () => {
  const nodes = [
    { Header: { children: [{ Banner: { config: { variant: "nav" } } }] } },
  ]
  const { container } = render(
    <DynamicLayoutRenderer nodes={nodes} context={mockContext} />
  )
  expect(container).toMatchSnapshot()
})
```

**Step 3 — E2E tests for critical user journeys** using **Playwright**

Two journeys that must never break:

1. `Browse Store → Product Detail → Add to Cart → Checkout → Order Confirmed`
2. `Login → Account Dashboard → View Order History`

**Step 4 — CI enforcement**

The `.github/` directory currently only contains an issue template. Add a GitHub Actions workflow that runs on every PR:

```yaml
- yarn lint # re-enable ignoreDuringBuilds: false first
- tsc --noEmit # re-enable ignoreBuildErrors: false first
- vitest run # data layer integration tests
- playwright test # critical path E2E
```

No PR merges to `main` without all four passing.
