Files
manoon-headless/SALEOR_MIGRATION_PLAN.md
Unchained 7b94537670 feat(saleor): Phase 1 - GraphQL Client Setup
- Add Apollo Client for Saleor GraphQL API
- Create GraphQL fragments (Product, Variant, Checkout)
- Create GraphQL queries (Products, Checkout)
- Create GraphQL mutations (Checkout operations)
- Add TypeScript types for Saleor entities
- Add product helper functions
- Install @apollo/client and graphql dependencies

Part of WordPress/WooCommerce → Saleor migration
2026-03-21 12:36:21 +02:00

12 KiB

Manoon Headless: WordPress/WooCommerce → Saleor Migration Plan

Current State Analysis

Tech Stack

  • Framework: Next.js 16.1.6 + React 19.2.3
  • Styling: Tailwind CSS v4
  • State: Zustand (cart)
  • i18n: next-intl (Serbian/English)
  • Animation: Framer Motion
  • Backend: WooCommerce REST API

Current Data Flow

Next.js Storefront → WooCommerce REST API → WordPress Database

Target Data Flow

Next.js Storefront → Saleor GraphQL API → PostgreSQL Database

Migration Strategy: Stacked PRs

Using stacked PRs for dependent changes:

main (WooCommerce - stable)
│
├── feature/001-saleor-graphql-client (base)
│   └── Saleor GraphQL client, types, config
│
├── feature/002-saleor-products (depends on 001)
│   └── Product fetching, listing, detail pages
│
├── feature/003-saleor-cart (depends on 002)
│   └── Cart functionality with Saleor checkout
│
├── feature/004-saleor-checkout (depends on 003)
│   └── Checkout flow, payments (COD), order creation
│
└── feature/005-remove-woocommerce (depends on 004)
    └── Remove WooCommerce code, env vars, deps

Phase 1: GraphQL Client Setup (feature/001-saleor-graphql-client)

Tasks

  • Install GraphQL dependencies (@apollo/client, graphql)
  • Create Saleor GraphQL client configuration
  • Set up type generation from Saleor schema
  • Create environment variables for Saleor API
  • Test connection to Saleor API

Files to Create

src/lib/saleor/
├── client.ts          # Apollo Client configuration
├── fragments/
│   ├── Product.ts     # Product fragment
│   ├── Variant.ts     # Variant fragment
│   └── Checkout.ts    # Checkout fragment
├── mutations/
│   ├── Checkout.ts    # Checkout mutations
│   └── Cart.ts        # Cart mutations
└── queries/
    ├── Products.ts    # Product queries
    └── Checkout.ts    # Checkout queries

src/types/saleor.ts    # Generated TypeScript types

Dependencies to Add

npm install @apollo/client graphql
npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations

Phase 2: Product Migration (feature/002-saleor-products)

Tasks

  • Create Saleor product types/interfaces
  • Replace getProducts() with Saleor query
  • Replace getProductBySlug() with Saleor query
  • Update ProductCard component to use Saleor data
  • Update ProductDetail component to use Saleor data
  • Handle product variants
  • Handle product translations (SR/EN)

GraphQL Queries Needed

# Get all products
query GetProducts($channel: String!, $locale: LanguageCodeEnum!) {
  products(channel: $channel, first: 100) {
    edges {
      node {
        id
        name
        slug
        description
        translation(languageCode: $locale) {
          name
          slug
          description
        }
        variants {
          id
          name
          sku
          pricing {
            price {
              gross {
                amount
                currency
              }
            }
          }
        }
        media {
          url
          alt
        }
      }
    }
  }
}

# Get product by slug
query GetProduct($slug: String!, $channel: String!, $locale: LanguageCodeEnum!) {
  product(slug: $slug, channel: $channel) {
    id
    name
    slug
    description
    translation(languageCode: $locale) {
      name
      slug
      description
    }
    variants {
      id
      name
      sku
      pricing {
        price {
          gross {
            amount
            currency
          }
        }
      }
    }
    media {
      url
      alt
    }
  }
}

