@@ -458,325 +393,6 @@ class Groq_AI_Product_Text_Settings_Page {
brand_taxonomy ) {
- return $this->brand_taxonomy;
- }
-
- $candidates = [
- 'product_brand',
- 'pwb-brand',
- 'yith_product_brand',
- 'berocket_brand',
- ];
-
- // Attribute-taxonomy fallback (vaak pa_brand).
- if ( taxonomy_exists( 'pa_brand' ) ) {
- array_unshift( $candidates, 'pa_brand' );
- }
-
- $candidates = apply_filters( 'groq_ai_brand_taxonomy_candidates', $candidates );
- $found = '';
- foreach ( $candidates as $tax ) {
- $tax = sanitize_key( (string) $tax );
- if ( $tax && taxonomy_exists( $tax ) ) {
- $found = $tax;
- break;
- }
- }
-
- $found = apply_filters( 'groq_ai_brand_taxonomy', $found );
- $this->brand_taxonomy = sanitize_key( (string) $found );
- return $this->brand_taxonomy;
- }
-
- private function get_term_page_url( $taxonomy, $term_id ) {
- return add_query_arg(
- [
- 'page' => 'groq-ai-product-text-term',
- 'taxonomy' => sanitize_key( (string) $taxonomy ),
- 'term_id' => absint( $term_id ),
- ],
- admin_url( 'options-general.php' )
- );
- }
-
- private function get_term_overview_data( $taxonomy ) {
- $taxonomy = sanitize_key( (string) $taxonomy );
-
- if ( isset( $this->term_overview_cache[ $taxonomy ] ) ) {
- return $this->term_overview_cache[ $taxonomy ];
- }
-
- $rows = [];
- $empty_rows = [];
-
- if ( '' !== $taxonomy && taxonomy_exists( $taxonomy ) ) {
- $terms = get_terms(
- [
- 'taxonomy' => $taxonomy,
- 'hide_empty' => false,
- 'orderby' => 'name',
- 'order' => 'ASC',
- 'number' => 0,
- ]
- );
-
- if ( is_wp_error( $terms ) ) {
- $terms = [];
- }
-
- foreach ( $terms as $term ) {
- if ( ! $term || ! is_object( $term ) || empty( $term->term_id ) ) {
- continue;
- }
-
- $words = $this->count_words( isset( $term->description ) ? $term->description : '' );
- $has_description = $words > 0;
-
- $row = [
- 'id' => absint( $term->term_id ),
- 'name' => (string) $term->name,
- 'slug' => (string) $term->slug,
- 'count' => isset( $term->count ) ? absint( $term->count ) : 0,
- 'words' => $words,
- 'has_description' => $has_description,
- 'url' => $this->get_term_page_url( $taxonomy, $term->term_id ),
- ];
-
- $rows[] = $row;
- if ( ! $has_description ) {
- $empty_rows[] = $row;
- }
- }
- }
-
- $data = [
- 'rows' => $rows,
- 'empty_rows' => $empty_rows,
- 'empty_count' => count( $empty_rows ),
- ];
-
- $this->term_overview_cache[ $taxonomy ] = $data;
-
- return $data;
- }
-
- private function render_term_bulk_panel( $label_plural, $empty_count ) {
- $label_plural = (string) $label_plural;
- ?>
-
-
- 0 ) {
- printf(
- /* translators: 1: amount, 2: label plural (e.g. categorieën) */
- esc_html__( 'Er zijn %1$d %2$s zonder omschrijving. Klik op de knop hieronder om automatisch teksten te genereren.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- (int) $empty_count,
- esc_html( $label_plural )
- );
- } else {
- printf(
- esc_html__( 'Alle %s hebben al een omschrijving.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- esc_html( $label_plural )
- );
- }
- ?>
-
-
-
-
-
-
-
-
-
- get_term_overview_data( $taxonomy );
- $rows = isset( $overview['rows'] ) ? $overview['rows'] : [];
-
- $terms = [];
- foreach ( $rows as $row ) {
- $terms[] = [
- 'id' => isset( $row['id'] ) ? (int) $row['id'] : 0,
- 'name' => isset( $row['name'] ) ? (string) $row['name'] : '',
- 'slug' => isset( $row['slug'] ) ? (string) $row['slug'] : '',
- 'count' => isset( $row['count'] ) ? (int) $row['count'] : 0,
- 'words' => isset( $row['words'] ) ? (int) $row['words'] : 0,
- 'hasDescription' => ! empty( $row['has_description'] ),
- ];
- }
-
- $defaults = [
- 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
- 'nonce' => wp_create_nonce( 'groq_ai_bulk_generate_terms' ),
- 'taxonomy' => $taxonomy,
- 'terms' => $terms,
- 'allowRegenerate' => false,
- 'strings' => [],
- ];
-
- $config = wp_parse_args( $overrides, $defaults );
-
- wp_localize_script( 'groq-ai-term-bulk', 'GroqAITermBulk', $config );
- }
-
- public function render_categories_overview_page() {
- if ( ! current_user_can( 'manage_options' ) ) {
- return;
- }
-
- $taxonomy = 'product_cat';
- $overview = $this->get_term_overview_data( $taxonomy );
- $rows = isset( $overview['rows'] ) ? $overview['rows'] : [];
- $empty_count = isset( $overview['empty_count'] ) ? (int) $overview['empty_count'] : 0;
- ?>
-
-
-
- render_term_bulk_panel( __( 'categorieën', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $empty_count ); ?>
-
-
-
- |
- |
- |
- |
- |
-
-
-
-
- |
-
-
-
-
- |
-
- |
- |
- |
- |
-
-
- |
-
-
-
-
-
-
- detect_brand_taxonomy();
- if ( '' === $taxonomy ) {
- ?>
-
- get_term_overview_data( $taxonomy );
- $rows = isset( $overview['rows'] ) ? $overview['rows'] : [];
- $empty_count = isset( $overview['empty_count'] ) ? (int) $overview['empty_count'] : 0;
- ?>
-
-
-
-
-
- render_term_bulk_panel( __( 'merken', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $empty_count ); ?>
-
-
-
-
- |
- |
- |
- |
- |
-
-
-
-
- |
-
-
-
-
- |
-
- |
- |
- |
- |
-
-
- |
-
-
-
-
-
-
-
plugin );
- $logs_table->prepare_items();
- ?>
-
-
-
-
- get_page_url( 'groq-ai-product-text-logs' );
- $log = null;
-
- if ( $log_id ) {
- global $wpdb;
- $table = $wpdb->prefix . 'groq_ai_generation_logs';
- $query = $wpdb->prepare(
- "SELECT l.*, p.post_title FROM {$table} l LEFT JOIN {$wpdb->posts} p ON p.ID = l.post_id WHERE l.id = %d",
- $log_id
- );
- $log = $wpdb->get_row( $query, ARRAY_A );
- }
-
- ?>
-
-
-
- ←
-
-
-
-
-
-
-
- |
- |
-
-
- |
-
- display_name ) : esc_html( (string) $log['user_id'] );
- } else {
- echo '—';
- }
- ?>
- |
-
-
- |
-
- ' . esc_html( $title ) . '' : esc_html( $title );
- } else {
- echo '—';
- }
- ?>
- |
-
-
- |
- |
-
-
- |
- |
-
-
- |
- |
-
-
- |
-
-
- |
-
-
- |
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- count_words( $term->description );
- $meta_prompt = get_term_meta( $term_id, 'groq_ai_term_custom_prompt', true );
- $settings = $this->plugin->get_settings();
- $bottom_meta_key = $this->resolve_term_bottom_description_meta_key( $term, $settings );
- $effective_bottom_meta_key = '' !== $bottom_meta_key ? $bottom_meta_key : 'groq_ai_term_bottom_description';
- $bottom_description = (string) get_term_meta( $term_id, $effective_bottom_meta_key, true );
- $rankmath_module_enabled = $this->plugin->is_module_enabled( 'rankmath', $settings );
- $rankmath_active = $this->plugin->is_rankmath_active();
- $rankmath_title = '';
- $rankmath_description = '';
- $rankmath_focus_keywords = '';
- if ( $rankmath_module_enabled ) {
- $rankmath_keys = $this->resolve_rankmath_term_meta_keys( $term, $settings );
- $rankmath_title = (string) get_term_meta( $term_id, $rankmath_keys['title'], true );
- $rankmath_description = (string) get_term_meta( $term_id, $rankmath_keys['description'], true );
- $rankmath_focus_keywords = (string) get_term_meta( $term_id, $rankmath_keys['focus_keyword'], true );
- }
- $default_prompt = $this->get_term_prompt_text( $term, $meta_prompt );
- ?>
-
-
- : name ); ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 'rank_math_title',
- 'description' => 'rank_math_description',
- 'focus_keyword' => 'rank_math_focus_keyword',
- ];
- $keys = apply_filters( 'groq_ai_rankmath_term_meta_keys', $keys, $term, $settings );
- if ( ! is_array( $keys ) ) {
- $keys = [];
- }
-
- return [
- 'title' => isset( $keys['title'] ) ? sanitize_key( (string) $keys['title'] ) : 'rank_math_title',
- 'description' => isset( $keys['description'] ) ? sanitize_key( (string) $keys['description'] ) : 'rank_math_description',
- 'focus_keyword' => isset( $keys['focus_keyword'] ) ? sanitize_key( (string) $keys['focus_keyword'] ) : 'rank_math_focus_keyword',
- ];
- }
-
- private function get_term_prompt_text( $term, $custom_prompt = null ) {
- $prompt = ( null !== $custom_prompt ) ? $custom_prompt : '';
-
- if ( null === $custom_prompt && $term && isset( $term->term_id ) ) {
- $prompt = get_term_meta( $term->term_id, 'groq_ai_term_custom_prompt', true );
- }
-
- $prompt = trim( (string) $prompt );
- if ( '' !== $prompt ) {
- return $prompt;
- }
-
- $default_prompt = __( 'Schrijf een SEO-vriendelijke categorieomschrijving in het Nederlands. Gebruik duidelijke tussenkoppen en
-tags. Voeg geen prijsinformatie toe.', GROQ_AI_PRODUCT_TEXT_DOMAIN );
-
- return apply_filters( 'groq_ai_default_term_prompt', $default_prompt, $term );
- }
-
-
-
public function render_product_attribute_includes_field() {
$settings = $this->plugin->get_settings();
$values = isset( $settings['product_attribute_includes'] ) && is_array( $settings['product_attribute_includes'] )
@@ -1333,6 +553,106 @@ class Groq_AI_Product_Text_Settings_Page {
+
+
+
+
+
+ ', $this->format_html_attributes( $attributes ) );
+
+ if ( isset( $field_args['provider_key'] ) && 'google' === $field_args['provider_key'] ) {
+ $this->render_google_safety_fields( $field_args );
+ }
+ }
+
+ private function render_google_safety_fields( $field_args ) {
+ $categories = isset( $field_args['google_safety_categories'] ) && is_array( $field_args['google_safety_categories'] )
+ ? $field_args['google_safety_categories']
+ : [];
+ $thresholds = isset( $field_args['google_safety_thresholds'] ) && is_array( $field_args['google_safety_thresholds'] )
+ ? $field_args['google_safety_thresholds']
+ : [];
+
+ if ( empty( $categories ) || empty( $thresholds ) ) {
+ return;
+ }
+
+ $selected_settings = isset( $field_args['google_safety_settings'] ) && is_array( $field_args['google_safety_settings'] )
+ ? $field_args['google_safety_settings']
+ : [];
+ $option_key = $this->plugin->get_option_key();
+ ?>
+
+
+
+ $info ) :
+ $category_label = isset( $info['label'] ) ? $info['label'] : $category_key;
+ $category_description = isset( $info['description'] ) ? $info['description'] : '';
+ $selected_threshold = isset( $selected_settings[ $category_key ] ) ? $selected_settings[ $category_key ] : '';
+ $field_id = 'groq-ai-google-safety-' . sanitize_html_class( $category_key );
+ ?>
+
+
+
+ $value ) {
+ if ( '' === $value && 0 !== $value && '0' !== $value ) {
+ continue;
+ }
+
+ $pairs[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $value ) );
+ }
+
+ return implode( ' ', $pairs );
+ }
+
private function get_product_attribute_include_options() {
$options = [
'__custom__' => __( 'Custom attributen (niet-taxonomie)', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
@@ -1471,9 +791,6 @@ class Groq_AI_Product_Text_Settings_Page {
'settings_page_groq-ai-product-text',
'settings_page_groq-ai-product-text-modules',
'settings_page_groq-ai-product-text-prompts',
- 'settings_page_groq-ai-product-text-categories',
- 'settings_page_groq-ai-product-text-brands',
- 'settings_page_groq-ai-product-text-term',
'settings_page_groq-ai-product-text-logs',
];
@@ -1489,19 +806,15 @@ class Groq_AI_Product_Text_Settings_Page {
return;
}
- wp_enqueue_style(
- 'groq-ai-settings',
- plugins_url( 'assets/css/admin.css', GROQ_AI_PRODUCT_TEXT_FILE ),
- [],
- GROQ_AI_PRODUCT_TEXT_VERSION
- );
+ $this->enqueue_admin_styles();
- wp_enqueue_style(
- 'groq-ai-settings-extra',
- plugins_url( 'assets/css/settings.css', GROQ_AI_PRODUCT_TEXT_FILE ),
- [ 'groq-ai-settings' ],
- GROQ_AI_PRODUCT_TEXT_VERSION
- );
+ $current_page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
+
+ $is_main_settings_screen = ( 0 === strpos( (string) $hook, 'settings_page_groq-ai-product-text' ) ) && ( 'groq-ai-product-text' === $current_page );
+
+ if ( ! $is_main_settings_screen ) {
+ return;
+ }
wp_enqueue_script(
'groq-ai-settings',
@@ -1511,96 +824,8 @@ class Groq_AI_Product_Text_Settings_Page {
true
);
- $current_page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
-
- if ( 0 === strpos( (string) $hook, 'settings_page_groq-ai-product-text-term' ) ) {
- wp_enqueue_script(
- 'groq-ai-term-admin',
- plugins_url( 'assets/js/term-admin.js', GROQ_AI_PRODUCT_TEXT_FILE ),
- [],
- GROQ_AI_PRODUCT_TEXT_VERSION,
- true
- );
-
- $taxonomy = isset( $_GET['taxonomy'] ) ? sanitize_key( wp_unslash( $_GET['taxonomy'] ) ) : '';
- $term_id = isset( $_GET['term_id'] ) ? absint( $_GET['term_id'] ) : 0;
- wp_localize_script(
- 'groq-ai-term-admin',
- 'GroqAITermGenerator',
- [
- 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
- 'nonce' => wp_create_nonce( 'groq_ai_generate_term' ),
- 'taxonomy' => $taxonomy,
- 'termId' => $term_id,
- ]
- );
- }
-
- $bulk_taxonomy = '';
- $bulk_allow_regen = false;
- $bulk_strings = [];
-
- if ( 0 === strpos( (string) $hook, 'settings_page_groq-ai-product-text-categories' ) ) {
- $bulk_taxonomy = 'product_cat';
- $bulk_allow_regen = true;
- $bulk_strings = [
- 'statusIdle' => __( 'Bulk gestart. AI werkt de geselecteerde categorieën bij…', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'statusProgress' => __( 'Categorie %1$s van %2$s: %3$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'statusDone' => __( 'Klaar! %d categorieën bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'statusStopped' => __( 'Bulk generatie gestopt. %d categorieën bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'statusEmpty' => __( 'Geen categorieën zonder omschrijving gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'logSuccess' => __( '%1$s gevuld (%2$d woorden).', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'logError' => __( '%1$s mislukt: %2$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'confirmStop' => __( 'Weet je zeker dat je wilt stoppen? De huidige categorie kan onafgemaakt blijven.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'confirmRegenerate' => __( 'Wil je categorie %s opnieuw laten schrijven?', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'regenerateProgress' => __( '%s wordt opnieuw geschreven…', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'regenerateDone' => __( '%s is bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'regenerateError' => __( 'Kon %1$s niet bijwerken: %2$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'regenerateBlocked' => __( 'Wacht tot de bulk generatie klaar is voordat je een categorie opnieuw genereert.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- ];
- } elseif ( 0 === strpos( (string) $hook, 'settings_page_groq-ai-product-text-brands' ) ) {
- $detected_taxonomy = $this->detect_brand_taxonomy();
- if ( '' !== $detected_taxonomy ) {
- $bulk_taxonomy = $detected_taxonomy;
- $bulk_allow_regen = true;
- $bulk_strings = [
- 'statusIdle' => __( 'Bulk gestart. AI werkt de geselecteerde merken bij…', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'statusProgress' => __( 'Merk %1$s van %2$s: %3$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'statusDone' => __( 'Klaar! %d merken bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'statusStopped' => __( 'Bulk generatie gestopt. %d merken bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'statusEmpty' => __( 'Geen merken zonder omschrijving gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'logSuccess' => __( '%1$s gevuld (%2$d woorden).', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'logError' => __( '%1$s mislukt: %2$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'confirmStop' => __( 'Weet je zeker dat je wilt stoppen? Het huidige merk kan onafgemaakt blijven.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'confirmRegenerate' => __( 'Wil je %s opnieuw laten schrijven?', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'regenerateProgress' => __( '%s wordt opnieuw geschreven…', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'regenerateDone' => __( '%s is bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'regenerateError' => __( 'Kon %1$s niet bijwerken: %2$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- 'regenerateBlocked' => __( 'Wacht tot de bulk generatie klaar is voordat je een merk opnieuw genereert.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
- ];
- }
- }
-
- if ( '' !== $bulk_taxonomy ) {
- wp_enqueue_script(
- 'groq-ai-term-bulk',
- plugins_url( 'assets/js/term-bulk.js', GROQ_AI_PRODUCT_TEXT_FILE ),
- [],
- GROQ_AI_PRODUCT_TEXT_VERSION,
- true
- );
-
- $this->localize_term_bulk_script(
- $bulk_taxonomy,
- [
- 'allowRegenerate' => $bulk_allow_regen,
- 'strings' => $bulk_strings,
- ]
- );
- }
-
$current_settings = $this->plugin->get_settings();
- $data = [
+ $data = [
'optionKey' => $this->plugin->get_option_key(),
'providers' => [],
'currentProvider' => $current_settings['provider'],
@@ -1615,15 +840,15 @@ class Groq_AI_Product_Text_Settings_Page {
];
foreach ( $this->provider_manager->get_providers() as $provider ) {
- $provider_key = $provider->get_key();
- $cached_models = $this->plugin->get_cached_models_for_provider( $provider_key );
- $cached_models = Groq_AI_Model_Exclusions::filter_models( $provider_key, $cached_models );
- $data['providers'][ $provider->get_key() ] = [
+ $provider_key = $provider->get_key();
+ $cached_models = $this->plugin->get_cached_models_for_provider( $provider_key );
+ $cached_models = Groq_AI_Model_Exclusions::filter_models( $provider_key, $cached_models );
+ $data['providers'][ $provider_key ] = [
'default_label' => sprintf( __( 'Gebruik standaardmodel (%s)', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $provider->get_default_model() ),
'models' => $cached_models,
'supports_live' => $provider->supports_live_models(),
];
- $data['providerRows'][ $provider->get_key() ] = 'groq_ai_api_key_' . $provider->get_key();
+ $data['providerRows'][ $provider_key ] = 'groq_ai_api_key_' . $provider_key;
}
wp_localize_script( 'groq-ai-settings', 'GroqAISettingsData', $data );
@@ -1861,67 +1086,4 @@ class Groq_AI_Product_Text_Settings_Page {
$this->redirect_with_google_notice( 'test', implode( ' ', $messages ), $redirect );
}
- public function handle_save_term_content() {
- if ( ! current_user_can( 'manage_options' ) ) {
- wp_die( esc_html__( 'Je hebt geen toestemming om deze actie uit te voeren.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), '', [ 'response' => 403 ] );
- }
-
- check_admin_referer( 'groq_ai_save_term_content' );
-
- $taxonomy = isset( $_POST['taxonomy'] ) ? sanitize_key( wp_unslash( $_POST['taxonomy'] ) ) : '';
- $term_id = isset( $_POST['term_id'] ) ? absint( $_POST['term_id'] ) : 0;
-
- if ( '' === $taxonomy || ! taxonomy_exists( $taxonomy ) || ! $term_id ) {
- $this->redirect_with_term_notice( $taxonomy, $term_id, 'error', __( 'Ongeldige term.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'error' );
- }
-
- $term = get_term( $term_id, $taxonomy );
- if ( ! $term || is_wp_error( $term ) ) {
- $this->redirect_with_term_notice( $taxonomy, $term_id, 'error', __( 'Term niet gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'error' );
- }
-
- $description = isset( $_POST['description'] ) ? wp_kses_post( wp_unslash( $_POST['description'] ) ) : '';
- $bottom_description = isset( $_POST['groq_ai_term_bottom_description'] ) ? wp_kses_post( wp_unslash( $_POST['groq_ai_term_bottom_description'] ) ) : '';
- $custom_prompt = isset( $_POST['groq_ai_term_custom_prompt'] ) ? sanitize_textarea_field( wp_unslash( $_POST['groq_ai_term_custom_prompt'] ) ) : '';
-
- $update = wp_update_term(
- $term_id,
- $taxonomy,
- [
- 'description' => $description,
- ]
- );
-
- if ( is_wp_error( $update ) ) {
- $this->redirect_with_term_notice( $taxonomy, $term_id, 'error', $update->get_error_message(), 'error' );
- }
-
- $settings = $this->plugin->get_settings();
- $bottom_meta_key = $this->resolve_term_bottom_description_meta_key( $term, $settings );
- $bottom_meta_key = '' !== $bottom_meta_key ? $bottom_meta_key : 'groq_ai_term_bottom_description';
- update_term_meta( $term_id, $bottom_meta_key, $bottom_description );
-
- if ( '' === trim( $custom_prompt ) ) {
- delete_term_meta( $term_id, 'groq_ai_term_custom_prompt' );
- } else {
- update_term_meta( $term_id, 'groq_ai_term_custom_prompt', $custom_prompt );
- }
-
- if ( isset( $_POST['groq_ai_term_bottom_description'] ) && $bottom_meta_key !== 'groq_ai_term_bottom_description' ) {
- update_term_meta( $term_id, 'groq_ai_term_bottom_description', $bottom_description );
- }
-
- if ( $this->plugin->is_module_enabled( 'rankmath', $settings ) ) {
- $rankmath_keys = $this->resolve_rankmath_term_meta_keys( $term, $settings );
- $rankmath_title = isset( $_POST['groq_ai_rankmath_meta_title'] ) ? sanitize_text_field( wp_unslash( $_POST['groq_ai_rankmath_meta_title'] ) ) : '';
- $rankmath_description = isset( $_POST['groq_ai_rankmath_meta_description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['groq_ai_rankmath_meta_description'] ) ) : '';
- $rankmath_keywords = isset( $_POST['groq_ai_rankmath_focus_keywords'] ) ? sanitize_text_field( wp_unslash( $_POST['groq_ai_rankmath_focus_keywords'] ) ) : '';
-
- update_term_meta( $term_id, $rankmath_keys['title'], $rankmath_title );
- update_term_meta( $term_id, $rankmath_keys['description'], $rankmath_description );
- update_term_meta( $term_id, $rankmath_keys['focus_keyword'], $rankmath_keywords );
- }
-
- $this->redirect_with_term_notice( $taxonomy, $term_id, 'saved', __( 'Term opgeslagen.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) );
- }
}
diff --git a/includes/Admin/class-groq-ai-settings-renderer.php b/includes/Admin/class-groq-ai-settings-renderer.php
new file mode 100644
index 0000000..6908c6e
--- /dev/null
+++ b/includes/Admin/class-groq-ai-settings-renderer.php
@@ -0,0 +1,262 @@
+option_key = $option_key;
+ $this->set_values( $values );
+ }
+
+ public function set_values( $values ) {
+ $this->values = is_array( $values ) ? $values : [];
+ }
+
+ public function open_table( $args = [] ) {
+ $defaults = [
+ 'class' => 'form-table',
+ 'role' => 'presentation',
+ ];
+ $args = wp_parse_args( $args, $defaults );
+
+ printf( '
', $this->build_attr_string( $args ) );
+ }
+
+ public function close_table() {
+ echo '
';
+ }
+
+ public function field( $args ) {
+ $defaults = [
+ 'key' => '',
+ 'name' => '',
+ 'id' => '',
+ 'label' => '',
+ 'description' => '',
+ 'type' => 'text',
+ 'placeholder' => '',
+ 'options' => [],
+ 'attributes' => [],
+ 'default' => '',
+ 'value' => null,
+ 'renderer' => null,
+ 'row_attributes' => [],
+ 'row_class' => '',
+ ];
+ $args = wp_parse_args( $args, $defaults );
+
+ if ( '' === $args['name'] && '' !== $args['key'] ) {
+ $args['name'] = $this->build_field_name( $args['key'] );
+ }
+
+ if ( '' === $args['id'] && '' !== $args['key'] ) {
+ $args['id'] = $this->build_field_id( $args['key'] );
+ }
+
+ if ( null === $args['value'] && '' !== $args['key'] ) {
+ $args['value'] = $this->get_value( $args['key'], $args['default'] );
+ }
+
+ if ( ! isset( $args['attributes']['id'] ) && '' !== $args['id'] ) {
+ $args['attributes']['id'] = $args['id'];
+ }
+
+ $type = $args['type'];
+
+ $row_attributes = $this->prepare_row_attributes( $args );
+ $row_attr_string = $row_attributes ? ' ' . $this->build_attr_string( $row_attributes ) : '';
+
+ echo '
';
+ $this->render_label_cell( $args );
+ echo '| ';
+
+ if ( is_callable( $args['renderer'] ) ) {
+ call_user_func( $args['renderer'], $args, $this );
+ } else {
+ switch ( $type ) {
+ case 'textarea':
+ $this->render_textarea( $args );
+ break;
+ case 'password':
+ $this->render_input( 'password', $args );
+ break;
+ case 'number':
+ $this->render_input( 'number', $args );
+ break;
+ case 'select':
+ $this->render_select( $args );
+ break;
+ case 'checkbox':
+ $this->render_checkbox( $args );
+ break;
+ case 'toggle':
+ $this->render_toggle( $args );
+ break;
+ default:
+ $this->render_input( 'text', $args );
+ }
+ }
+
+ $this->render_description( $args['description'] );
+
+ echo ' | ';
+ echo '
';
+ }
+
+ private function render_label_cell( $args ) {
+ $label = $args['label'];
+ $id = $args['id'];
+ echo '
';
+ if ( '' !== $label ) {
+ printf( '', esc_attr( $id ), esc_html( $label ) );
+ }
+ echo ' | ';
+ }
+
+ private function render_input( $type, $args ) {
+ $attributes = $this->prepare_input_attributes( $args );
+ printf( '
', esc_attr( $type ), $attributes );
+ }
+
+ private function render_textarea( $args ) {
+ $attributes = $this->prepare_input_attributes( $args, [ 'rows' => 4, 'class' => 'large-text' ] );
+ printf( '
', $attributes, esc_textarea( $args['value'] ) );
+ }
+
+ private function render_select( $args ) {
+ $attributes = $this->prepare_input_attributes( $args );
+ printf( '
';
+ }
+
+ private function render_checkbox( $args ) {
+ $value = ! empty( $args['value'] );
+ $attributes = $this->prepare_input_attributes( $args, [ 'class' => '' ] );
+ printf( '
', $attributes, checked( $value, true, false ), esc_html( $args['checkbox_label'] ?? '' ) );
+ }
+
+ private function render_toggle( $args ) {
+ $value = ! empty( $args['value'] );
+ $attributes = $this->prepare_input_attributes( $args, [ 'class' => '' ] );
+ printf( '
', $attributes, checked( $value, true, false ), esc_html( $args['checkbox_label'] ?? '' ) );
+ }
+
+ private function render_description( $text ) {
+ $text = trim( (string) $text );
+ if ( '' === $text ) {
+ return;
+ }
+
+ printf( '
%s
', wp_kses_post( $text ) );
+ }
+
+ private function prepare_input_attributes( $args, $defaults = [] ) {
+ $attributes = wp_parse_args( $args['attributes'], $defaults );
+ $attributes['name'] = $args['name'];
+
+ if ( ! isset( $attributes['id'] ) ) {
+ $attributes['id'] = $args['id'];
+ }
+
+ if ( '' !== $args['placeholder'] ) {
+ $attributes['placeholder'] = $args['placeholder'];
+ }
+
+ if ( ! isset( $attributes['class'] ) ) {
+ $attributes['class'] = 'regular-text';
+ }
+
+ if ( ! in_array( $args['type'], [ 'checkbox', 'toggle', 'select', 'textarea' ], true ) ) {
+ $attributes['value'] = $args['value'];
+ }
+
+ return $this->build_attr_string( $attributes );
+ }
+ private function prepare_row_attributes( $args ) {
+ $attributes = [];
+ if ( isset( $args['row_attributes'] ) && is_array( $args['row_attributes'] ) ) {
+ $attributes = $args['row_attributes'];
+ }
+
+ $row_class = isset( $args['row_class'] ) ? trim( (string) $args['row_class'] ) : '';
+ if ( '' !== $row_class ) {
+ if ( isset( $attributes['class'] ) ) {
+ $attributes['class'] .= ' ' . $row_class;
+ } else {
+ $attributes['class'] = $row_class;
+ }
+ }
+
+ return array_filter(
+ $attributes,
+ function ( $value ) {
+ return '' !== $value || 0 === $value || '0' === $value;
+ }
+ );
+ }
+
+ private function build_attr_string( $attributes ) {
+ $buffer = [];
+ foreach ( $attributes as $key => $value ) {
+ if ( '' === $value && 0 !== $value && '0' !== $value ) {
+ continue;
+ }
+
+ $buffer[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $value ) );
+ }
+
+ return implode( ' ', $buffer );
+ }
+
+ private function build_field_name( $key ) {
+ $segments = $this->split_key( $key );
+ $name = $this->option_key;
+
+ foreach ( $segments as $segment ) {
+ $name .= '[' . $segment . ']';
+ }
+
+ return $name;
+ }
+
+ private function build_field_id( $key ) {
+ $segments = $this->split_key( $key );
+
+ return 'groq-ai-' . implode( '-', $segments );
+ }
+
+ private function get_value( $key, $default = '' ) {
+ $segments = $this->split_key( $key );
+ $value = $this->values;
+
+ foreach ( $segments as $segment ) {
+ if ( is_array( $value ) && array_key_exists( $segment, $value ) ) {
+ $value = $value[ $segment ];
+ } else {
+ return $default;
+ }
+ }
+
+ return $value;
+ }
+
+ private function split_key( $key ) {
+ if ( is_array( $key ) ) {
+ return $key;
+ }
+
+ $key = trim( (string) $key );
+ if ( '' === $key ) {
+ return [];
+ }
+
+ return array_map( 'sanitize_key', explode( '.', $key ) );
+ }
+}
diff --git a/includes/Admin/class-groq-ai-term-admin-base.php b/includes/Admin/class-groq-ai-term-admin-base.php
new file mode 100644
index 0000000..c1554b4
--- /dev/null
+++ b/includes/Admin/class-groq-ai-term-admin-base.php
@@ -0,0 +1,520 @@
+ensure_term_handler_registered();
+ $this->ensure_term_assets_hook();
+ }
+
+ protected function register_term_page() {
+ if ( self::$term_page_registered ) {
+ return;
+ }
+
+ add_submenu_page(
+ 'options-general.php',
+ __( 'Siti AI Term tekst', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
+ __( 'Siti AI Term tekst', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
+ 'manage_options',
+ 'groq-ai-product-text-term',
+ [ $this, 'render_term_generator_page' ]
+ );
+
+ self::$term_page_registered = true;
+ }
+
+ protected function render_term_bulk_panel( $label_plural, $empty_count ) {
+ $label_plural = (string) $label_plural;
+ ?>
+
+
+ 0 ) {
+ printf(
+ /* translators: 1: amount, 2: label plural (e.g. categorieën) */
+ esc_html__( 'Er zijn %1$d %2$s zonder omschrijving. Klik op de knop hieronder om automatisch teksten te genereren.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
+ (int) $empty_count,
+ esc_html( $label_plural )
+ );
+ } else {
+ printf(
+ esc_html__( 'Alle %s hebben al een omschrijving.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
+ esc_html( $label_plural )
+ );
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+ get_term_overview_data( $taxonomy );
+ $rows = isset( $overview['rows'] ) ? $overview['rows'] : [];
+
+ $terms = [];
+ foreach ( $rows as $row ) {
+ $terms[] = [
+ 'id' => isset( $row['id'] ) ? (int) $row['id'] : 0,
+ 'name' => isset( $row['name'] ) ? (string) $row['name'] : '',
+ 'slug' => isset( $row['slug'] ) ? (string) $row['slug'] : '',
+ 'count' => isset( $row['count'] ) ? (int) $row['count'] : 0,
+ 'words' => isset( $row['words'] ) ? (int) $row['words'] : 0,
+ 'hasDescription' => ! empty( $row['has_description'] ),
+ ];
+ }
+
+ $defaults = [
+ 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
+ 'nonce' => wp_create_nonce( 'groq_ai_bulk_generate_terms' ),
+ 'taxonomy' => $taxonomy,
+ 'terms' => $terms,
+ 'allowRegenerate' => false,
+ 'strings' => [],
+ ];
+
+ $config = wp_parse_args( $overrides, $defaults );
+
+ wp_localize_script( 'groq-ai-term-bulk', 'GroqAITermBulk', $config );
+ }
+
+ protected function get_term_overview_data( $taxonomy ) {
+ $taxonomy = sanitize_key( (string) $taxonomy );
+
+ if ( isset( $this->term_overview_cache[ $taxonomy ] ) ) {
+ return $this->term_overview_cache[ $taxonomy ];
+ }
+
+ $rows = [];
+ $empty_rows = [];
+
+ if ( '' !== $taxonomy && taxonomy_exists( $taxonomy ) ) {
+ $terms = get_terms(
+ [
+ 'taxonomy' => $taxonomy,
+ 'hide_empty' => false,
+ 'orderby' => 'name',
+ 'order' => 'ASC',
+ 'number' => 0,
+ ]
+ );
+
+ if ( is_wp_error( $terms ) ) {
+ $terms = [];
+ }
+
+ foreach ( $terms as $term ) {
+ if ( ! $term || ! is_object( $term ) || empty( $term->term_id ) ) {
+ continue;
+ }
+
+ $words = $this->count_words( isset( $term->description ) ? $term->description : '' );
+ $has_description = $words > 0;
+
+ $row = [
+ 'id' => absint( $term->term_id ),
+ 'name' => (string) $term->name,
+ 'slug' => (string) $term->slug,
+ 'count' => isset( $term->count ) ? absint( $term->count ) : 0,
+ 'words' => $words,
+ 'has_description' => $has_description,
+ 'url' => $this->get_term_page_url( $taxonomy, $term->term_id ),
+ ];
+
+ $rows[] = $row;
+ if ( ! $has_description ) {
+ $empty_rows[] = $row;
+ }
+ }
+ }
+
+ $data = [
+ 'rows' => $rows,
+ 'empty_rows' => $empty_rows,
+ 'empty_count' => count( $empty_rows ),
+ ];
+
+ $this->term_overview_cache[ $taxonomy ] = $data;
+
+ return $data;
+ }
+
+ private function count_words( $text ) {
+ $text = wp_strip_all_tags( (string) $text );
+ $text = trim( preg_replace( '/\s+/u', ' ', $text ) );
+ if ( '' === $text ) {
+ return 0;
+ }
+ if ( preg_match_all( '/\pL[\pL\pN\']*/u', $text, $matches ) ) {
+ return count( $matches[0] );
+ }
+ return 0;
+ }
+
+ protected function get_term_page_url( $taxonomy, $term_id ) {
+ return add_query_arg(
+ [
+ 'page' => 'groq-ai-product-text-term',
+ 'taxonomy' => sanitize_key( (string) $taxonomy ),
+ 'term_id' => absint( $term_id ),
+ ],
+ admin_url( 'options-general.php' )
+ );
+ }
+
+ public function enqueue_term_assets( $hook ) {
+ if ( 0 !== strpos( (string) $hook, 'settings_page_groq-ai-product-text-term' ) ) {
+ return;
+ }
+
+ $this->enqueue_admin_styles();
+
+ wp_enqueue_script(
+ 'groq-ai-term-admin',
+ plugins_url( 'assets/js/term-admin.js', GROQ_AI_PRODUCT_TEXT_FILE ),
+ [],
+ GROQ_AI_PRODUCT_TEXT_VERSION,
+ true
+ );
+
+ $taxonomy = isset( $_GET['taxonomy'] ) ? sanitize_key( wp_unslash( $_GET['taxonomy'] ) ) : '';
+ $term_id = isset( $_GET['term_id'] ) ? absint( $_GET['term_id'] ) : 0;
+
+ wp_localize_script(
+ 'groq-ai-term-admin',
+ 'GroqAITermGenerator',
+ [
+ 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
+ 'nonce' => wp_create_nonce( 'groq_ai_generate_term' ),
+ 'taxonomy' => $taxonomy,
+ 'termId' => $term_id,
+ ]
+ );
+ }
+
+ public function render_term_generator_page() {
+ if ( ! $this->current_user_can_manage() ) {
+ return;
+ }
+
+ $taxonomy = isset( $_GET['taxonomy'] ) ? sanitize_key( wp_unslash( $_GET['taxonomy'] ) ) : '';
+ $term_id = isset( $_GET['term_id'] ) ? absint( $_GET['term_id'] ) : 0;
+
+ if ( '' === $taxonomy || ! taxonomy_exists( $taxonomy ) || ! $term_id ) {
+ ?>
+
+
+
+ count_words( $term->description );
+ $meta_prompt = get_term_meta( $term_id, 'groq_ai_term_custom_prompt', true );
+ $settings = $this->plugin->get_settings();
+ $bottom_meta_key = $this->resolve_term_bottom_description_meta_key( $term, $settings );
+ $effective_bottom_meta_key = '' !== $bottom_meta_key ? $bottom_meta_key : 'groq_ai_term_bottom_description';
+ $bottom_description = (string) get_term_meta( $term_id, $effective_bottom_meta_key, true );
+ $rankmath_module_enabled = $this->plugin->is_module_enabled( 'rankmath', $settings );
+ $rankmath_active = $this->plugin->is_rankmath_active();
+ $rankmath_title = '';
+ $rankmath_description = '';
+ $rankmath_focus_keywords = '';
+ if ( $rankmath_module_enabled ) {
+ $rankmath_keys = $this->resolve_rankmath_term_meta_keys( $term, $settings );
+ $rankmath_title = (string) get_term_meta( $term_id, $rankmath_keys['title'], true );
+ $rankmath_description = (string) get_term_meta( $term_id, $rankmath_keys['description'], true );
+ $rankmath_focus_keywords = (string) get_term_meta( $term_id, $rankmath_keys['focus_keyword'], true );
+ }
+ $default_prompt = $this->get_term_prompt_text( $term, $meta_prompt );
+ ?>
+
+
+ : name ); ?>
+
+
+
+
+
+
+
+
+
+
+ 403 ] );
+ }
+
+ check_admin_referer( 'groq_ai_save_term_content' );
+
+ $taxonomy = isset( $_POST['taxonomy'] ) ? sanitize_key( wp_unslash( $_POST['taxonomy'] ) ) : '';
+ $term_id = isset( $_POST['term_id'] ) ? absint( $_POST['term_id'] ) : 0;
+
+ if ( '' === $taxonomy || ! taxonomy_exists( $taxonomy ) || ! $term_id ) {
+ $this->redirect_with_term_notice( $taxonomy, $term_id, 'error', __( 'Ongeldige term.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'error' );
+ }
+
+ $term = get_term( $term_id, $taxonomy );
+ if ( ! $term || is_wp_error( $term ) ) {
+ $this->redirect_with_term_notice( $taxonomy, $term_id, 'error', __( 'Term niet gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'error' );
+ }
+
+ $description = isset( $_POST['description'] ) ? wp_kses_post( wp_unslash( $_POST['description'] ) ) : '';
+ $bottom_description = isset( $_POST['groq_ai_term_bottom_description'] ) ? wp_kses_post( wp_unslash( $_POST['groq_ai_term_bottom_description'] ) ) : '';
+ $custom_prompt = isset( $_POST['groq_ai_term_custom_prompt'] ) ? sanitize_textarea_field( wp_unslash( $_POST['groq_ai_term_custom_prompt'] ) ) : '';
+
+ $update = wp_update_term(
+ $term_id,
+ $taxonomy,
+ [
+ 'description' => $description,
+ ]
+ );
+
+ if ( is_wp_error( $update ) ) {
+ $this->redirect_with_term_notice( $taxonomy, $term_id, 'error', $update->get_error_message(), 'error' );
+ }
+
+ $settings = $this->plugin->get_settings();
+ $bottom_meta_key = $this->resolve_term_bottom_description_meta_key( $term, $settings );
+ $bottom_meta_key = '' !== $bottom_meta_key ? $bottom_meta_key : 'groq_ai_term_bottom_description';
+ update_term_meta( $term_id, $bottom_meta_key, $bottom_description );
+
+ if ( '' === trim( $custom_prompt ) ) {
+ delete_term_meta( $term_id, 'groq_ai_term_custom_prompt' );
+ } else {
+ update_term_meta( $term_id, 'groq_ai_term_custom_prompt', $custom_prompt );
+ }
+
+ if ( isset( $_POST['groq_ai_term_bottom_description'] ) && $bottom_meta_key !== 'groq_ai_term_bottom_description' ) {
+ update_term_meta( $term_id, 'groq_ai_term_bottom_description', $bottom_description );
+ }
+
+ if ( $this->plugin->is_module_enabled( 'rankmath', $settings ) ) {
+ $rankmath_keys = $this->resolve_rankmath_term_meta_keys( $term, $settings );
+ $rankmath_title = isset( $_POST['groq_ai_rankmath_meta_title'] ) ? sanitize_text_field( wp_unslash( $_POST['groq_ai_rankmath_meta_title'] ) ) : '';
+ $rankmath_description = isset( $_POST['groq_ai_rankmath_meta_description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['groq_ai_rankmath_meta_description'] ) ) : '';
+ $rankmath_keywords = isset( $_POST['groq_ai_rankmath_focus_keywords'] ) ? sanitize_text_field( wp_unslash( $_POST['groq_ai_rankmath_focus_keywords'] ) ) : '';
+
+ update_term_meta( $term_id, $rankmath_keys['title'], $rankmath_title );
+ update_term_meta( $term_id, $rankmath_keys['description'], $rankmath_description );
+ update_term_meta( $term_id, $rankmath_keys['focus_keyword'], $rankmath_keywords );
+ }
+
+ $this->redirect_with_term_notice( $taxonomy, $term_id, 'saved', __( 'Term opgeslagen.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) );
+ }
+
+ private function resolve_term_bottom_description_meta_key( $term, $settings ) {
+ $default_key = '';
+ if ( is_array( $settings ) && isset( $settings['term_bottom_description_meta_key'] ) ) {
+ $default_key = sanitize_key( (string) $settings['term_bottom_description_meta_key'] );
+ }
+
+ $key = apply_filters( 'groq_ai_term_bottom_description_meta_key', $default_key, $term, $settings );
+ $key = sanitize_key( (string) $key );
+ return $key;
+ }
+
+ private function resolve_rankmath_term_meta_keys( $term, $settings ) {
+ $keys = [
+ 'title' => 'rank_math_title',
+ 'description' => 'rank_math_description',
+ 'focus_keyword' => 'rank_math_focus_keyword',
+ ];
+ $keys = apply_filters( 'groq_ai_rankmath_term_meta_keys', $keys, $term, $settings );
+ if ( ! is_array( $keys ) ) {
+ $keys = [];
+ }
+
+ return [
+ 'title' => isset( $keys['title'] ) ? sanitize_key( (string) $keys['title'] ) : 'rank_math_title',
+ 'description' => isset( $keys['description'] ) ? sanitize_key( (string) $keys['description'] ) : 'rank_math_description',
+ 'focus_keyword' => isset( $keys['focus_keyword'] ) ? sanitize_key( (string) $keys['focus_keyword'] ) : 'rank_math_focus_keyword',
+ ];
+ }
+
+ private function get_term_prompt_text( $term, $custom_prompt = null ) {
+ $prompt = ( null !== $custom_prompt ) ? $custom_prompt : '';
+
+ if ( null === $custom_prompt && $term && isset( $term->term_id ) ) {
+ $prompt = get_term_meta( $term->term_id, 'groq_ai_term_custom_prompt', true );
+ }
+
+ $prompt = trim( (string) $prompt );
+ if ( '' !== $prompt ) {
+ return $prompt;
+ }
+
+ $default_prompt = __( 'Schrijf een SEO-vriendelijke categorieomschrijving in het Nederlands. Gebruik duidelijke tussenkoppen en
-tags. Voeg geen prijsinformatie toe.', GROQ_AI_PRODUCT_TEXT_DOMAIN );
+
+ return apply_filters( 'groq_ai_default_term_prompt', $default_prompt, $term );
+ }
+
+ private function redirect_with_term_notice( $taxonomy, $term_id, $type, $message = '', $status = 'success' ) {
+ $url = ( $taxonomy && $term_id ) ? $this->get_term_page_url( $taxonomy, $term_id ) : $this->get_page_url( 'groq-ai-product-text-categories' );
+
+ $args = [
+ 'groq_ai_term_notice' => sanitize_key( (string) $type ),
+ 'groq_ai_term_status' => sanitize_key( (string) $status ),
+ ];
+
+ if ( '' !== $message ) {
+ $args['groq_ai_term_notice_message'] = rawurlencode( (string) $message );
+ }
+
+ wp_safe_redirect( add_query_arg( $args, $url ) );
+ exit;
+ }
+
+ private function ensure_term_handler_registered() {
+ if ( self::$term_handler_registered ) {
+ return;
+ }
+
+ add_action( 'admin_post_groq_ai_save_term_content', [ $this, 'handle_save_term_content' ] );
+ self::$term_handler_registered = true;
+ }
+
+ private function ensure_term_assets_hook() {
+ if ( self::$term_assets_hook_registered ) {
+ return;
+ }
+
+ add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_term_assets' ] );
+ self::$term_assets_hook_registered = true;
+ }
+}