AIEO_DMM::MENU_SLUG, 'dmm_tab' => 'web-push'), admin_url('admin.php'))) . '">', '' )), 'info', array('inline' => true) ); if (class_exists('AIEO_DMM_Web_Push') && method_exists('AIEO_DMM_Web_Push', 'get_banner_settings')) { $this->render_banner_appearance($pointer); } else { list($open, $close) = AIEO_DMM_UI::card(array( 'anchor' => 'web-push-banner-appearance', 'icon' => 'palette', 'title' => __('Banner appearance', 'ai-eshop-optimizer'), )); echo $open; $missing = \Rooster\Admin\Ui::notice(esc_html__('Web Push module is not loaded — check that AIEO_DMM_Web_Push is active.', 'ai-eshop-optimizer'), 'destructive', array('inline' => true)); echo $missing; echo $close; } } private function render_banner_appearance($pointer) { $b = AIEO_DMM_Web_Push::get_banner_settings(); $row = static function ($name, $label, $control, $help = '') { return \Rooster\Admin\Ui::field_row(array( 'id' => 'aieo-wpd-' . $name, 'label' => $label, 'control' => $control, 'help' => $help, )); }; $color = static function ($name, $label, $fallback) use ($b) { return \Rooster\Admin\Fields::color($name, (string) $b[$name], array('id' => 'aieo-wpd-' . $name, 'default' => $fallback, 'label' => $label)); }; $num = static function ($name, $min, $max, $unit, $width = 'xs', $step = 1) use ($b) { return \Rooster\Admin\Ui::input($name, (string) (int) $b[$name], array('id' => 'aieo-wpd-' . $name, 'type' => 'number', $width => true, 'unit' => $unit, 'min' => $min, 'max' => $max, 'step' => $step)); }; $card = function ($anchor, $icon, $title, $description, $fields, $before = '') { $form_id = 'aieo-card-' . $anchor . '-form'; list($open, $close) = AIEO_DMM_UI::card(array( 'anchor' => $anchor, 'icon' => $icon, 'title' => $title, 'description' => $description, 'footer' => class_exists('\Rooster\Admin\States') ? \Rooster\Admin\States::save_button($form_id) : '', )); echo $open; echo $before; echo AIEO_DMM_UI::card_form('aieo_dmm_webpush_save_banner', $this->get_id(), $form_id, '
' . $fields . '
'); echo $close; }; $card( 'web-push-banner-appearance', 'palette', __('Banner appearance', 'ai-eshop-optimizer'), __('Visual + behavior settings for the first-visit opt-in banner. Top position auto-detects the site header on most themes (block-based or classic) and lands the card just below it.', 'ai-eshop-optimizer'), $row( 'banner_enabled', __('Show the opt-in banner on the storefront', 'ai-eshop-optimizer'), \Rooster\Admin\Ui::toggle('banner_enabled', !empty($b['banner_enabled']), array('id' => 'aieo-wpd-banner_enabled', 'hidden' => false, 'label' => _x('On', 'a switch is on', 'ai-eshop-optimizer'), 'label_hidden' => true)), __('Master display switch. Independent from the "send push messages" master switch — when this is OFF, no first-visit opt-in prompt is shown to new visitors, but customers who already subscribed keep receiving campaigns normally. Turn this OFF when running a campaign-freeze, doing a redesign, or when GDPR/legal review temporarily pauses new acquisition.', 'ai-eshop-optimizer') ) . $row( 'position', __('Position', 'ai-eshop-optimizer'), \Rooster\Admin\Ui::select('position', array( 'top' => __('Top — under the site header (auto-detected)', 'ai-eshop-optimizer'), 'middle' => __('Middle — viewport-centered card', 'ai-eshop-optimizer'), 'bottom' => __('Bottom — above the footer', 'ai-eshop-optimizer'), ), (string) $b['position'], array('id' => 'aieo-wpd-position')) ) . $row('offset', __('Manual offset', 'ai-eshop-optimizer'), $num('offset', 0, 500, 'px'), __('Distance from the top/bottom edge. Used as a fallback when the header auto-detector can\'t find a header. Ignored for "middle".', 'ai-eshop-optimizer')) . $row('delay_ms', __('Show delay', 'ai-eshop-optimizer'), $num('delay_ms', 0, 60000, 'ms', 'short', 500), __('Milliseconds before the banner appears on first visit. A larger value (8000–12000) keeps the LCP / CLS clean for GTmetrix and Lighthouse — the SW + Push API check + first paint of the banner all happen well after the critical render path.', 'ai-eshop-optimizer')), $pointer ); $card( 'web-push-banner-modal', 'app-window', __('Modal', 'ai-eshop-optimizer'), __('The opt-in banner\'s background, text, bell icon and border, as visitors see it.', 'ai-eshop-optimizer'), $row('bg', __('Background color', 'ai-eshop-optimizer'), $color('bg', __('Background color', 'ai-eshop-optimizer'), '#ffffff')) . $row('text', __('Text color', 'ai-eshop-optimizer'), $color('text', __('Text color', 'ai-eshop-optimizer'), '#1f2937')) . $row('icon_color', __('Icon color (bell)', 'ai-eshop-optimizer'), $color('icon_color', __('Icon color (bell)', 'ai-eshop-optimizer'), '#F46767')) . $row('border_color', __('Border color', 'ai-eshop-optimizer'), $color('border_color', __('Border color', 'ai-eshop-optimizer'), '#e5e7eb')) . $row('border_width', __('Border width', 'ai-eshop-optimizer'), $num('border_width', 0, 10, 'px')) . $row('border_radius', __('Border radius', 'ai-eshop-optimizer'), $num('border_radius', 0, 40, 'px')) ); $yes = __('Primary (Yes)', 'ai-eshop-optimizer'); $no = __('Secondary (Not now)', 'ai-eshop-optimizer'); $pair = static function ($key, $label, $yes_control, $no_control, $yes_name, $no_name) use ($yes, $no) { return \Rooster\Admin\Ui::field_row(array( 'id' => 'aieo-wpd-btn-' . $key, 'label' => $label, 'parts' => array( array('label' => $yes, 'control' => $yes_control, 'id' => 'aieo-wpd-' . $yes_name), array('label' => $no, 'control' => $no_control, 'id' => 'aieo-wpd-' . $no_name), ), )); }; $card( 'web-push-banner-buttons', 'square', __('Buttons', 'ai-eshop-optimizer'), __('The colours and borders of the banner\'s Yes and Not now buttons.', 'ai-eshop-optimizer'), $pair('bg', __('Background', 'ai-eshop-optimizer'), $color('btn_primary_bg', __('Background', 'ai-eshop-optimizer') . ', ' . $yes, '#F46767'), $color('btn_secondary_bg', __('Background', 'ai-eshop-optimizer') . ', ' . $no, '#f3f4f6'), 'btn_primary_bg', 'btn_secondary_bg') . $pair('text', __('Text', 'ai-eshop-optimizer'), $color('btn_primary_text', __('Text', 'ai-eshop-optimizer') . ', ' . $yes, '#ffffff'), $color('btn_secondary_text', __('Text', 'ai-eshop-optimizer') . ', ' . $no, '#4b5563'), 'btn_primary_text', 'btn_secondary_text') . $pair('border', __('Border color', 'ai-eshop-optimizer'), $color('btn_primary_border', __('Border color', 'ai-eshop-optimizer') . ', ' . $yes, '#F46767'), $color('btn_secondary_border', __('Border color', 'ai-eshop-optimizer') . ', ' . $no, '#f3f4f6'), 'btn_primary_border', 'btn_secondary_border') . $pair('border-w', __('Border width', 'ai-eshop-optimizer'), $num('btn_primary_border_w', 0, 6, 'px'), $num('btn_secondary_border_w', 0, 6, 'px'), 'btn_primary_border_w', 'btn_secondary_border_w') . $row('btn_radius', __('Button border radius', 'ai-eshop-optimizer'), $num('btn_radius', 0, 40, 'px')) ); $text = static function ($name) use ($b) { return \Rooster\Admin\Ui::input($name, (string) $b[$name], array('id' => 'aieo-wpd-' . $name, 'medium' => true)); }; $area = static function ($name) use ($b) { return \Rooster\Admin\Ui::textarea($name, (string) $b[$name], array('id' => 'aieo-wpd-' . $name, 'rows' => 2)); }; $card( 'web-push-banner-copy', 'text-cursor-input', __('Copy (editable for A/B without a deploy)', 'ai-eshop-optimizer'), __('The words the opt-in banner shows: its title, its message and its two buttons.', 'ai-eshop-optimizer'), $row('copy_title', __('Title', 'ai-eshop-optimizer'), $text('copy_title')) . $row('copy_body', __('Body', 'ai-eshop-optimizer'), $area('copy_body')) . $row('copy_accept', __('Accept label', 'ai-eshop-optimizer'), $text('copy_accept')) . $row('copy_dismiss', __('Dismiss label', 'ai-eshop-optimizer'), $text('copy_dismiss')) ); $card( 'web-push-banner-thanks', 'message-square-text', __('Thank-you toast (after Yes / No click)', 'ai-eshop-optimizer'), __('The short message a visitor sees after answering the banner, and how long it stays open.', 'ai-eshop-optimizer'), $row('copy_thanks_yes', __('After "Yes" message', 'ai-eshop-optimizer'), $area('copy_thanks_yes'), __('Browsers always show their own native permission prompt after the user clicks Yes — there is no API to bypass it. Use this message to nudge the visitor to also confirm in the browser pop-up.', 'ai-eshop-optimizer')) . $row('copy_thanks_no', __('After "No" message', 'ai-eshop-optimizer'), $area('copy_thanks_no')) . $row('thanks_autoclose_secs', __('Auto-close after', 'ai-eshop-optimizer'), $num('thanks_autoclose_secs', 0, 30, 's'), __('0 = stay open until the user clicks the X.', 'ai-eshop-optimizer')) ); } }