# 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 ```bash 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 ```graphql # 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 ```graphql # 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 ```graphql # 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 ```bash npm uninstall @woocommerce/woocommerce-rest-api ``` ### Environment Variables to Update ```bash # 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: ```bash 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 ```bash # 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 ```