Files
WooNooW/REAL_FIX.md
Dwindi Ramadhana f397ef850f feat: Add product images support with WP Media Library integration
- Add WP Media Library integration for product and variation images
- Support images array (URLs) conversion to attachment IDs
- Add images array to API responses (Admin & Customer SPA)
- Implement drag-and-drop sortable images in Admin product form
- Add image gallery thumbnails in Customer SPA product page
- Initialize WooCommerce session for guest cart operations
- Fix product variations and attributes display in Customer SPA
- Add variation image field in Admin SPA

Changes:
- includes/Api/ProductsController.php: Handle images array, add to responses
- includes/Frontend/ShopController.php: Add images array for customer SPA
- includes/Frontend/CartController.php: Initialize WC session for guests
- admin-spa/src/lib/wp-media.ts: Add openWPMediaGallery function
- admin-spa/src/routes/Products/partials/tabs/GeneralTab.tsx: WP Media + sortable images
- admin-spa/src/routes/Products/partials/tabs/VariationsTab.tsx: Add variation image field
- customer-spa/src/pages/Product/index.tsx: Add gallery thumbnails display
2025-11-26 16:18:43 +07:00

5.1 KiB

Real Fix - Different Approach

Problem Analysis

After multiple failed attempts with aspect-ratio and padding-bottom techniques, the root issues were:

  1. CSS aspect-ratio property - Unreliable with absolute positioning across browsers
  2. Padding-bottom technique - Not rendering correctly in this specific setup
  3. Missing slug parameter - Backend API didn't support filtering by product slug

Solution: Fixed Height Approach

Why This Works

Instead of trying to maintain aspect ratios dynamically, use fixed heights with object-cover:

// Simple, reliable approach
<div className="w-full h-64 overflow-hidden bg-gray-100">
  <img 
    src={product.image}
    alt={product.name}
    className="w-full h-full object-cover object-center"
  />
</div>

Benefits:

  • Predictable rendering
  • Works across all browsers
  • No complex CSS tricks
  • object-cover handles image fitting
  • Simple to understand and maintain

Heights Used

  • Classic Layout: h-64 (256px)
  • Modern Layout: h-64 (256px)
  • Boutique Layout: h-80 (320px) - taller for elegance
  • Launch Layout: h-64 (256px)
  • Product Page: h-96 (384px) - larger for detail view

Changes Made

1. ProductCard Component

File: customer-spa/src/components/ProductCard.tsx

Changed:

// Before (didn't work)
<div style={{ paddingBottom: '100%' }}>
  <img className="absolute inset-0 w-full h-full object-cover" />
</div>

// After (works!)
<div className="w-full h-64 overflow-hidden bg-gray-100">
  <img className="w-full h-full object-cover object-center" />
</div>

Applied to:

  • Classic layout
  • Modern layout
  • Boutique layout (h-80)
  • Launch layout

2. Product Page

File: customer-spa/src/pages/Product/index.tsx

Image Container:

<div className="w-full h-96 rounded-lg overflow-hidden bg-gray-100">
  <img className="w-full h-full object-cover object-center" />
</div>

Query Fix: Added proper error handling and logging:

queryFn: async () => {
  if (!slug) return null;
  
  const response = await apiClient.get<ProductsResponse>(
    apiClient.endpoints.shop.products,
    { slug, per_page: 1 }
  );
  
  console.log('Product API Response:', response);
  
  if (response && response.products && response.products.length > 0) {
    return response.products[0];
  }
  
  return null;
}

3. Backend API - Slug Support

File: includes/Frontend/ShopController.php

Added slug parameter:

'slug' => [
    'default'           => '',
    'sanitize_callback' => 'sanitize_text_field',
],

Added slug filtering:

// Add slug filter (for single product lookup)
if (!empty($slug)) {
    $args['name'] = $slug;
}

How it works:

  • WordPress WP_Query accepts name parameter
  • name matches the post slug exactly
  • Returns single product when slug is provided

Why Previous Attempts Failed

Attempt 1: aspect-square class

<div className="aspect-square">
  <img className="absolute inset-0" />
</div>

Problem: CSS aspect-ratio property doesn't work reliably with absolute positioning.

Attempt 2: padding-bottom technique

<div style={{ paddingBottom: '100%' }}>
  <img className="absolute inset-0" />
</div>

Problem: The padding creates space, but the image positioning wasn't working in this specific component structure.

Why Fixed Height Works

<div className="h-64">
  <img className="w-full h-full object-cover" />
</div>

Success:

  • Container has explicit height
  • Image fills container with w-full h-full
  • object-cover ensures proper cropping
  • No complex positioning needed

Testing

Test Shop Page Images

  1. Go to /shop
  2. All product images should fill their containers completely
  3. Images should be 256px tall (or 320px for Boutique)
  4. No gaps or empty space

Test Product Page

  1. Click any product
  2. Product image should display (384px tall)
  3. Image should fill the container
  4. Console should show API response with product data

Check Console

Open browser console and navigate to a product page. You should see:

Product API Response: {
  products: [{
    id: 123,
    name: "Product Name",
    slug: "product-slug",
    image: "https://..."
  }],
  total: 1
}

Summary

Root Cause: CSS aspect-ratio techniques weren't working in this setup.

Solution: Use simple fixed heights with object-cover.

Result:

  • Images fill containers properly
  • Product page loads images
  • Backend supports slug filtering
  • Simple, maintainable code

Files Modified:

  1. customer-spa/src/components/ProductCard.tsx - Fixed all 4 layouts
  2. customer-spa/src/pages/Product/index.tsx - Fixed image container and query
  3. includes/Frontend/ShopController.php - Added slug parameter support

Lesson Learned

Sometimes the simplest solution is the best. Instead of complex CSS tricks:

  • Use fixed heights when appropriate
  • Let object-cover handle image fitting
  • Keep code simple and maintainable

This approach is:

  • More reliable
  • Easier to debug
  • Better browser support
  • Simpler to understand