- 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
12 KiB
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
ProductCardcomponent to use Saleor data - Update
ProductDetailcomponent 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