' . esc_html__( 'Score', 'prodlyra' ) . '', '' . esc_html__( 'Settings', 'prodlyra' ) . '' ); return $links; } /** * Page router. */ public static function render() { if ( ! current_user_can( 'manage_woocommerce' ) ) { return; } // Only selects which tab to display; nothing is changed. $tab = isset( $_GET['tab'] ) && 'settings' === sanitize_key( wp_unslash( $_GET['tab'] ) ) ? 'settings' : 'score'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended echo '
'; echo '

' . esc_html__( 'Prodlyra', 'prodlyra' ) . '

'; echo '

' . esc_html__( 'How well AI shopping assistants (ChatGPT, Gemini, Perplexity) can read and recommend your products.', 'prodlyra' ) . '

'; echo ''; if ( 'settings' === $tab ) { self::render_settings(); } else { self::render_score(); } echo '
'; } /** * Score tab. */ private static function render_score() { $r = Prodlyra_Scanner::run(); $score = $r['score']; $level = $score >= 80 ? 'good' : ( $score >= 50 ? 'medium' : 'low' ); $sections = array( 'access' => __( 'Access for AI assistants', 'prodlyra' ), 'products' => __( 'Product data', 'prodlyra' ), 'policies' => __( 'Shipping and returns', 'prodlyra' ), 'protocols' => __( 'Files for AI agents', 'prodlyra' ), ); echo '
'; echo '
' . esc_html( (string) $score ) . '/100
'; echo '
'; if ( 'good' === $level ) { echo '

' . esc_html__( 'Your store is easy for AI assistants to read.', 'prodlyra' ) . '

'; } elseif ( 'medium' === $level ) { echo '

' . esc_html__( 'AI assistants can read your store, with gaps.', 'prodlyra' ) . '

'; } else { echo '

' . esc_html__( 'AI assistants miss most of what they need.', 'prodlyra' ) . '

'; } if ( $r['total'] > $r['sampled'] ) { /* translators: 1: products checked, 2: total products. */ echo '

' . esc_html( sprintf( __( 'Based on your %1$d newest products out of %2$d.', 'prodlyra' ), $r['sampled'], $r['total'] ) ) . '

'; } else { /* translators: %d: number of products. */ echo '

' . esc_html( sprintf( _n( 'Based on %d product.', 'Based on all %d products.', $r['sampled'], 'prodlyra' ), $r['sampled'] ) ) . '

'; } echo '
'; // Fixes, biggest gain first. $fixes = array_filter( $r['checks'], function ( $c ) { return ! $c['passed'] && '' !== $c['fix']; } ); usort( $fixes, function ( $a, $b ) { return ( $b['max'] - $b['points'] ) <=> ( $a['max'] - $a['points'] ); } ); if ( $fixes ) { echo '

' . esc_html__( 'What to fix first', 'prodlyra' ) . '

    '; foreach ( $fixes as $fix ) { $gain = round( $fix['max'] - $fix['points'] ); echo '
  1. ' . esc_html( $fix['label'] ) . ' '; /* translators: %s: points. */ echo '' . esc_html( sprintf( __( '+%s points', 'prodlyra' ), $gain ) ) . '
    '; echo esc_html( $fix['fix'] ); if ( $fix['link'] ) { echo ' ' . esc_html__( 'Open settings', 'prodlyra' ) . ''; } echo '
  2. '; } echo '
'; } // Every check, by section. echo '

' . esc_html__( 'All checks', 'prodlyra' ) . '

'; echo ''; foreach ( $sections as $key => $title ) { echo ''; foreach ( $r['checks'] as $c ) { if ( $c['section'] !== $key ) { continue; } $result = $c['passed'] ? __( 'OK', 'prodlyra' ) : __( 'To fix', 'prodlyra' ); if ( '' !== $c['result'] ) { $result = $c['result']; } elseif ( null !== $c['of'] ) { /* translators: 1: products passing, 2: products checked. */ $result = sprintf( __( '%1$d of %2$d products', 'prodlyra' ), $c['ok'], $c['of'] ); } printf( '', esc_html( $c['label'] ), $c['passed'] ? 'prodlyra-ok' : 'prodlyra-ko', esc_html( $result ), esc_html( number_format_i18n( $c['points'], 1 ) ), absint( $c['max'] ) ); } } echo '
' . esc_html__( 'Check', 'prodlyra' ) . '' . esc_html__( 'Result', 'prodlyra' ) . '' . esc_html__( 'Points', 'prodlyra' ) . '
' . esc_html( $title ) . '
%s%s%s / %d
'; // Products to fix. if ( $r['products'] ) { echo '

' . esc_html__( 'Products to fix', 'prodlyra' ) . '

'; if ( $r['to_fix'] > count( $r['products'] ) ) { /* translators: 1: products shown, 2: products with issues. */ echo '

