feat: implement multiple saved addresses with modal selector in checkout

- Add AddressController with full CRUD API for saved addresses
- Implement address management UI in My Account > Addresses
- Add modal-based address selector in checkout (Tokopedia-style)
- Hide checkout forms when saved address is selected
- Add search functionality in address modal
- Auto-select default addresses on page load
- Fix variable products to show 'Select Options' instead of 'Add to Cart'
- Add admin toggle for multiple addresses feature
- Clean up debug logs and fix TypeScript errors
This commit is contained in:
Dwindi Ramadhana
2025-12-26 01:16:11 +07:00
parent 9ac09582d2
commit 100f9cce55
27 changed files with 2492 additions and 205 deletions

View File

@@ -158,6 +158,37 @@ class CheckoutController {
return ['error' => $order->get_error_message()];
}
// Set customer ID if user is logged in
if (is_user_logged_in()) {
$user_id = get_current_user_id();
$order->set_customer_id($user_id);
// Update user's billing information from checkout data
if (!empty($payload['billing'])) {
$billing = $payload['billing'];
// Update first name and last name
if (!empty($billing['first_name'])) {
update_user_meta($user_id, 'first_name', sanitize_text_field($billing['first_name']));
update_user_meta($user_id, 'billing_first_name', sanitize_text_field($billing['first_name']));
}
if (!empty($billing['last_name'])) {
update_user_meta($user_id, 'last_name', sanitize_text_field($billing['last_name']));
update_user_meta($user_id, 'billing_last_name', sanitize_text_field($billing['last_name']));
}
// Update billing phone
if (!empty($billing['phone'])) {
update_user_meta($user_id, 'billing_phone', sanitize_text_field($billing['phone']));
}
// Update billing email
if (!empty($billing['email'])) {
update_user_meta($user_id, 'billing_email', sanitize_email($billing['email']));
}
}
}
// Add items
foreach ($payload['items'] as $line) {
$product = $this->load_product($line);
@@ -352,7 +383,7 @@ class CheckoutController {
if (!WC()->customer) { WC()->customer = new \WC_Customer(get_current_user_id(), true); }
if (!WC()->cart) { WC()->cart = new \WC_Cart(); }
// Address context for taxes/shipping rules
// Address context for taxes/shipping rules - set temporarily without saving to user profile
$ship = !empty($payload['shipping']) ? $payload['shipping'] : $payload['billing'];
if (!empty($payload['billing'])) {
foreach (['country','state','postcode','city','address_1','address_2'] as $k) {
@@ -370,7 +401,8 @@ class CheckoutController {
}
}
}
WC()->customer->save();
// DO NOT save customer data - only use for quote calculation
// WC()->customer->save(); // REMOVED - should not update user profile during checkout
WC()->cart->empty_cart(true);
foreach ($payload['items'] as $line) {

View File

@@ -24,6 +24,7 @@ use WooNooW\Api\NewsletterController;
use WooNooW\Frontend\ShopController;
use WooNooW\Frontend\CartController as FrontendCartController;
use WooNooW\Frontend\AccountController;
use WooNooW\Frontend\AddressController;
use WooNooW\Frontend\HookBridge;
use WooNooW\Api\Controllers\SettingsController;
use WooNooW\Api\Controllers\CartController as ApiCartController;
@@ -38,20 +39,7 @@ class Routes {
// Initialize CartController auth bypass (must be before rest_api_init)
FrontendCartController::init();
// Log ALL REST API requests to debug routing
add_filter('rest_pre_dispatch', function($result, $server, $request) {
$route = $request->get_route();
$method = $request->get_method();
$result_type = is_null($result) ? 'NULL (will call handler)' : 'NON-NULL (handler bypassed!)';
error_log("WooNooW REST: {$method} {$route} - Result: {$result_type}");
if (!is_null($result)) {
error_log("WooNooW REST: BYPASSED! Result type: " . gettype($result));
}
return $result;
}, 10, 3);
add_action('rest_api_init', function () {
error_log('WooNooW Routes: rest_api_init hook fired');
$namespace = 'woonoow/v1';
// Auth endpoints (public - no permission check)
@@ -82,10 +70,6 @@ class Routes {
$settings_controller = new SettingsController();
$settings_controller->register_routes();
// Cart controller (API) - DISABLED: Using Frontend CartController instead to avoid route conflicts
// $api_cart_controller = new ApiCartController();
// $api_cart_controller->register_routes();
// Payments controller
$payments_controller = new PaymentsController();
$payments_controller->register_routes();
@@ -127,9 +111,7 @@ class Routes {
$activity_log_controller->register_routes();
// Products controller
error_log('WooNooW Routes: Registering ProductsController routes');
ProductsController::register_routes();
error_log('WooNooW Routes: ProductsController routes registered');
// Coupons controller
CouponsController::register_routes();
@@ -141,12 +123,11 @@ class Routes {
NewsletterController::register_routes();
// Frontend controllers (customer-facing)
error_log('WooNooW Routes: Registering Frontend controllers');
ShopController::register_routes();
FrontendCartController::register_routes();
AccountController::register_routes();
AddressController::register_routes();
HookBridge::register_routes();
error_log('WooNooW Routes: Frontend controllers registered');
});
}
}