feat: Improve settings layout and add addon integration design
🎨 Layout Changes: - Changed settings from boxed (max-w-5xl) to full-width - Consistent with Orders/Dashboard pages - Better use of space for complex forms 📝 Payments Page Reorder: - Manual payment methods first (Bank Transfer, COD) - Payment providers second (Stripe, PayPal) - Payment settings third (test mode, capture) - Test mode banner moved inside Payment Settings card 📚 Documentation: - Created SETUP_WIZARD_DESIGN.md - 5-step wizard flow (Store, Payments, Shipping, Taxes, Product) - Smart defaults and skip logic - Complete addon integration architecture 🔌 Addon Integration Design: - PaymentProviderRegistry with filter hooks - ShippingMethodRegistry with filter hooks - REST API endpoints for dynamic loading - Example addon implementations - Support for custom React components ✨ Key Features: - woonoow_payment_providers filter hook - woonoow_shipping_zones filter hook - Dynamic component loading from addons - OAuth flow support for payment gateways - Backward compatible with WooCommerce
This commit is contained in:
614
SETUP_WIZARD_DESIGN.md
Normal file
614
SETUP_WIZARD_DESIGN.md
Normal file
@@ -0,0 +1,614 @@
|
|||||||
|
# Setup Wizard Design Document
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
A guided onboarding experience for new WooNooW stores, helping merchants configure essential settings in 5-10 minutes.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
1. **Fast Setup** - Get store operational in < 10 minutes
|
||||||
|
2. **Smart Defaults** - Pre-configure based on location/industry
|
||||||
|
3. **Progressive** - Can skip and complete later
|
||||||
|
4. **Educational** - Teach key concepts without overwhelming
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wizard Flow (5 Steps)
|
||||||
|
|
||||||
|
### Step 1: Store Basics (2 min)
|
||||||
|
**Purpose:** Essential store identity
|
||||||
|
|
||||||
|
**Fields:**
|
||||||
|
- Store name (required)
|
||||||
|
- Store email (required)
|
||||||
|
- Country (required) → Auto-detects currency, timezone
|
||||||
|
- Industry (optional) → Suggests products/categories
|
||||||
|
|
||||||
|
**Smart Defaults:**
|
||||||
|
- Currency from country
|
||||||
|
- Timezone from country
|
||||||
|
- Language from browser
|
||||||
|
|
||||||
|
**Skip:** ❌ Cannot skip (required for operation)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 2: Payments (2 min)
|
||||||
|
**Purpose:** Enable at least one payment method
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
1. **Quick Start (Recommended)**
|
||||||
|
- Enable manual methods (Bank Transfer, COD)
|
||||||
|
- "Set up Stripe later" button
|
||||||
|
|
||||||
|
2. **Connect Payment Provider**
|
||||||
|
- Stripe (OAuth flow)
|
||||||
|
- PayPal (OAuth flow)
|
||||||
|
- Other providers
|
||||||
|
|
||||||
|
**Smart Defaults:**
|
||||||
|
- Bank Transfer: Enabled
|
||||||
|
- Cash on Delivery: Enabled if physical products
|
||||||
|
|
||||||
|
**Skip:** ⚠️ Warning: "Customers won't be able to checkout"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 3: Shipping (2 min)
|
||||||
|
**Purpose:** Configure basic shipping
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
1. **Simple Shipping**
|
||||||
|
- Flat rate for domestic
|
||||||
|
- Flat rate for international
|
||||||
|
- Free shipping threshold (optional)
|
||||||
|
|
||||||
|
2. **Advanced Setup**
|
||||||
|
- Multiple zones
|
||||||
|
- Calculated rates
|
||||||
|
- Carrier integrations
|
||||||
|
|
||||||
|
**Smart Defaults:**
|
||||||
|
- Domestic zone: Country from Step 1
|
||||||
|
- Flat rate: $10 (or currency equivalent)
|
||||||
|
|
||||||
|
**Skip:** ⚠️ Warning: "Only for digital products"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 4: Taxes (1 min)
|
||||||
|
**Purpose:** Basic tax compliance
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
1. **Auto-calculate** (Recommended)
|
||||||
|
- Based on customer location
|
||||||
|
- Standard rates from database
|
||||||
|
|
||||||
|
2. **Manual rates**
|
||||||
|
- Set fixed percentage
|
||||||
|
- Per-country rates
|
||||||
|
|
||||||
|
3. **No taxes**
|
||||||
|
- For tax-exempt stores
|
||||||
|
|
||||||
|
**Smart Defaults:**
|
||||||
|
- Auto-calculate: ON
|
||||||
|
- Tax rate from country (e.g., 10% for Indonesia VAT)
|
||||||
|
|
||||||
|
**Skip:** ✅ Can skip (configure later)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 5: First Product (3 min)
|
||||||
|
**Purpose:** Create sample product to test checkout
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
1. **Create Sample Product** (Recommended)
|
||||||
|
- Pre-filled with dummy data
|
||||||
|
- Can edit or delete later
|
||||||
|
- Helps test checkout flow
|
||||||
|
|
||||||
|
2. **Import Products**
|
||||||
|
- CSV upload
|
||||||
|
- WooCommerce import
|
||||||
|
|
||||||
|
3. **Skip for now**
|
||||||
|
- Add products later
|
||||||
|
|
||||||
|
**Smart Defaults:**
|
||||||
|
- Sample product based on industry from Step 1
|
||||||
|
|
||||||
|
**Skip:** ✅ Can skip
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technical Architecture
|
||||||
|
|
||||||
|
### Frontend Components
|
||||||
|
|
||||||
|
```
|
||||||
|
/routes/Setup/
|
||||||
|
├── index.tsx # Wizard container
|
||||||
|
├── StepProgress.tsx # Progress indicator (1/5, 2/5, etc)
|
||||||
|
├── steps/
|
||||||
|
│ ├── StoreBasics.tsx
|
||||||
|
│ ├── Payments.tsx
|
||||||
|
│ ├── Shipping.tsx
|
||||||
|
│ ├── Taxes.tsx
|
||||||
|
│ └── FirstProduct.tsx
|
||||||
|
└── Complete.tsx # Success screen
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backend API
|
||||||
|
|
||||||
|
```php
|
||||||
|
// REST API Endpoints
|
||||||
|
POST /woonoow/v1/setup/store-basics
|
||||||
|
POST /woonoow/v1/setup/payments
|
||||||
|
POST /woonoow/v1/setup/shipping
|
||||||
|
POST /woonoow/v1/setup/taxes
|
||||||
|
POST /woonoow/v1/setup/first-product
|
||||||
|
POST /woonoow/v1/setup/complete
|
||||||
|
|
||||||
|
// Option to track wizard state
|
||||||
|
update_option('wnw_setup_completed', true);
|
||||||
|
update_option('wnw_setup_step', 3); // Current step
|
||||||
|
```
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Zustand store for wizard state
|
||||||
|
interface SetupState {
|
||||||
|
currentStep: number;
|
||||||
|
completed: boolean;
|
||||||
|
data: {
|
||||||
|
storeBasics: StoreBasicsData;
|
||||||
|
payments: PaymentsData;
|
||||||
|
shipping: ShippingData;
|
||||||
|
taxes: TaxesData;
|
||||||
|
firstProduct: ProductData;
|
||||||
|
};
|
||||||
|
goToStep: (step: number) => void;
|
||||||
|
saveStep: (step: string, data: any) => Promise<void>;
|
||||||
|
completeSetup: () => Promise<void>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Payment Provider Integration
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
**Problem:** How to support payment addons (Stripe, PayPal, Xendit, Midtrans, etc)?
|
||||||
|
|
||||||
|
**Solution:** WordPress filter hooks + React components
|
||||||
|
|
||||||
|
### Backend: Payment Provider Registry
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
// includes/Compat/PaymentProviderRegistry.php
|
||||||
|
|
||||||
|
class PaymentProviderRegistry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all registered payment providers
|
||||||
|
* Allows addons to register their providers
|
||||||
|
*/
|
||||||
|
public static function get_providers(): array {
|
||||||
|
$providers = [
|
||||||
|
// Built-in manual methods
|
||||||
|
[
|
||||||
|
'id' => 'bacs',
|
||||||
|
'name' => 'Bank Transfer (BACS)',
|
||||||
|
'type' => 'manual',
|
||||||
|
'enabled' => true,
|
||||||
|
'icon' => 'banknote',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'cod',
|
||||||
|
'name' => 'Cash on Delivery',
|
||||||
|
'type' => 'manual',
|
||||||
|
'enabled' => true,
|
||||||
|
'icon' => 'banknote',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter: Allow addons to register payment providers
|
||||||
|
*
|
||||||
|
* @param array $providers List of payment providers
|
||||||
|
*
|
||||||
|
* Example addon usage:
|
||||||
|
* add_filter('woonoow_payment_providers', function($providers) {
|
||||||
|
* $providers[] = [
|
||||||
|
* 'id' => 'stripe',
|
||||||
|
* 'name' => 'Stripe Payments',
|
||||||
|
* 'type' => 'gateway',
|
||||||
|
* 'description' => 'Accept Visa, Mastercard, Amex',
|
||||||
|
* 'icon' => 'credit-card',
|
||||||
|
* 'enabled' => false,
|
||||||
|
* 'connected' => false,
|
||||||
|
* 'setup_url' => admin_url('admin.php?page=wc-settings&tab=checkout§ion=stripe'),
|
||||||
|
* 'component_url' => plugins_url('build/stripe-settings.js', __FILE__),
|
||||||
|
* 'fees' => '2.9% + $0.30 per transaction',
|
||||||
|
* ];
|
||||||
|
* return $providers;
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
return apply_filters('woonoow_payment_providers', $providers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get provider configuration
|
||||||
|
*/
|
||||||
|
public static function get_provider_config(string $provider_id): array {
|
||||||
|
$providers = self::get_providers();
|
||||||
|
foreach ($providers as $provider) {
|
||||||
|
if ($provider['id'] === $provider_id) {
|
||||||
|
return $provider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save provider settings
|
||||||
|
*/
|
||||||
|
public static function save_provider_settings(string $provider_id, array $settings): bool {
|
||||||
|
// Save to WooCommerce payment gateway settings
|
||||||
|
$gateway = WC()->payment_gateways()->payment_gateways()[$provider_id] ?? null;
|
||||||
|
if ($gateway) {
|
||||||
|
foreach ($settings as $key => $value) {
|
||||||
|
$gateway->update_option($key, $value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### REST API Endpoint
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
// includes/API/PaymentsController.php
|
||||||
|
|
||||||
|
class PaymentsController {
|
||||||
|
|
||||||
|
public static function register_routes() {
|
||||||
|
register_rest_route('woonoow/v1', '/payments/providers', [
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => [self::class, 'get_providers'],
|
||||||
|
'permission_callback' => [self::class, 'check_permission'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
register_rest_route('woonoow/v1', '/payments/providers/(?P<id>[a-zA-Z0-9_-]+)', [
|
||||||
|
'methods' => 'POST',
|
||||||
|
'callback' => [self::class, 'save_provider'],
|
||||||
|
'permission_callback' => [self::class, 'check_permission'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_providers(WP_REST_Request $request) {
|
||||||
|
$providers = PaymentProviderRegistry::get_providers();
|
||||||
|
return rest_ensure_response($providers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function save_provider(WP_REST_Request $request) {
|
||||||
|
$provider_id = $request->get_param('id');
|
||||||
|
$settings = $request->get_json_params();
|
||||||
|
|
||||||
|
$success = PaymentProviderRegistry::save_provider_settings($provider_id, $settings);
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
return rest_ensure_response(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new WP_Error('save_failed', 'Failed to save provider settings', ['status' => 500]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend: Dynamic Provider Loading
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/routes/Settings/Payments.tsx
|
||||||
|
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { api } from '@/lib/api';
|
||||||
|
|
||||||
|
export default function PaymentsPage() {
|
||||||
|
// Fetch providers from API (includes addon providers)
|
||||||
|
const { data: providers = [], isLoading } = useQuery({
|
||||||
|
queryKey: ['payment-providers'],
|
||||||
|
queryFn: () => api.get('/payments/providers'),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsLayout title="Payments">
|
||||||
|
{/* Manual Methods */}
|
||||||
|
<SettingsCard title="Manual Payment Methods">
|
||||||
|
{providers
|
||||||
|
.filter(p => p.type === 'manual')
|
||||||
|
.map(provider => (
|
||||||
|
<PaymentMethodCard key={provider.id} provider={provider} />
|
||||||
|
))}
|
||||||
|
</SettingsCard>
|
||||||
|
|
||||||
|
{/* Payment Gateways */}
|
||||||
|
<SettingsCard title="Payment Providers">
|
||||||
|
{providers
|
||||||
|
.filter(p => p.type === 'gateway')
|
||||||
|
.map(provider => (
|
||||||
|
<PaymentProviderCard key={provider.id} provider={provider} />
|
||||||
|
))}
|
||||||
|
</SettingsCard>
|
||||||
|
</SettingsLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Addon Example: Stripe Integration
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: WooNooW Stripe Addon
|
||||||
|
* Description: Stripe payment gateway for WooNooW
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Register Stripe provider
|
||||||
|
add_filter('woonoow_payment_providers', function($providers) {
|
||||||
|
$stripe_settings = get_option('woocommerce_stripe_settings', []);
|
||||||
|
|
||||||
|
$providers[] = [
|
||||||
|
'id' => 'stripe',
|
||||||
|
'name' => 'Stripe Payments',
|
||||||
|
'type' => 'gateway',
|
||||||
|
'description' => 'Accept Visa, Mastercard, Amex, and more',
|
||||||
|
'icon' => 'credit-card',
|
||||||
|
'enabled' => $stripe_settings['enabled'] === 'yes',
|
||||||
|
'connected' => !empty($stripe_settings['publishable_key']),
|
||||||
|
'setup_url' => admin_url('admin.php?page=wc-settings&tab=checkout§ion=stripe'),
|
||||||
|
'component_url' => plugins_url('build/stripe-settings.js', __FILE__),
|
||||||
|
'fees' => '2.9% + $0.30 per transaction',
|
||||||
|
'test_mode' => $stripe_settings['testmode'] === 'yes',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $providers;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Shipping Methods Integration
|
||||||
|
|
||||||
|
### Backend: Shipping Method Registry
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
// includes/Compat/ShippingMethodRegistry.php
|
||||||
|
|
||||||
|
class ShippingMethodRegistry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all shipping zones with methods
|
||||||
|
* Allows addons to register custom shipping methods
|
||||||
|
*/
|
||||||
|
public static function get_zones(): array {
|
||||||
|
$zones_data = [];
|
||||||
|
$zones = WC_Shipping_Zones::get_zones();
|
||||||
|
|
||||||
|
foreach ($zones as $zone) {
|
||||||
|
$zone_obj = new WC_Shipping_Zone($zone['id']);
|
||||||
|
$methods = $zone_obj->get_shipping_methods();
|
||||||
|
|
||||||
|
$zone_methods = [];
|
||||||
|
foreach ($methods as $method) {
|
||||||
|
$zone_methods[] = [
|
||||||
|
'id' => $method->id,
|
||||||
|
'instance_id' => $method->instance_id,
|
||||||
|
'title' => $method->title,
|
||||||
|
'enabled' => $method->enabled === 'yes',
|
||||||
|
'method_id' => $method->id,
|
||||||
|
'settings' => $method->instance_settings,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$zones_data[] = [
|
||||||
|
'id' => $zone['id'],
|
||||||
|
'name' => $zone['zone_name'],
|
||||||
|
'regions' => $zone['formatted_zone_location'],
|
||||||
|
'methods' => $zone_methods,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter: Allow addons to modify shipping zones
|
||||||
|
*
|
||||||
|
* @param array $zones_data List of shipping zones with methods
|
||||||
|
*
|
||||||
|
* Example addon usage:
|
||||||
|
* add_filter('woonoow_shipping_zones', function($zones) {
|
||||||
|
* // Add custom shipping method to each zone
|
||||||
|
* foreach ($zones as &$zone) {
|
||||||
|
* $zone['methods'][] = [
|
||||||
|
* 'id' => 'custom_shipping',
|
||||||
|
* 'title' => 'Custom Shipping',
|
||||||
|
* 'enabled' => true,
|
||||||
|
* 'component_url' => plugins_url('build/custom-shipping.js', __FILE__),
|
||||||
|
* ];
|
||||||
|
* }
|
||||||
|
* return $zones;
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
return apply_filters('woonoow_shipping_zones', $zones_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get available shipping methods (for adding to zones)
|
||||||
|
*/
|
||||||
|
public static function get_available_methods(): array {
|
||||||
|
$wc_methods = WC()->shipping()->get_shipping_methods();
|
||||||
|
$methods = [];
|
||||||
|
|
||||||
|
foreach ($wc_methods as $method) {
|
||||||
|
$methods[] = [
|
||||||
|
'id' => $method->id,
|
||||||
|
'title' => $method->method_title,
|
||||||
|
'description' => $method->method_description,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter: Allow addons to register custom shipping methods
|
||||||
|
*/
|
||||||
|
return apply_filters('woonoow_available_shipping_methods', $methods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### REST API Endpoint
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
// includes/API/ShippingController.php
|
||||||
|
|
||||||
|
class ShippingController {
|
||||||
|
|
||||||
|
public static function register_routes() {
|
||||||
|
register_rest_route('woonoow/v1', '/shipping/zones', [
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => [self::class, 'get_zones'],
|
||||||
|
'permission_callback' => [self::class, 'check_permission'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
register_rest_route('woonoow/v1', '/shipping/methods', [
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => [self::class, 'get_methods'],
|
||||||
|
'permission_callback' => [self::class, 'check_permission'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_zones(WP_REST_Request $request) {
|
||||||
|
$zones = ShippingMethodRegistry::get_zones();
|
||||||
|
return rest_ensure_response($zones);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_methods(WP_REST_Request $request) {
|
||||||
|
$methods = ShippingMethodRegistry::get_available_methods();
|
||||||
|
return rest_ensure_response($methods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend: Dynamic Shipping Loading
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/routes/Settings/Shipping.tsx
|
||||||
|
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { api } from '@/lib/api';
|
||||||
|
|
||||||
|
export default function ShippingPage() {
|
||||||
|
// Fetch zones from API (includes addon methods)
|
||||||
|
const { data: zones = [], isLoading } = useQuery({
|
||||||
|
queryKey: ['shipping-zones'],
|
||||||
|
queryFn: () => api.get('/shipping/zones'),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsLayout title="Shipping & Delivery">
|
||||||
|
{/* Shipping Zones */}
|
||||||
|
<SettingsCard title="Shipping Zones">
|
||||||
|
{zones.map(zone => (
|
||||||
|
<ShippingZoneCard key={zone.id} zone={zone} />
|
||||||
|
))}
|
||||||
|
</SettingsCard>
|
||||||
|
</SettingsLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Timeline
|
||||||
|
|
||||||
|
### Phase 1: Foundation (Week 1)
|
||||||
|
- [ ] Create PaymentProviderRegistry.php
|
||||||
|
- [ ] Create ShippingMethodRegistry.php
|
||||||
|
- [ ] Add REST API endpoints
|
||||||
|
- [ ] Update Payments.tsx to use API
|
||||||
|
- [ ] Update Shipping.tsx to use API
|
||||||
|
|
||||||
|
### Phase 2: Setup Wizard (Week 2)
|
||||||
|
- [ ] Create wizard UI components
|
||||||
|
- [ ] Implement 5-step flow
|
||||||
|
- [ ] Add smart defaults
|
||||||
|
- [ ] Add skip logic
|
||||||
|
- [ ] Create completion screen
|
||||||
|
|
||||||
|
### Phase 3: Addon Support (Week 3)
|
||||||
|
- [ ] Document filter hooks
|
||||||
|
- [ ] Create example addons
|
||||||
|
- [ ] Test with real payment gateways
|
||||||
|
- [ ] Test with shipping plugins
|
||||||
|
|
||||||
|
### Phase 4: Polish (Week 4)
|
||||||
|
- [ ] Add animations/transitions
|
||||||
|
- [ ] Improve error handling
|
||||||
|
- [ ] Add help documentation
|
||||||
|
- [ ] User testing & feedback
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
1. **Setup Time** - < 10 minutes average
|
||||||
|
2. **Completion Rate** - > 80% complete wizard
|
||||||
|
3. **Payment Setup** - > 90% enable at least one method
|
||||||
|
4. **Shipping Setup** - > 70% configure shipping
|
||||||
|
5. **First Sale** - < 24 hours after setup
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
1. **AI-Powered Suggestions**
|
||||||
|
- Detect industry from store name
|
||||||
|
- Suggest products based on industry
|
||||||
|
- Recommend payment methods by country
|
||||||
|
|
||||||
|
2. **Video Tutorials**
|
||||||
|
- Embedded help videos
|
||||||
|
- Step-by-step guides
|
||||||
|
|
||||||
|
3. **Templates**
|
||||||
|
- Pre-configured setups by industry
|
||||||
|
- "Fashion Store", "Digital Products", etc.
|
||||||
|
|
||||||
|
4. **Import Wizards**
|
||||||
|
- Import from Shopify
|
||||||
|
- Import from WooCommerce
|
||||||
|
- Import from CSV
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Questions & Answers
|
||||||
|
|
||||||
|
### Q: How do payment addons register themselves?
|
||||||
|
**A:** Use the `woonoow_payment_providers` filter hook to add providers to the list. The addon provides metadata (name, icon, fees) and optionally a React component URL for custom settings UI.
|
||||||
|
|
||||||
|
### Q: Can addons have custom settings UI?
|
||||||
|
**A:** Yes! Addons can provide a `component_url` that points to a React component. WooNooW will dynamically load and render it in the settings page.
|
||||||
|
|
||||||
|
### Q: How does the wizard handle incomplete setup?
|
||||||
|
**A:** The wizard saves progress at each step. Users can exit and resume later. A dashboard banner reminds them to complete setup.
|
||||||
|
|
||||||
|
### Q: Can merchants skip the wizard?
|
||||||
|
**A:** Yes, but they'll see a persistent banner until they complete essential steps (store basics, at least one payment method).
|
||||||
|
|
||||||
|
### Q: How do we handle payment gateway OAuth flows?
|
||||||
|
**A:** Payment addons provide a `setup_url` that can be an OAuth redirect. After completion, the addon updates its connection status via the API.
|
||||||
@@ -65,21 +65,51 @@ export default function PaymentsPage() {
|
|||||||
description="Manage how you get paid"
|
description="Manage how you get paid"
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
>
|
>
|
||||||
{/* Test Mode Banner */}
|
{/* Manual Payment Methods - First priority */}
|
||||||
{testMode && (
|
<SettingsCard
|
||||||
<div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
|
title="Manual Payment Methods"
|
||||||
<div className="flex items-center gap-2">
|
description="Accept payments outside your online store"
|
||||||
<span className="text-yellow-600 dark:text-yellow-400 font-semibold">
|
>
|
||||||
⚠️ Test Mode Active
|
<div className="space-y-4">
|
||||||
</span>
|
{manualMethods.map((method) => (
|
||||||
<span className="text-sm text-yellow-700 dark:text-yellow-300">
|
<div
|
||||||
No real charges will be processed
|
key={method.id}
|
||||||
</span>
|
className="border rounded-lg p-4"
|
||||||
</div>
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="p-2 bg-muted rounded-lg">
|
||||||
|
<Banknote className="h-5 w-5 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium">{method.name}</h3>
|
||||||
|
{method.enabled && (
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
|
Customers can choose this at checkout
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{method.enabled && (
|
||||||
|
<Button variant="ghost" size="sm">
|
||||||
|
<Settings className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<ToggleField
|
||||||
|
id={method.id}
|
||||||
|
label=""
|
||||||
|
checked={method.enabled}
|
||||||
|
onCheckedChange={() => toggleManualMethod(method.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</SettingsCard>
|
||||||
|
|
||||||
{/* Payment Providers */}
|
{/* Payment Providers - Second priority */}
|
||||||
<SettingsCard
|
<SettingsCard
|
||||||
title="Payment Providers"
|
title="Payment Providers"
|
||||||
description="Accept credit cards and digital wallets"
|
description="Accept credit cards and digital wallets"
|
||||||
@@ -141,55 +171,25 @@ export default function PaymentsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</SettingsCard>
|
</SettingsCard>
|
||||||
|
|
||||||
{/* Manual Payment Methods */}
|
{/* Payment Settings - Third priority (test mode, capture, etc) */}
|
||||||
<SettingsCard
|
|
||||||
title="Manual Payment Methods"
|
|
||||||
description="Accept payments outside your online store"
|
|
||||||
>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{manualMethods.map((method) => (
|
|
||||||
<div
|
|
||||||
key={method.id}
|
|
||||||
className="border rounded-lg p-4"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<div className="p-2 bg-muted rounded-lg">
|
|
||||||
<Banknote className="h-5 w-5 text-muted-foreground" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="font-medium">{method.name}</h3>
|
|
||||||
{method.enabled && (
|
|
||||||
<p className="text-sm text-muted-foreground mt-1">
|
|
||||||
Customers can choose this at checkout
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
{method.enabled && (
|
|
||||||
<Button variant="ghost" size="sm">
|
|
||||||
<Settings className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<ToggleField
|
|
||||||
id={method.id}
|
|
||||||
label=""
|
|
||||||
checked={method.enabled}
|
|
||||||
onCheckedChange={() => toggleManualMethod(method.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</SettingsCard>
|
|
||||||
|
|
||||||
{/* Payment Settings */}
|
|
||||||
<SettingsCard
|
<SettingsCard
|
||||||
title="Payment Settings"
|
title="Payment Settings"
|
||||||
description="General payment options"
|
description="General payment options"
|
||||||
>
|
>
|
||||||
|
{/* Test Mode Banner */}
|
||||||
|
{testMode && (
|
||||||
|
<div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4 mb-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-yellow-600 dark:text-yellow-400 font-semibold">
|
||||||
|
⚠️ Test Mode Active
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-yellow-700 dark:text-yellow-300">
|
||||||
|
No real charges will be processed
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<ToggleField
|
<ToggleField
|
||||||
id="testMode"
|
id="testMode"
|
||||||
label="Test mode"
|
label="Test mode"
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ export function SettingsLayout({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content - Full width like Orders/Dashboard */}
|
||||||
<div className="container max-w-5xl mx-auto px-4 py-8">
|
<div className="px-4 py-8 w-full">
|
||||||
{!onSave && (
|
{!onSave && (
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h1 className="text-2xl font-bold tracking-tight">{title}</h1>
|
<h1 className="text-2xl font-bold tracking-tight">{title}</h1>
|
||||||
@@ -68,13 +68,12 @@ export function SettingsLayout({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="flex items-center justify-center py-12">
|
||||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-6">{children}</div>
|
<div className="space-y-6 max-w-6xl">{children}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user