' . esc_html( sprintf( __( 'Showing %1$d of %2$d products with issues.', 'prodlyra' ), count( $r['products'] ), $r['to_fix'] ) ) . '

'; } echo ''; foreach ( $r['products'] as $row ) { $product = $row['product']; $edit = get_edit_post_link( $product->get_id() ); printf( '', esc_url( $edit ? $edit : '' ), esc_html( $product->get_name() ), esc_html( implode( ', ', $row['issues'] ) ) ); } echo '
' . esc_html__( 'Product', 'prodlyra' ) . '' . esc_html__( 'Issues', 'prodlyra' ) . '
%s%s
'; } echo ''; } /** * Settings tab. */ private static function render_settings() { $s = Prodlyra_Settings::get(); $name = Prodlyra_Settings::OPTION; echo '
'; settings_fields( 'prodlyra' ); echo '

' . esc_html__( 'Access', 'prodlyra' ) . '

'; self::checkbox( $name, 'allow_ai_bots', $s, __( 'Allow AI shopping assistants', 'prodlyra' ), __( 'Adds rules to robots.txt so ChatGPT, Perplexity and Claude can read your products (never cart, checkout or account pages).', 'prodlyra' ) ); self::checkbox( $name, 'llms_enabled', $s, __( 'Publish llms.txt', 'prodlyra' ), __( 'A short summary of your store, categories and policies for AI assistants, at /llms.txt.', 'prodlyra' ) ); echo '
'; echo '

' . esc_html__( 'Brand', 'prodlyra' ) . '

'; self::text( $name, 'default_brand', $s, __( 'Default brand', 'prodlyra' ), __( 'Used for products without a brand. If you make your own products, enter your store name.', 'prodlyra' ), get_bloginfo( 'name' ) ); echo '
'; echo '

' . esc_html__( 'Shipping', 'prodlyra' ) . '

'; self::text( $name, 'ship_countries', $s, __( 'Countries', 'prodlyra' ), __( 'Two-letter codes separated by commas, for example TN, FR, BE. Empty = your store country.', 'prodlyra' ), 'TN, FR' ); /* translators: %s: currency code. */ self::text( $name, 'ship_cost', $s, sprintf( __( 'Shipping cost (%s)', 'prodlyra' ), get_woocommerce_currency() ), __( 'Your usual cost. Enter 0 for free shipping. Empty = not shown to AI assistants.', 'prodlyra' ), '7' ); self::range( $name, 'handling', $s, __( 'Preparation time (days)', 'prodlyra' ) ); self::range( $name, 'transit', $s, __( 'Delivery time (days)', 'prodlyra' ) ); echo '
'; echo '

' . esc_html__( 'Returns', 'prodlyra' ) . '

'; self::text( $name, 'return_days', $s, __( 'Return window (days)', 'prodlyra' ), __( 'For example 14 or 30. Enter 0 if you do not accept returns. Empty = not shown to AI assistants.', 'prodlyra' ), '14' ); echo '
' . esc_html__( 'Return shipping', 'prodlyra' ) . ''; foreach ( array( 'customer' => __( 'Paid by the customer', 'prodlyra' ), 'free' => __( 'Free for the customer', 'prodlyra' ), ) as $value => $label ) { printf( '', esc_attr( $name ), esc_attr( $value ), checked( $s['return_fees'], $value, false ), esc_html( $label ) ); } echo '
'; submit_button(); echo '
'; } /** * Checkbox row. * * @param string $name Option name. * @param string $key Setting key. * @param array $s Settings. * @param string $label Label. * @param string $help Help text. */ private static function checkbox( $name, $key, $s, $label, $help ) { printf( '%1$s', esc_html( $label ), esc_attr( $name ), esc_attr( $key ), checked( (int) $s[ $key ], 1, false ), esc_html( $help ) ); } /** * Text input row. * * @param string $name Option name. * @param string $key Setting key. * @param array $s Settings. * @param string $label Label. * @param string $help Help text. * @param string $placeholder Placeholder. */ private static function text( $name, $key, $s, $label, $help, $placeholder = '' ) { printf( '

%6$s

', esc_html( $label ), esc_attr( $name ), esc_attr( $key ), esc_attr( (string) $s[ $key ] ), esc_attr( $placeholder ), esc_html( $help ) ); } /** * Min–max days row. * * @param string $name Option name. * @param string $prefix "handling" or "transit". * @param array $s Settings. * @param string $label Label. */ private static function range( $name, $prefix, $s, $label ) { printf( '%1$s – ', esc_html( $label ), esc_attr( $name ), esc_attr( $prefix ), absint( $s[ $prefix . '_min' ] ), esc_attr__( 'Minimum', 'prodlyra' ), absint( $s[ $prefix . '_max' ] ), esc_attr__( 'Maximum', 'prodlyra' ) ); } }