Files
manoon-headless/ONE-PAGE-CHECKOUT-PLAN.md
2026-03-28 17:51:06 +02:00

11 KiB

One-Page Checkout Implementation Plan

Branch: feature/one-page-checkout
Status: In Development
Priority: High
Phone Requirement: Required (not optional)


Overview

Convert the current two-phase checkout into a streamlined one-page checkout experience where customers can see all fields at once and complete their order in a single action.

Current State

  • Phase 1: Collect email, shipping address → fetch shipping methods
  • Phase 2: Select shipping method, billing address → complete order
  • Total API calls: 6-7 sequential requests across 2 user interactions

Target State

  • Single Page: All fields visible simultaneously
  • Dynamic updates: Shipping methods fetch automatically when address changes
  • Single submit: One "Complete Order" button
  • Optimized API: 3-4 sequential steps (parallel where possible)

Requirements

Must-Have

  • All checkout fields visible on single page
  • Phone number is required (strict validation)
  • Shipping methods fetch automatically (debounced) when address changes
  • Real-time total calculation (updates when shipping method selected)
  • Single "Complete Order" submit button
  • Section-based validation with inline errors
  • Auto-scroll to first error on validation failure
  • Preserve form data on error

UX Requirements

  • Clear visual hierarchy (Contact → Shipping → Billing → Shipping Method → Payment)
  • Collapsible sections (optional - all expanded by default)
  • Loading states for shipping method fetching
  • Disabled submit button until all required fields valid
  • Success confirmation page (existing)

Technical Requirements

  • Debounced shipping method API calls (500ms)
  • Optimistic UI updates where possible
  • Proper error handling per section
  • Analytics events for checkout steps
  • Mobile-responsive layout

UI Layout

Left Column (Form - 60% width on desktop)

┌─────────────────────────────────────┐
│  1. Contact Information             │
│  ├─ Email * [________________]      │
│  └─ Phone * [________________]      │
│     [+381... format hint]           │
├─────────────────────────────────────┤
│  2. Shipping Address                │
│  ├─ First Name * [____________]     │
│  ├─ Last Name * [_____________]     │
│  ├─ Country * [▼ Serbia ▼]          │
│  ├─ Street Address * [________]     │
│  ├─ Apt/Suite [______________]      │
│  ├─ City * [_________________]      │
│  └─ Postal Code * [__________]      │
├─────────────────────────────────────┤
│  3. Billing Address                 │
│  [✓] Same as shipping address       │
│  (Fields hidden when checked)       │
├─────────────────────────────────────┤
│  4. Shipping Method                 │
│  (Loading... / Select to see        │
│   available options)                │
│  ○ Standard (2-3 days)     400 RSD  │
│  ○ Express (1-2 days)      800 RSD  │
├─────────────────────────────────────┤
│  5. Payment Method                  │
│  ● Cash on Delivery                 │
│  (Additional payment methods TBD)   │
├─────────────────────────────────────┤
│  [ Complete Order - 3,600 RSD ]     │
│  Loading spinner when processing    │
└─────────────────────────────────────┘

Right Column (Order Summary - 40% width on desktop)

┌─────────────────────────────────────┐
│  Order Summary                      │
├─────────────────────────────────────┤
│  Product Image  Serum x1    3,200   │
│                                 RSD │
├─────────────────────────────────────┤
│  Subtotal                 3,200 RSD │
│  Shipping                   400 RSD │
│  ─────────────────────────────────  │
│  Total                    3,600 RSD │
└─────────────────────────────────────┘

Mobile Layout

Single column, stacked sections with sticky order summary at bottom.


Technical Implementation

State Management

// Form state (existing)
const [shippingAddress, setShippingAddress] = useState<AddressForm>({...});
const [billingAddress, setBillingAddress] = useState<AddressForm>({...});
const [sameAsShipping, setSameAsShipping] = useState(true);

// New state
const [paymentMethod, setPaymentMethod] = useState<string>("cod");
const [errors, setErrors] = useState<ValidationErrors>({
  contact: null,
  shipping: null,
  billing: null,
  shippingMethod: null,
  general: null,
});

Debounced Shipping Method Fetching

useEffect(() => {
  if (!isAddressComplete(shippingAddress)) return;
  
  const timer = setTimeout(() => {
    fetchShippingMethods();
  }, 500); // 500ms debounce
  
  return () => clearTimeout(timer);
}, [shippingAddress]);

Validation Schema

const validationRules = {
  email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
  phone: (value) => {
    // Country-specific validation
    // Serbia: +381 XX XXX XXXX
    // Bosnia: +387 XX XXX XXX
    // etc.
  },
  required: (value) => value.trim().length > 0,
  postalCode: (value, country) => {
    // Country-specific postal code validation
  },
};

