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
This commit is contained in:
Dwindi Ramadhana
2025-11-26 16:18:43 +07:00
parent 909bddb23d
commit f397ef850f
69 changed files with 12481 additions and 156 deletions

View File

@@ -0,0 +1,299 @@
/**
* WooNooW Customer SPA - Design Tokens
*
* All styling is controlled via CSS custom properties (design tokens).
* These values are injected from PHP settings via ThemeProvider.
*/
:root {
/* ========================================
* COLORS
* ======================================== */
/* Brand Colors (injected from settings) */
--color-primary: #3B82F6;
--color-secondary: #8B5CF6;
--color-accent: #10B981;
--color-background: #FFFFFF;
--color-text: #1F2937;
/* Color Shades (auto-generated) */
--color-primary-50: #EFF6FF;
--color-primary-100: #DBEAFE;
--color-primary-200: #BFDBFE;
--color-primary-300: #93C5FD;
--color-primary-400: #60A5FA;
--color-primary-500: var(--color-primary);
--color-primary-600: #2563EB;
--color-primary-700: #1D4ED8;
--color-primary-800: #1E40AF;
--color-primary-900: #1E3A8A;
/* Semantic Colors */
--color-success: #10B981;
--color-warning: #F59E0B;
--color-error: #EF4444;
--color-info: #3B82F6;
/* Neutral Colors */
--color-gray-50: #F9FAFB;
--color-gray-100: #F3F4F6;
--color-gray-200: #E5E7EB;
--color-gray-300: #D1D5DB;
--color-gray-400: #9CA3AF;
--color-gray-500: #6B7280;
--color-gray-600: #4B5563;
--color-gray-700: #374151;
--color-gray-800: #1F2937;
--color-gray-900: #111827;
/* ========================================
* TYPOGRAPHY
* ======================================== */
/* Font Families (injected from settings) */
--font-heading: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-body: 'Lora', Georgia, serif;
--font-mono: 'IBM Plex Mono', 'Courier New', monospace;
/* Font Weights */
--font-weight-heading: 700;
--font-weight-body: 400;
--font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
/* Font Sizes (8px base scale) */
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
--text-4xl: 2.25rem; /* 36px */
--text-5xl: 3rem; /* 48px */
--text-6xl: 3.75rem; /* 60px */
/* Line Heights */
--line-height-none: 1;
--line-height-tight: 1.25;
--line-height-snug: 1.375;
--line-height-normal: 1.5;
--line-height-relaxed: 1.625;
--line-height-loose: 2;
/* ========================================
* SPACING (8px grid system)
* ======================================== */
--space-0: 0;
--space-1: 0.5rem; /* 8px */
--space-2: 1rem; /* 16px */
--space-3: 1.5rem; /* 24px */
--space-4: 2rem; /* 32px */
--space-5: 2.5rem; /* 40px */
--space-6: 3rem; /* 48px */
--space-8: 4rem; /* 64px */
--space-10: 5rem; /* 80px */
--space-12: 6rem; /* 96px */
--space-16: 8rem; /* 128px */
--space-20: 10rem; /* 160px */
--space-24: 12rem; /* 192px */
/* ========================================
* BORDER RADIUS
* ======================================== */
--radius-none: 0;
--radius-sm: 0.25rem; /* 4px */
--radius-md: 0.5rem; /* 8px */
--radius-lg: 1rem; /* 16px */
--radius-xl: 1.5rem; /* 24px */
--radius-2xl: 2rem; /* 32px */
--radius-full: 9999px;
/* ========================================
* SHADOWS
* ======================================== */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
/* ========================================
* TRANSITIONS
* ======================================== */
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-slow: 350ms cubic-bezier(0.4, 0, 0.2, 1);
/* ========================================
* BREAKPOINTS (for reference in JS)
* ======================================== */
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
/* ========================================
* Z-INDEX SCALE
* ======================================== */
--z-base: 0;
--z-dropdown: 1000;
--z-sticky: 1020;
--z-fixed: 1030;
--z-modal-backdrop: 1040;
--z-modal: 1050;
--z-popover: 1060;
--z-tooltip: 1070;
}
/* ========================================
* DARK MODE
* ======================================== */
@media (prefers-color-scheme: dark) {
:root {
--color-background: #1F2937;
--color-text: #F9FAFB;
/* Invert gray scale for dark mode */
--color-gray-50: #111827;
--color-gray-100: #1F2937;
--color-gray-200: #374151;
--color-gray-300: #4B5563;
--color-gray-400: #6B7280;
--color-gray-500: #9CA3AF;
--color-gray-600: #D1D5DB;
--color-gray-700: #E5E7EB;
--color-gray-800: #F3F4F6;
--color-gray-900: #F9FAFB;
}
}
/* ========================================
* BASE STYLES
* ======================================== */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: var(--font-body);
font-size: var(--text-base);
line-height: var(--line-height-normal);
color: var(--color-text);
background-color: var(--color-background);
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-heading);
font-weight: var(--font-weight-heading);
line-height: var(--line-height-tight);
color: var(--color-text);
}
h1 {
font-size: var(--text-5xl);
}
h2 {
font-size: var(--text-4xl);
}
h3 {
font-size: var(--text-3xl);
}
h4 {
font-size: var(--text-2xl);
}
h5 {
font-size: var(--text-xl);
}
h6 {
font-size: var(--text-lg);
}
a {
color: var(--color-primary);
text-decoration: none;
transition: color var(--transition-fast);
}
a:hover {
color: var(--color-primary-600);
}
button {
font-family: var(--font-heading);
cursor: pointer;
}
img {
max-width: 100%;
height: auto;
}
/* ========================================
* UTILITY CLASSES
* ======================================== */
.container {
width: 100%;
margin-left: auto;
margin-right: auto;
padding-left: var(--space-4);
padding-right: var(--space-4);
}
@media (min-width: 640px) {
.container {
max-width: 640px;
}
}
@media (min-width: 768px) {
.container {
max-width: 768px;
}
}
@media (min-width: 1024px) {
.container {
max-width: 1024px;
}
}
@media (min-width: 1280px) {
.container {
max-width: 1280px;
}
}
@media (min-width: 1536px) {
.container {
max-width: 1536px;
}
}