Files to Modify

src/lib/woocommerce.ts → src/lib/saleor/products.ts
src/components/product/ProductCard.tsx
src/components/product/ProductDetail.tsx
src/app/products/page.tsx
src/app/products/[slug]/page.tsx

Phase 3: Cart Migration (feature/003-saleor-cart)

Tasks

  • Replace Zustand cart store with Saleor checkout
  • Create checkout on first cart addition
  • Update cart lines (add, remove, update quantity)
  • Fetch checkout by ID (from localStorage/cookie)
  • Update CartDrawer component

Saleor Checkout Flow

1. User adds item → Create checkout (if not exists)
2. Add checkout line → checkoutLinesAdd mutation
3. Update quantity → checkoutLinesUpdate mutation
4. Remove item → checkoutLinesDelete mutation
5. Store checkoutId in localStorage

GraphQL Mutations Needed

# Create checkout
mutation CheckoutCreate($input: CheckoutCreateInput!) {
  checkoutCreate(input: $input) {
    checkout {
      id
      token
      lines {
        id
        quantity
        variant {
          id
          name
          product {
            name
            media {
              url
            }
          }
        }
      }
    }
    errors {
      field
      message
    }
  }
}

# Add lines
mutation CheckoutLinesAdd($checkoutId: ID!, $lines: [CheckoutLineInput!]!) {
  checkoutLinesAdd(checkoutId: $checkoutId, lines: $lines) {
    checkout {
      id
      lines {
        id
        quantity
      }
    }
  }
}

# Update lines
mutation CheckoutLinesUpdate($checkoutId: ID!, $lines: [CheckoutLineUpdateInput!]!) {
  checkoutLinesUpdate(checkoutId: $checkoutId, lines: $lines) {
    checkout {
      id
      lines {
        id
        quantity
      }
    }
  }
}

Files to Modify

src/stores/cartStore.ts → src/stores/saleorCheckoutStore.ts
src/components/cart/CartDrawer.tsx

Phase 4: Checkout Flow (feature/004-saleor-checkout)

Tasks

  • Create checkout page
  • Implement shipping address form
  • Implement billing address form
  • Set shipping method (COD)
  • Create order on completion
  • Show order confirmation

Cash on Delivery (COD) Flow

1. User completes checkout form
2. Set shipping/billing addresses
3. Select shipping method (fixed price)
4. Complete checkout → creates order
5. Order status: UNFULFILLED
6. Payment status: NOT_CHARGED (COD)

GraphQL Mutations

# Set shipping address
mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: AddressInput!) {
  checkoutShippingAddressUpdate(checkoutId: $checkoutId, shippingAddress: $shippingAddress) {
    checkout {
      id
      shippingAddress {
        firstName
        lastName
        streetAddress1
        city
        postalCode
        phone
      }
    }
  }
}

# Set billing address
mutation CheckoutBillingAddressUpdate($checkoutId: ID!, $billingAddress: AddressInput!) {
  checkoutBillingAddressUpdate(checkoutId: $checkoutId, billingAddress: $billingAddress) {
    checkout {
      id
      billingAddress {
        firstName
        lastName
        streetAddress1
        city
        postalCode
        phone
      }
    }
  }
}

# Complete checkout (creates order)
mutation CheckoutComplete($checkoutId: ID!) {
  checkoutComplete(checkoutId: $checkoutId) {
    order {
      id
      number
      status
      total {
        gross {
          amount
          currency
        }
      }
    }
    errors {
      field
      message
    }
  }
}

Files to Create

src/app/checkout/
├── page.tsx              # Checkout page
├── CheckoutForm.tsx      # Address forms
├── OrderSummary.tsx      # Cart summary
└── CheckoutSuccess.tsx   # Order confirmation

Phase 5: Cleanup (feature/005-remove-woocommerce)

