__( 'Audience Builder', 'ai-eshop-optimizer' ), 'description' => __( 'Define reusable audiences over your customer intelligence in plain language or a visual rule builder — including behavioural-vector similarity (lookalikes, in-market intent) no scalar filter can express. Preview exact membership before you send.', 'ai-eshop-optimizer' ), 'bullets' => array( __( 'Type a sentence → validated rule tree → live count + sample', 'ai-eshop-optimizer' ), __( 'Behavioural predicates: lookalikes, instigator-state, taste similarity', 'ai-eshop-optimizer' ), __( 'Save once, reuse across campaigns, coupons and the cascade', 'ai-eshop-optimizer' ), ), ); } public function register_hooks() { add_action( 'wp_ajax_aieo_dmm_audience_generate', array( $this, 'ajax_generate' ) ); add_action( 'wp_ajax_aieo_dmm_audience_count', array( $this, 'ajax_count' ) ); add_action( 'wp_ajax_aieo_dmm_audience_save', array( $this, 'ajax_save' ) ); add_action( 'wp_ajax_aieo_dmm_audience_list', array( $this, 'ajax_list' ) ); add_action( 'wp_ajax_aieo_dmm_audience_delete', array( $this, 'ajax_delete' ) ); add_action( 'wp_ajax_aieo_dmm_audience_suggest', array( $this, 'ajax_suggest' ) ); } public function render_subtab() { if ( ! class_exists( 'AIEO_Audience_Rules' ) ) { list( $aieo_ab_open, $aieo_ab_close ) = AIEO_DMM_UI::card( array( 'icon' => 'sparkles', 'title' => $this->get_label() ) ); echo $aieo_ab_open; echo \Rooster\Admin\Ui::notice( esc_html__( 'Audience Rules engine is not loaded.', 'ai-eshop-optimizer' ), 'destructive', array( 'inline' => true ) ); echo $aieo_ab_close; return; } $this->render_template( 'audience-builder.php', array( 'nonce' => wp_create_nonce( 'aieo_dmm_nonce' ), 'fields' => AIEO_Audience_Rules::field_catalog(), ) ); } public function ajax_generate() { AIEO_DMM::verify_ajax_request(); if ( ! self::is_aieo_pro_user() ) { wp_send_json_error( array( 'message' => __( 'This feature requires the Pro version.', 'ai-eshop-optimizer' ) ) ); } $text = isset( $_POST['text'] ) ? sanitize_textarea_field( wp_unslash( $_POST['text'] ) ) : ''; if ( '' === $text ) { wp_send_json_error( array( 'message' => __( 'Describe the audience first.', 'ai-eshop-optimizer' ) ) ); } $res = AIEO_Audience_Rules::from_nl( $text ); if ( empty( $res['ok'] ) ) { wp_send_json_error( array( 'message' => (string) $res['error'] ) ); } $rule = $res['rule']; $cnt = AIEO_Audience_Rules::count( $rule ); wp_send_json_success( array( 'rule' => $rule, 'summary' => AIEO_Audience_Rules::describe_rule( $rule ), 'count' => $cnt, 'reach' => AIEO_Audience_Rules::reach( $rule ), 'sample' => AIEO_Audience_Rules::preview( $rule, 8 ), ) ); } public function ajax_count() { AIEO_DMM::verify_ajax_request(); $rule = self::posted_rule(); if ( null === $rule ) { wp_send_json_error( array( 'message' => __( 'Invalid rule.', 'ai-eshop-optimizer' ) ) ); } wp_send_json_success( array( 'count' => AIEO_Audience_Rules::count( $rule ), 'summary' => AIEO_Audience_Rules::describe_rule( $rule ), 'reach' => AIEO_Audience_Rules::reach( $rule ), 'sample' => AIEO_Audience_Rules::preview( $rule, 8 ), ) ); } public function ajax_save() { AIEO_DMM::verify_ajax_request(); if ( ! self::is_aieo_pro_user() ) { wp_send_json_error( array( 'message' => __( 'This feature requires the Pro version.', 'ai-eshop-optimizer' ) ) ); } $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : ''; $rule = self::posted_rule(); if ( '' === $name || null === $rule ) { wp_send_json_error( array( 'message' => __( 'Name and a valid rule are required.', 'ai-eshop-optimizer' ) ) ); } $id = AIEO_Audience_Rules::save( $name, $rule ); wp_send_json_success( array( 'id' => $id, 'saved' => $id > 0 ) ); } public function ajax_list() { AIEO_DMM::verify_ajax_request(); $audiences = AIEO_Audience_Rules::list_saved(); wp_send_json_success( array( 'audiences' => $audiences, 'table' => self::saved_table( $audiences ), ) ); } private static function saved_table( array $audiences ) { if ( ! $audiences || ! class_exists( '\Rooster\Admin\Ui' ) || ! class_exists( '\Rooster\Admin\States' ) ) { return ''; } $rows = array(); foreach ( $audiences as $a ) { $id = (string) (int) ( $a['id'] ?? 0 ); $name = (string) ( $a['name'] ?? '' ); $rows[] = array( '_name' => $name, 'name' => $name, 'count' => max( 0, (int) ( $a['count_cached'] ?? 0 ) ), 'actions' => array( array( 'label' => __( 'Load', 'ai-eshop-optimizer' ), 'attrs' => array( 'data-aieo-ab-load' => $id ), ), array( 'label' => __( 'Delete', 'ai-eshop-optimizer' ), 'destructive' => true, 'attrs' => array( 'data-aieo-ab-del' => $id ) + \Rooster\Admin\States::confirm_attrs( 'aieo-ab-delete-dialog' ), ), ), ); } return \Rooster\Admin\Ui::table( array( array( 'key' => 'name', 'label' => __( 'Audience', 'ai-eshop-optimizer' ), 'type' => 'text' ), array( 'key' => 'count', 'label' => __( 'Customers', 'ai-eshop-optimizer' ), 'type' => 'num', 'priority' => 'low' ), array( 'key' => 'actions', 'label' => __( 'Actions', 'ai-eshop-optimizer' ), 'type' => 'actions' ), ), $rows, array( 'id' => 'aieo-ab-saved-table', 'caption' => __( 'Saved audiences', 'ai-eshop-optimizer' ), ) ); } public function ajax_delete() { AIEO_DMM::verify_ajax_request(); if ( ! self::is_aieo_pro_user() ) { wp_send_json_error( array( 'message' => __( 'This feature requires the Pro version.', 'ai-eshop-optimizer' ) ) ); } $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; wp_send_json_success( array( 'deleted' => AIEO_Audience_Rules::delete( $id ) ) ); } public function ajax_suggest() { AIEO_DMM::verify_ajax_request(); wp_send_json_success( array( 'suggestions' => AIEO_Audience_Rules::suggestions() ) ); } private static function posted_rule() { $raw = isset( $_POST['rule'] ) ? wp_unslash( $_POST['rule'] ) : ''; if ( is_string( $raw ) && '' !== $raw ) { $d = json_decode( $raw, true ); return is_array( $d ) ? $d : null; } return null; } }