API Call Sequence

Optimized Flow (parallel + sequential):

Step 1: Validation (client-side)
  ├─ Validate all fields
  └─ Show inline errors

Step 2: Parallel Independent Calls
  ├─ Update Email
  └─ Update Shipping Address
  (Both can run simultaneously)

Step 3: Conditional Call
  └─ Update Billing Address (if different from shipping)

Step 4: Sequential Dependent Calls
  ├─ Update Shipping Method
  ├─ Update Metadata (phone, language, payment method)
  └─ Complete Checkout

Total: 4 sequential steps vs current 7+

Error Handling Strategy

Field-level:

  • Real-time validation on blur
  • Visual indicators (red border, error message)
  • Prevent submit if validation fails

Section-level:

  • Group errors by section
  • Show section header in red if has errors
  • Expand section if collapsed and has errors

Form-level:

  • On submit: validate all fields
  • If errors: scroll to first error, show summary
  • If API error: show in relevant section, preserve data

API-level:

  • Map Saleor errors to form fields when possible
  • Generic error: show at top of form
  • Network error: show retry button

Files to Modify

Primary Files

  1. /src/app/[locale]/checkout/page.tsx

    • Major refactor of checkout flow
    • Combine Phase 1 & Phase 2 into single component
    • Add debounced shipping method fetching
    • Implement section-based validation
    • Optimize API call sequence
  2. /src/lib/saleor/mutations/Checkout.ts

    • Ensure all mutations available
    • Add metadata update mutation if needed
  3. /src/lib/saleor/queries/Checkout.ts

    • Ensure checkout query returns shipping methods

Translation Files

  1. /messages/sr.json (and other language files)
    • Add new translation keys for one-page checkout
    • Section headers
    • Validation messages
    • Button labels

Styling

  1. /src/app/globals.css (or Tailwind config)
    • Ensure consistent form styling
    • Add validation state styles
    • Loading spinner styles

Implementation Phases

Phase 1: Core Structure (Day 1-2)

  • Refactor checkout page layout
  • Display all sections simultaneously
  • Keep existing form logic working
  • Test existing flow still works

Phase 2: Dynamic Shipping Methods (Day 3)

  • Implement debounced fetching
  • Add loading states
  • Display shipping methods inline
  • Update total when method selected

Phase 3: Validation & Error Handling (Day 4)

  • Implement field-level validation
  • Add section-based error display
  • Auto-scroll to errors
  • Test all validation scenarios

Phase 4: Optimization (Day 5)

  • Optimize API call sequence
  • Add parallel mutation execution
  • Improve loading states
  • Add optimistic updates

Phase 5: Polish (Day 6)

  • Mobile responsiveness
  • Analytics events
  • Accessibility improvements
  • Final testing

Testing Checklist

Functionality Tests

  • Fill all fields, submit successfully
  • Verify order created in Saleor
  • Verify emails sent
  • Change shipping method, verify total updates
  • Change address, verify shipping methods refetch

Validation Tests

  • Submit with empty email → email error
  • Submit with empty phone → phone error
  • Submit with invalid email format → format error
  • Submit with invalid phone → format error
  • Submit with empty required fields → field errors
  • Submit without selecting shipping method → shipping error

Edge Cases

  • Slow network (test debouncing)
  • No shipping methods available
  • API failure during submission
  • Partial API failure (some mutations succeed)
  • Browser refresh (preserve data?)

Mobile Tests

  • Layout works on iPhone SE
  • Layout works on iPhone 14 Pro Max
  • Touch targets large enough
  • Scroll behavior smooth

Accessibility Tests

  • Tab navigation works
  • Screen reader friendly
  • Error announcements
  • Focus management

Rollout Strategy

  1. Development: Complete on feature branch
  2. Testing: Local testing with all scenarios
  3. Staging: Deploy to dev.manoonoils.com
  4. Monitoring: Check for errors, conversion rates
  5. Production: Merge to master and deploy

Success Metrics

  • Conversion Rate: Should increase (fewer steps = less drop-off)
  • Time to Complete: Should decrease (single page vs two phases)
  • Error Rate: Should decrease (better validation)
  • Mobile Completion: Should improve (optimized for mobile)

Future Enhancements (Out of Scope)

  • Save addresses for logged-in users
  • Address autocomplete (Google Maps)
  • Multiple payment methods (Stripe, etc.)
  • Guest checkout improvements
  • Order notes/comments field
  • Gift wrapping options
  • Promo code input

Notes

  • Phone number is strictly required - validate format per country
  • Keep existing checkout success page
  • Maintain multi-language support
  • Ensure analytics tracking works
  • Don't break existing cart functionality

Created: March 28, 2026
Branch: feature/one-page-checkout
Next Step: Start Phase 1 - Core Structure