# Shipping Address Fields - Dynamic via Hooks ## Philosophy: Addon Responsibility, Not Hardcoding WooNooW should **listen to WooCommerce hooks** to determine which fields are required, not hardcode assumptions about Indonesian vs International shipping. --- ## The Problem with Hardcoding **Bad Approach (What we almost did):** ```javascript // ❌ DON'T DO THIS if (country === 'ID') { showSubdistrict = true; // Hardcoded assumption } ``` **Why it's bad:** - Assumes all Indonesian shipping needs subdistrict - Breaks if addon changes requirements - Not extensible for other countries - Violates separation of concerns --- ## The Right Approach: Listen to Hooks **WooCommerce Core Hooks:** ### 1. `woocommerce_checkout_fields` Filter Addons use this to add/modify/remove fields: ```php // Example: Indonesian Shipping Addon add_filter('woocommerce_checkout_fields', function($fields) { // Add subdistrict field $fields['shipping']['shipping_subdistrict'] = [ 'label' => __('Subdistrict'), 'required' => true, 'class' => ['form-row-wide'], 'priority' => 65, ]; return $fields; }); ``` ### 2. `woocommerce_default_address_fields` Filter Modifies default address fields: ```php add_filter('woocommerce_default_address_fields', function($fields) { // Make postal code required for UPS $fields['postcode']['required'] = true; return $fields; }); ``` ### 3. Field Validation Hooks ```php add_action('woocommerce_checkout_process', function() { if (empty($_POST['shipping_subdistrict'])) { wc_add_notice(__('Subdistrict is required'), 'error'); } }); ``` --- ## Implementation in WooNooW ### Backend: Expose Checkout Fields via API **New Endpoint:** `GET /checkout/fields` ```php // includes/Api/CheckoutController.php public function get_checkout_fields(WP_REST_Request $request) { // Get fields with all filters applied $fields = WC()->checkout()->get_checkout_fields(); // Format for frontend $formatted = []; foreach ($fields as $fieldset_key => $fieldset) { foreach ($fieldset as $key => $field) { $formatted[] = [ 'key' => $key, 'fieldset' => $fieldset_key, // billing, shipping, account, order 'type' => $field['type'] ?? 'text', 'label' => $field['label'] ?? '', 'placeholder' => $field['placeholder'] ?? '', 'required' => $field['required'] ?? false, 'class' => $field['class'] ?? [], 'priority' => $field['priority'] ?? 10, 'options' => $field['options'] ?? null, // For select fields 'custom' => $field['custom'] ?? false, // Custom field flag ]; } } // Sort by priority usort($formatted, function($a, $b) { return $a['priority'] <=> $b['priority']; }); return new WP_REST_Response($formatted, 200); } ``` ### Frontend: Dynamic Field Rendering **Create Order - Address Section:** ```typescript // Fetch checkout fields from API const { data: checkoutFields = [] } = useQuery({ queryKey: ['checkout-fields'], queryFn: () => api.get('/checkout/fields'), }); // Filter shipping fields const shippingFields = checkoutFields.filter( field => field.fieldset === 'shipping' ); // Render dynamically {shippingFields.map(field => { // Standard WooCommerce fields if (['first_name', 'last_name', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country'].includes(field.key)) { return ; } // Custom fields (e.g., subdistrict from addon) if (field.custom) { return ; } return null; })} ``` **Field Components:** ```typescript function StandardField({ field }) { return (
); } function CustomField({ field }) { // Handle custom field types (select, textarea, etc.) if (field.type === 'select') { return (
); } return ; } ``` --- ## How Addons Work ### Example: Indonesian Shipping Addon **Addon adds subdistrict field:** ```php add_filter('woocommerce_checkout_fields', function($fields) { $fields['shipping']['shipping_subdistrict'] = [ 'type' => 'select', 'label' => __('Subdistrict'), 'required' => true, 'class' => ['form-row-wide'], 'priority' => 65, 'options' => get_subdistricts(), // Addon provides this 'custom' => true, // Flag as custom field ]; return $fields; }); ``` **WooNooW automatically:** 1. Fetches fields via API 2. Sees `shipping_subdistrict` with `required: true` 3. Renders it in Create Order form 4. Validates it on submit **No hardcoding needed!** --- ## Benefits ✅ **Addon responsibility** - Addons declare their own requirements ✅ **No hardcoding** - WooNooW just renders what WooCommerce says ✅ **Extensible** - Works with ANY addon (Indonesian, UPS, custom) ✅ **Future-proof** - New addons work automatically ✅ **Separation of concerns** - Each addon manages its own fields --- ## Edge Cases ### Case 1: Subdistrict for Indonesian Shipping - Addon adds `shipping_subdistrict` field - WooNooW renders it - ✅ Works! ### Case 2: UPS Requires Postal Code - UPS addon sets `postcode.required = true` - WooNooW renders it as required - ✅ Works! ### Case 3: Custom Shipping Needs Extra Field - Addon adds `shipping_delivery_notes` field - WooNooW renders it - ✅ Works! ### Case 4: No Custom Fields - Standard WooCommerce fields only - WooNooW renders them - ✅ Works! --- ## Implementation Plan 1. **Backend:** - Create `GET /checkout/fields` endpoint - Return fields with all filters applied - Include field metadata (type, required, options, etc.) 2. **Frontend:** - Fetch checkout fields on Create Order page - Render fields dynamically based on API response - Handle standard + custom field types - Validate based on `required` flag 3. **Testing:** - Test with no addons (standard fields only) - Test with Indonesian shipping addon (subdistrict) - Test with UPS addon (postal code required) - Test with custom addon (custom fields) --- ## Next Steps 1. Create `CheckoutController.php` with `get_checkout_fields` endpoint 2. Update Create Order to fetch and render fields dynamically 3. Test with Indonesian shipping addon 4. Document for addon developers