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'))
);
}
}