Tasks

  • Remove WooCommerce dependencies
  • Remove WooCommerce API file
  • Clean up environment variables
  • Update documentation
  • Test complete flow

Files to Remove

src/lib/woocommerce.ts

Dependencies to Remove

npm uninstall @woocommerce/woocommerce-rest-api

Environment Variables to Update

# Remove
NEXT_PUBLIC_WOOCOMMERCE_URL
NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_KEY
NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_SECRET

# Add
NEXT_PUBLIC_SALEOR_API_URL
NEXT_PUBLIC_SALEOR_CHANNEL

URL Structure

Current (WooCommerce)

/products/                    # Product listing
/products/:slug/              # Product detail (Serbian)
/en/products/:slug/           # Product detail (English)

Target (Saleor)

/products/                    # Product listing
/products/:slug/              # Product detail (Serbian or English slug)

Saleor stores both Serbian and English slugs. The storefront will fetch by slug and detect language.


Component Mapping

Current Component Saleor Equivalent Changes
WooProduct interface Product fragment Different field names
getProducts() GetProducts query GraphQL instead of REST
getProductBySlug() GetProduct query GraphQL instead of REST
useCartStore (Zustand) useCheckoutStore Saleor checkout-based
formatPrice() formatPrice() Handle Money type

Data Mapping

Product

WooCommerce Saleor Notes
id id Woo uses int, Saleor uses UUID
name name Same
slug slug Same
price variants[0].pricing.price.gross.amount Nested in variant
regular_price variants[0].pricing.price.gross.amount Saleor has discounts
images[0].src media[0].url Different structure
stock_status variants[0].quantityAvailable Check > 0
description description JSON editor format
sku variants[0].sku In variant

Cart/Checkout

WooCommerce Saleor Notes
Cart items in localStorage Checkout ID in localStorage Saleor stores server-side
add_to_cart checkoutLinesAdd Mutation
update_quantity checkoutLinesUpdate Mutation
remove_from_cart checkoutLinesDelete Mutation
Cart total (calculated) checkout.totalPrice Server-calculated

Testing Checklist

Phase 1: GraphQL Client

  • Apollo Client connects to Saleor API
  • Type generation works
  • Environment variables configured

Phase 2: Products

  • Product listing page shows products
  • Product detail page works with Serbian slug
  • Product detail page works with English slug
  • Language switcher works
  • Product images load
  • Prices display correctly (RSD)

Phase 3: Cart

  • Add to cart works
  • Update quantity works
  • Remove from cart works
  • Cart persists across page reloads
  • CartDrawer shows correct items

Phase 4: Checkout

  • Checkout page loads
  • Shipping address form works
  • Billing address form works
  • Order creation works
  • Order confirmation shows
  • COD payment method available

Phase 5: Cleanup

  • No WooCommerce dependencies
  • All tests pass
  • Build succeeds
  • No console errors

Rollback Plan

If issues arise, revert to WooCommerce:

git checkout master
npm install  # Restore WooCommerce deps

Post-Migration Tasks

  • Update deployment docs
  • Train team on Saleor dashboard
  • Set up monitoring
  • Configure CDN for images
  • Test on staging
  • Deploy to production
  • Monitor for errors
  • Collect user feedback

Resources

  • Saleor API URL: https://api.manoonoils.com/graphql/
  • Saleor Dashboard: https://dashboard.manoonoils.com/
  • Current Storefront: https://dev.manoonoils.com/
  • MinIO Assets: https://minio-api.nodecrew.me/saleor/

Migration Commands

# Start migration
git checkout -b feature/001-saleor-graphql-client

# After each phase
git add .
git commit -m "feat(saleor): Phase X - Description"
git push -u origin feature/001-saleor-graphql-client

# Create PR on GitHub
gh pr create --title "[1/5] Saleor GraphQL Client Setup" --base main

# Merge and continue
git checkout main
git pull origin main
git checkout -b feature/002-saleor-products