*/ const RANGES = array( '7d' => 7, '30d' => 30, '90d' => 90, ); /** * Register hooks. * * @return void */ public function hooks() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); } /** * Enqueue the dashboard's local CSS/JS, but only on the dashboard screen. * * @param string $hook Current admin page hook suffix. * @return void */ public function enqueue_assets( $hook ) { if ( self::HOOK !== $hook ) { return; } wp_enqueue_style( self::HANDLE, ADPRIVA_PLUGIN_URL . 'assets/css/dashboard.css', array(), ADPRIVA_VERSION ); wp_enqueue_script( self::HANDLE, ADPRIVA_PLUGIN_URL . 'assets/js/dashboard.js', array(), ADPRIVA_VERSION, true ); } /** * Render the top-level page: the dashboard when connected, otherwise a Connect * call-to-action. * * @return void */ public static function render() { if ( ! current_user_can( 'manage_options' ) ) { return; } if ( ! Adpriva_Tokens::is_connected() ) { self::render_connect_screen(); return; } $range = self::current_range(); $window = self::window_for( $range ); $summary = Adpriva_API::summary( $window['from'], $window['to'] ); echo '
'; echo '

' . esc_html__( 'AdPriva', 'adpriva' ) . '

'; self::render_range_form( $range ); if ( is_wp_error( $summary ) ) { echo '

' . esc_html__( "We couldn't load your dashboard right now. Please try again shortly.", 'adpriva' ) . '

'; echo '
'; return; } self::render_cards( $summary ); self::render_breakdown( $summary ); $series = Adpriva_API::timeseries( $window['from'], $window['to'] ); $points = ( ! is_wp_error( $series ) && isset( $series['points'] ) && is_array( $series['points'] ) ) ? $series['points'] : array(); self::render_chart( $points ); printf( '

%2$s

', esc_url( Adpriva_Config::console_url() ), esc_html__( 'Open the full AdPriva console →', 'adpriva' ) ); echo ''; } /** * The validated active range: a valid ?range= wins, else the configured default. * * @return string */ public static function current_range() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only view state; no data is mutated. $requested = isset( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : ''; if ( isset( self::RANGES[ $requested ] ) ) { return $requested; } $default = Adpriva_Config::default_range(); return isset( self::RANGES[ $default ] ) ? $default : '30d'; } /** * The span (in days) for a range key, defaulting to 30. * * @param string $range Range key. * @return int */ public static function days_for( $range ) { return isset( self::RANGES[ $range ] ) ? self::RANGES[ $range ] : 30; } /** * The [from, to] YYYY-MM-DD window for a range, ending today (UTC). * * @param string $range Range key. * @return array{from: string, to: string} */ public static function window_for( $range ) { $days = self::days_for( $range ); $now = time(); return array( 'from' => gmdate( 'Y-m-d', $now - ( $days * DAY_IN_SECONDS ) ), 'to' => gmdate( 'Y-m-d', $now ), ); } /** * Render the range selector (a GET form that reloads the page with ?range=). * * @param string $active Active range key. * @return void */ private static function render_range_form( $active ) { $labels = array( '7d' => __( 'Last 7 days', 'adpriva' ), '30d' => __( 'Last 30 days', 'adpriva' ), '90d' => __( 'Last 90 days', 'adpriva' ), ); echo '
'; echo ''; echo ''; echo ' '; // Submitted on change by dashboard.js; this button keeps it working without JS. echo ''; echo '
'; } /** * Render the KPI cards from the summary payload. * * @param array $summary Summary response. * @return void */ private static function render_cards( array $summary ) { $cards = array( __( 'Verified events', 'adpriva' ) => self::num( $summary, 'total_count' ), __( 'No automation found', 'adpriva' ) => self::num( $summary, 'human_count' ), __( 'AI agent', 'adpriva' ) => self::num( $summary, 'authorized_agent_count' ), __( 'Automation detected', 'adpriva' ) => self::num( $summary, 'abusive_bot_count' ), ); echo '
'; foreach ( $cards as $label => $value ) { printf( '
%1$s%2$s
', esc_html( number_format_i18n( $value ) ), esc_html( $label ) ); } echo '
'; } /** * Render the verdict breakdown list. * * @param array $summary Summary response. * @return void */ private static function render_breakdown( array $summary ) { $rows = array( __( 'No automation found', 'adpriva' ) => self::num( $summary, 'human_count' ), __( 'AI agent', 'adpriva' ) => self::num( $summary, 'authorized_agent_count' ), __( 'Automation detected', 'adpriva' ) => self::num( $summary, 'abusive_bot_count' ), __( 'Possible automation', 'adpriva' ) => self::num( $summary, 'uncertain_count' ), ); echo '

' . esc_html__( 'Check results', 'adpriva' ) . '

'; echo ''; } /** * Render the trend chart container with the series as a JSON data island that * the bundled dashboard.js reads and draws as an inline SVG. * * @param array> $points Per-day series. * @return void */ private static function render_chart( array $points ) { echo '

' . esc_html__( 'Traffic trend', 'adpriva' ) . '

'; printf( '
', esc_attr( (string) wp_json_encode( array_values( $points ) ) ) ); } /** * Render the not-connected Connect call-to-action. * * @return void */ private static function render_connect_screen() { echo '
'; echo '

' . esc_html__( 'AdPriva', 'adpriva' ) . '

'; echo '

' . esc_html__( 'Connect this site to AdPriva to verify your ad traffic and see your dashboard here.', 'adpriva' ) . '

'; echo '
'; echo ''; wp_nonce_field( 'adpriva_connect' ); echo '

'; echo '
'; echo '
'; } /** * Read an integer metric from the summary payload. * * @param array $summary Summary response. * @param string $key Metric key. * @return int */ private static function num( array $summary, $key ) { return isset( $summary[ $key ] ) ? (int) $summary[ $key ] : 0; } }