get_module( 'web-push' ) : null; } public function render_subtab() { global $wpdb; $tbl_ci = $wpdb->prefix . 'aieo_customer_intelligence'; $push = $this->push_module(); if ( ! $push || ! method_exists( $push, 'cohort_rollup' ) ) { list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => 'web-push-lookalike-audience-export-meta-google', 'icon' => 'users-round', 'title' => __( 'Lookalike audience export', 'ai-eshop-optimizer' ), ) ); echo $open; echo \Rooster\Admin\Ui::notice( esc_html__( 'The export is unavailable in this build.', 'ai-eshop-optimizer' ), 'warning', array( 'inline' => true ) ); echo $close; return; } $this->render_export_cards( $tbl_ci, $push->cohort_rollup( $tbl_ci ), __( 'Export a group of customers as a hashed list and upload it to Meta or Google as the seed for a lookalike audience. The platforms then find new prospects who resemble that group. Emails are SHA-256 hashed before they leave this server, which is the format both platforms expect — no raw address is ever exported.', 'ai-eshop-optimizer' ), __( 'Tip: the strongest seed is usually your best customers rather than the largest group. VIP, Loyalist and Brand-Devoted are the ones to try first.', 'ai-eshop-optimizer' ) ); } private function part( $title, $description = '' ) { if ( function_exists( 'rooster_admin_card_section_html' ) && class_exists( 'AIEO_Chrome' ) && AIEO_Chrome::on() ) { echo rooster_admin_card_section_html( (string) $title, '' !== (string) $description ? array( 'description' => (string) $description ) : array() ); return; } echo '
' . esc_html( (string) $description ) . '
'; } } private function render_export_cards( $tbl_ci, $rollup, $intro, $tip ) { if ( ! is_array( $rollup ) ) { $rollup = array(); } if ( ! class_exists( 'AIEO_Audience_Rules' ) && defined( 'AIEO_DIR' ) ) { $f = AIEO_DIR . 'includes/class-aieo-audience-rules.php'; if ( is_readable( $f ) ) { require_once $f; } } $cohort_order = class_exists( 'AIEO_Audience_Rules' ) ? array_map( static function ( $c ) { return 'active' === $c ? '' : $c; }, AIEO_Audience_Rules::DORMANCY_COHORTS ) : array( '', 'drifting', 'lapsed', 'long_dormant', 'lost' ); $base = admin_url( 'admin-post.php?action=aieo_dmm_webpush_export_lookalike' ); $nonce = wp_create_nonce( 'aieo_dmm_webpush_export_lookalike' ); if ( function_exists( 'rooster_admin_band_open' ) ) { rooster_admin_band_open( array( 'cols' => 2 ) ); } list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => 'web-push-lookalike-audience-export-meta-google', 'icon' => 'users-round', 'title' => __( 'Lookalike audience export', 'ai-eshop-optimizer' ), 'description' => $intro, 'footnote' => $tip, 'col' => 1, ) ); echo $open; echo \Rooster\Admin\Guidance::text( __( 'Both Meta and Google accept SHA-256-hashed customer email lists as the seed for lookalike audiences — they find new prospects who match the profile of your Rooster One segment. Download the hashed CSV, upload via the platform UI, build the lookalike there. Native OAuth API integration is on the Phase 7 roadmap; the manual upload flow is fast (<5 min) and works for any cohort.', 'ai-eshop-optimizer' ) ); $this->part( __( 'Hashed CSV — drop into the platform UI', 'ai-eshop-optimizer' ), __( 'Each export contains: sha256_email, sha256_phone (when available). UTF-8, no BOM (Meta/Google prefer raw). Capped at 50,000 rows per file. A customer with no email address cannot be hashed, so the addressable count is shown as its own column next to the full cohort size — the "In cohort" figures are the same ones the Cohorts table further up reports, and the export file contains the "Addressable" count of rows.', 'ai-eshop-optimizer' ) ); $rows = array(); foreach ( $cohort_order as $cohort ) { if ( ! isset( $rollup[ $cohort ] ) ) { continue; } $total = isset( $rollup[ $cohort ]['cnt'] ) ? (int) $rollup[ $cohort ]['cnt'] : 0; $emailable = isset( $rollup[ $cohort ]['has_email'] ) ? (int) $rollup[ $cohort ]['has_email'] : 0; if ( 0 === $emailable ) { continue; } $rows[] = array( 'cohort' => '' === $cohort ? 'active' : $cohort, 'label' => '' === $cohort ? __( 'Active customers', 'ai-eshop-optimizer' ) : ucwords( str_replace( '_', ' ', $cohort ) ), 'slug' => $cohort, 'total' => $total, 'emailable' => $emailable, 'pct' => $total > 0 ? (int) round( 100 * $emailable / $total ) : 0, 'url' => add_query_arg( array( 'cohort' => '' === $cohort ? 'active' : $cohort, '_wpnonce' => $nonce, ), $base ), ); } if ( empty( $rows ) ) { echo \Rooster\Admin\States::state( 'first-use', array( 'title' => __( 'Nothing to show yet.', 'ai-eshop-optimizer' ), 'text' => __( 'Customer groups appear here once Customer Intelligence has built them and some of their customers have an email address.', 'ai-eshop-optimizer' ) ) ); } else { AIEO_Table::render( array( 'page_id' => 'lookalike_export', 'row_key' => 'cohort', 'rows' => $rows, 'columns' => array( array( 'key' => 'label', 'label' => __( 'Cohort', 'ai-eshop-optimizer' ), 'subline' => 'slug' ), array( 'key' => 'total', 'label' => __( 'In cohort', 'ai-eshop-optimizer' ), 'type' => 'integer', 'width' => '150px' ), array( 'key' => 'emailable', 'label' => __( 'Addressable (has email)', 'ai-eshop-optimizer' ), 'type' => 'integer', 'width' => '190px', 'suffix' => static function ( $r ) { return $r['pct'] > 0 ? '(' . number_format_i18n( $r['pct'] ) . '%)' : ''; }, ), ), 'actions' => array( array( 'id' => 'export', 'label' => '⬇ ' . __( 'Hashed CSV', 'ai-eshop-optimizer' ), 'href' => static function ( $r ) { return $r['url']; }, 'variant' => 'secondary', ), ), ) ); } $this->part( __( 'Upload UIs', 'ai-eshop-optimizer' ) ); echo ''; echo '' . esc_html__( 'Open Meta Audience Manager →', 'ai-eshop-optimizer' ) . ' '; echo '' . esc_html__( 'Open Google Ads Audience Manager →', 'ai-eshop-optimizer' ) . ''; echo '
'; echo $close; list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => 'lookalike-export-start-here-pick-the-seed', 'icon' => 'list-checks', 'title' => __( 'Start here: pick the seed', 'ai-eshop-optimizer' ), 'description' => __( 'Export the cohort that represents your ideal customer, not the largest one. VIP, Loyalist and active Brand-Devoted make the strongest seeds; "Lost" makes a poor one. A few thousand rows is plenty — quality of the seed decides the quality of the audience.', 'ai-eshop-optimizer' ), 'col' => 2, ) ); echo $open; $this->part( __( 'Meta (Facebook / Instagram)', 'ai-eshop-optimizer' ) ); echo '