array( 'label' => __('Pricing batch (REST)', 'ai-eshop-optimizer'), 'desc' => __('Batch effective-price lookup via /aieo/v1/pricing/batch. Read-only; safe to leave on.', 'ai-eshop-optimizer'), ), 'wishlist' => array( 'label' => __('Wishlist heart', 'ai-eshop-optimizer'), 'desc' => __('Heart toggle, optimistic paint with rollback, cookie sync, multi-heart sync, "See your favourites" label.', 'ai-eshop-optimizer'), ), 'waitlist' => array( 'label' => __('Waitlist', 'ai-eshop-optimizer'), 'desc' => __('"Notify me when back in stock" form: email validation, loading overlay, WC variation integration.', 'ai-eshop-optimizer'), ), 'complementary' => array( 'label' => __('Complementary products', 'ai-eshop-optimizer'), 'desc' => __('Multi-checkbox batch add-to-cart. Routes fragments through the IAPI cart store.', 'ai-eshop-optimizer'), ), 'reco' => array( 'label' => __('Recommendations (events only)', 'ai-eshop-optimizer'), 'desc' => __('No standalone shim — emits aieo:reco:rendered after each section mounts. Per-card UI lives in the wishlist/swatches modules.', 'ai-eshop-optimizer'), ), 'ratings' => array( 'label' => __('Star ratings', 'ai-eshop-optimizer'), 'desc' => __('5-star product rating widget with per-product aggregate. Optimistic paint + rollback, batch REST hydration on archives, anonymous-cookie scoping.', 'ai-eshop-optimizer'), ), 'swatches' => array( 'label' => __('Swatches', 'ai-eshop-optimizer'), 'desc' => __('Single-select hydration only. Multi-select / quick-view modal / cart-sync markers NOT IMPLEMENTED — see ROADMAP.md, Phase C-θ.', 'ai-eshop-optimizer'), ), 'sticky_atc' => array( 'label' => __('Sticky add-to-cart', 'ai-eshop-optimizer'), 'desc' => __('IO-based visibility + cart-store add. Multi-select sticky / persistent sticky / smart auto-pick NOT IMPLEMENTED — Phase C-ι.', 'ai-eshop-optimizer'), ), 'side_cart' => array( 'label' => __('Side cart', 'ai-eshop-optimizer'), 'desc' => __('The whole drawer: the cart, the recommendation rails, recently viewed, favourites, quick view with variation dropdowns, the free-gift pickers and the product form\'s AJAX add. The default on block themes.', 'ai-eshop-optimizer'), ), ); } private function module_statuses() { if (method_exists('AIEO_DMM', 'get_iapi_module_statuses')) { return AIEO_DMM::get_iapi_module_statuses(); } return array(); } public function render_page() { if (!current_user_can(self::CAPABILITY)) { wp_die(esc_html__('You do not have permission to access this page.', 'ai-eshop-optimizer')); } if (class_exists('AIEO_Chrome') && AIEO_Chrome::on() && method_exists('\Rooster\Admin\Board', 'page')) { \Rooster\Admin\Board::page(array($this, 'render_body')); return; } $this->render_body(); } public function render_body() { $master_on = (bool) get_option('rooster_iapi_enabled', get_option('roosterx_iapi_enabled', false)); $modules = $this->modules(); $statuses = $this->module_statuses(); $saved = isset($_GET['aieo_iapi_saved']) && $_GET['aieo_iapi_saved'] === '1'; $tones = array('production' => 'success', 'scaffold' => 'warning'); echo '
'; $head = class_exists('AIEO_Chrome') ? AIEO_Chrome::page_head(self::MENU_SLUG, __('Interactivity (IAPI)', 'ai-eshop-optimizer'), 'zap', self::MENU_SLUG) : ''; echo '' !== $head ? $head : '

' . esc_html__('Interactivity (IAPI)', 'ai-eshop-optimizer') . '


'; if ($saved) { echo \Rooster\Admin\Ui::notice(esc_html__('IAPI settings saved.', 'ai-eshop-optimizer'), 'success', array('dismissible' => true)); } $board = class_exists('\Rooster\Admin\Board') && \Rooster\Admin\Board::start(self::MENU_SLUG); list($open, $close) = AIEO_DMM_UI::card(array( 'anchor' => 'iapi-master', 'icon' => 'zap', 'title' => rtrim(__('Master switch (foundation):', 'ai-eshop-optimizer'), ': '), 'description' => __('Loads the IAPI bootstrap (stores, REST endpoint, public CustomEvents) regardless of per-module flags. Owned by the Rooster X theme.', 'ai-eshop-optimizer'), 'meta' => class_exists('\Rooster\Admin\States') ? \Rooster\Admin\States::status($master_on ? 'on' : 'off') : '', 'footer' => \Rooster\Admin\Ui::button(__('Open theme settings', 'ai-eshop-optimizer'), array('href' => admin_url('themes.php?page=rooster-settings'))), )); echo $open; echo \Rooster\Admin\Ui::facts(array(array('label' => __('Setting', 'ai-eshop-optimizer'), 'code' => 'rooster_iapi_enabled'))); echo $close; list($open, $close) = AIEO_DMM_UI::card(array( 'anchor' => 'iapi-cutover', 'icon' => 'layers', 'title' => __('Per-module cutover', 'ai-eshop-optimizer'), 'description' => __('Each module has three states: Inherit (= Off — explicit cutover required), On (use the IAPI shim when production-ready), Off (force legacy jQuery). Scaffold modules are blocked by a safety gate even when set to On.', 'ai-eshop-optimizer'), 'footer' => \Rooster\Admin\Ui::button(__('Save Changes', 'ai-eshop-optimizer'), array('variant' => 'primary', 'type' => 'submit', 'attrs' => array('form' => 'aieo-iapi-form'))), )); echo $open; echo '
' . ''; wp_nonce_field(self::NONCE_ACTION); $rows = ''; foreach ($modules as $slug => $info) { $opt_key = 'aieo_dmm_' . $slug . '_iapi_enabled'; $current = (string) get_option($opt_key, 'inherit'); $status = isset($statuses[$slug]) ? (string) $statuses[$slug] : 'unknown'; $is_active = method_exists('AIEO_DMM', 'is_iapi_enabled') ? AIEO_DMM::is_iapi_enabled($slug) : false; $help = \Rooster\Admin\Ui::badge($status, isset($tones[$status]) ? $tones[$status] : 'mono', array('sm' => true)) . ' ' . esc_html($info['desc']) . '
' . sprintf( esc_html__('option %1$s = %2$s · resolves: %3$s', 'ai-eshop-optimizer'), '' . esc_html($opt_key) . '', '' . esc_html($current) . '', $is_active ? \Rooster\Admin\Ui::badge(_x('IAPI', 'IAPI branch label', 'ai-eshop-optimizer'), 'success', array('sm' => true)) : \Rooster\Admin\Ui::badge(_x('legacy', 'legacy branch label', 'ai-eshop-optimizer'), 'mono', array('sm' => true)) ); if ('scaffold' === $status && 'yes' === $current) { $help .= '
' . esc_html__('Scaffold gate active — flag is ignored. Override with the aieo/iapi/allow_scaffold filter for dev only.', 'ai-eshop-optimizer'); } $rows .= \Rooster\Admin\Ui::field_row(array( 'id' => 'aieo-iapi-' . $slug, 'label' => (string) $info['label'], 'group' => true, 'help_html' => $help, 'control' => \Rooster\Admin\Ui::checks('aieo_iapi[' . $slug . ']', array( 'inherit' => __('Inherit', 'ai-eshop-optimizer'), 'yes' => __('On', 'ai-eshop-optimizer'), 'no' => __('Off', 'ai-eshop-optimizer'), ), array($current), array('type' => 'radio', 'inline' => true)), )); } echo '
' . $rows . '
'; echo $close; list($open, $close) = AIEO_DMM_UI::card(array( 'anchor' => 'iapi-smoke', 'icon' => 'search', 'title' => __('Smoke tests and inspection', 'ai-eshop-optimizer'), )); echo $open; echo '
' . \Rooster\Admin\Ui::button(__('Open frontend', 'ai-eshop-optimizer'), array('href' => home_url('/'), 'external' => true)) . ' ' . \Rooster\Admin\Ui::button(__('Pricing batch endpoint', 'ai-eshop-optimizer'), array('href' => rest_url('aieo/v1/pricing/batch'), 'external' => true)) . '
'; echo '

' . esc_html__('DevTools console one-liners:', 'ai-eshop-optimizer') . '

'; echo '
' . esc_html("// after adding to cart on the frontend:\naieo.iapi.stores.cart.state.fragments\n\n// pricing batch round-trip:\naieo.iapi.stores.pricing.actions.prefetch([487390])\n  .then(() => aieo.iapi.stores.pricing.state.effectivePrice)\n\n// wishlist state:\naieo.iapi.stores.wishlist.state.productIds") . '
'; echo $close; $runtime_dir = defined('AIEO_RUNTIME_DIR') ? AIEO_RUNTIME_DIR : ''; if ($runtime_dir) { list($open, $close) = AIEO_DMM_UI::card(array( 'anchor' => 'iapi-docs', 'icon' => 'book-open', 'title' => __('Docs', 'ai-eshop-optimizer'), )); echo $open; echo '

' . wp_kses_post(sprintf( __('Companion docs live in the runtime plugin: %s', 'ai-eshop-optimizer'), '' . esc_html($runtime_dir) . '' )) . '

'; echo \Rooster\Admin\Ui::facts(array( array('label' => 'CONTRACT.md', 'value' => __('public DOM/JS surface', 'ai-eshop-optimizer')), array('label' => 'STATUS.md', 'value' => __('what\'s done + open TODOs (incl. multi-select swatches)', 'ai-eshop-optimizer')), array('label' => 'ROADMAP.md', 'value' => __('sequenced next phases', 'ai-eshop-optimizer')), )); echo $close; } if ($board) { \Rooster\Admin\Board::end(); } echo '
'; } public function handle_save() { if (!current_user_can(self::CAPABILITY)) { wp_die(esc_html__('Insufficient permissions.', 'ai-eshop-optimizer')); } check_admin_referer(self::NONCE_ACTION); $allowed_slugs = array_keys($this->modules()); $allowed_vals = array('inherit', 'yes', 'no'); $submitted = isset($_POST['aieo_iapi']) && is_array($_POST['aieo_iapi']) ? wp_unslash($_POST['aieo_iapi']) : array(); foreach ($allowed_slugs as $slug) { $value = isset($submitted[$slug]) ? sanitize_key($submitted[$slug]) : 'inherit'; if (!in_array($value, $allowed_vals, true)) { $value = 'inherit'; } update_option('aieo_dmm_' . $slug . '_iapi_enabled', $value, false); } wp_safe_redirect(add_query_arg( array('page' => self::MENU_SLUG, 'aieo_iapi_saved' => '1'), admin_url('admin.php') )); exit; } }