—'; return; } echo esc_html( implode( ', ', $parts ) ); } public static function format_device_type( $device_type ) { $device_type = sanitize_key( (string) $device_type ); if ( '' === $device_type ) { return '—'; } return ucfirst( $device_type ); } public static function format_entry_source( $entry_source ) { $entry_source = sanitize_key( (string) $entry_source ); if ( '' === $entry_source ) { return '—'; } $labels = array( 'google_cpc' => __( 'Google Ads', 'adct-tracking-template' ), 'google_organic' => __( 'Google Organic', 'adct-tracking-template' ), 'direct' => __( 'Direct', 'adct-tracking-template' ), 'referral' => __( 'Referral', 'adct-tracking-template' ), 'youtube' => __( 'YouTube', 'adct-tracking-template' ), 'facebook' => __( 'Facebook', 'adct-tracking-template' ), 'instagram' => __( 'Instagram', 'adct-tracking-template' ), ); return $labels[ $entry_source ] ?? ucwords( str_replace( '_', ' ', $entry_source ) ); } public static function format_landing_path_short( $landing_path, $max_length = 42 ) { $landing_path = trim( (string) $landing_path ); if ( '' === $landing_path ) { return ''; } if ( preg_match( '#^https?://#i', $landing_path ) ) { $parsed = wp_parse_url( $landing_path ); if ( ! empty( $parsed['path'] ) ) { $landing_path = $parsed['path']; } if ( ! empty( $parsed['query'] ) ) { $landing_path .= '?' . $parsed['query']; } } elseif ( false !== strpos( $landing_path, '/' ) && '/' !== $landing_path[0] ) { $first_slash = strpos( $landing_path, '/' ); $landing_path = substr( $landing_path, $first_slash ); } $landing_path = preg_replace( '#/+#', '/', $landing_path ); if ( strlen( $landing_path ) <= $max_length ) { return $landing_path; } return substr( $landing_path, 0, $max_length - 1 ) . '…'; } public static function format_duration( $seconds ) { $seconds = absint( $seconds ); if ( 0 === $seconds ) { return '—'; } if ( $seconds < 60 ) { /* translators: %d: number of seconds (abbreviated as "s") */ return sprintf( __( '%ds', 'adct-tracking-template' ), $seconds ); } $minutes = (int) floor( $seconds / 60 ); $remain = $seconds % 60; if ( $minutes < 60 ) { if ( $remain ) { /* translators: 1: number of minutes (abbreviated as "m"), 2: number of seconds (abbreviated as "s") */ return sprintf( __( '%1$dm %2$ds', 'adct-tracking-template' ), $minutes, $remain ); } /* translators: %d: number of minutes (abbreviated as "m") */ return sprintf( __( '%dm', 'adct-tracking-template' ), $minutes ); } $hours = (int) floor( $minutes / 60 ); $mins = $minutes % 60; if ( $mins ) { /* translators: 1: number of hours (abbreviated as "h"), 2: number of minutes (abbreviated as "m") */ return sprintf( __( '%1$dh %2$dm', 'adct-tracking-template' ), $hours, $mins ); } /* translators: %d: number of hours (abbreviated as "h") */ return sprintf( __( '%dh', 'adct-tracking-template' ), $hours ); } public static function has_attribution_data( $row ) { $fields = array( 'entry_source', 'landing_url', 'landing_path', 'utm_campaign', 'utm_source', 'utm_medium', 'gclid', 'landing_referrer', ); foreach ( $fields as $field ) { if ( ! empty( $row->$field ) ) { return true; } } return ! empty( $row->duration_seconds ); } public static function render_product_image( $url, $title ) { if ( empty( $url ) ) { echo ''; return; } printf( '%s', esc_url( $url ), esc_attr( $title ) ); } public static function render_pagination( $total_items, $per_page, $paged, $query_args ) { $total_pages = max( 1, (int) ceil( $total_items / $per_page ) ); if ( $total_pages <= 1 ) { return; } $query_args['per_page'] = $per_page; unset( $query_args['paged'] ); $links = paginate_links( array( 'base' => add_query_arg( array_merge( $query_args, array( 'paged' => '%#%' ) ), admin_url( 'admin.php' ) ), 'format' => '', 'current' => $paged, 'total' => $total_pages, 'prev_text' => '« ' . __( 'Previous', 'adct-tracking-template' ), 'next_text' => __( 'Next', 'adct-tracking-template' ) . ' »', 'type' => 'plain', ) ); if ( ! $links ) { return; } echo '
' . wp_kses_post( $links ) . '
'; } public static function format_contact_type_label( $contact_type ) { $labels = array( 'whatsapp' => __( 'WhatsApp', 'adct-tracking-template' ), 'phone' => __( 'Phone', 'adct-tracking-template' ), 'showroom_landline' => __( 'Showroom', 'adct-tracking-template' ), 'floating_whatsapp' => __( 'Floating WhatsApp', 'adct-tracking-template' ), 'floating_phone' => __( 'Floating Call', 'adct-tracking-template' ), 'elfsight_call' => __( 'Elfsight Call', 'adct-tracking-template' ), 'footer_landline' => __( 'Footer Call', 'adct-tracking-template' ), ); return $labels[ $contact_type ] ?? ucwords( str_replace( '_', ' ', (string) $contact_type ) ); } public static function contact_type_badge_class( $contact_type ) { $map = array( 'whatsapp' => 'is-whatsapp', 'phone' => 'is-phone', 'showroom_landline' => 'is-showroom', 'floating_whatsapp' => 'is-floating', 'floating_phone' => 'is-floating', 'elfsight_call' => 'is-elfsight', 'footer_landline' => 'is-showroom', ); return $map[ $contact_type ] ?? ''; } public static function render_meta_box( $title, $items ) { ?>

entry_source ) ) { $items[] = array( 'label' => __( 'Entry source', 'adct-tracking-template' ), 'value' => self::format_entry_source( $row->entry_source ), ); } if ( ! empty( $row->landing_url ) ) { $items[] = array( 'label' => __( 'Landing URL', 'adct-tracking-template' ), 'value' => $row->landing_url, 'url' => $row->landing_url, ); } elseif ( ! empty( $row->landing_path ) ) { $items[] = array( 'label' => __( 'Landing page', 'adct-tracking-template' ), 'value' => $row->landing_path, ); } if ( ! empty( $row->session_duration ) || ! empty( $row->duration_seconds ) ) { $items[] = array( 'label' => __( 'Session time', 'adct-tracking-template' ), 'value' => self::format_duration( $row->session_duration ?? $row->duration_seconds ?? 0 ), ); } if ( ! empty( $row->utm_campaign ) ) { $items[] = array( 'label' => __( 'Campaign name', 'adct-tracking-template' ), 'value' => $row->utm_campaign, ); } if ( ! empty( $row->utm_id ) && $row->utm_id !== ( $row->utm_campaign ?? '' ) ) { $items[] = array( 'label' => __( 'Campaign ID', 'adct-tracking-template' ), 'value' => $row->utm_id, ); } if ( ! empty( $row->utm_source ) || ! empty( $row->utm_medium ) ) { $items[] = array( 'label' => __( 'UTM source / medium', 'adct-tracking-template' ), 'value' => trim( ( $row->utm_source ?? '' ) . ' / ' . ( $row->utm_medium ?? '' ), ' /' ), ); } if ( ! empty( $row->utm_term ) ) { $items[] = array( 'label' => __( 'Keyword', 'adct-tracking-template' ), 'value' => $row->utm_term, ); } if ( ! empty( $row->landing_referrer ) ) { $items[] = array( 'label' => __( 'Referrer', 'adct-tracking-template' ), 'value' => $row->landing_referrer, ); } return $items; } public static function render_product_block( $row ) { $title = $row->product_title ?? ''; $url = $row->product_url ?? ''; $image = $row->product_image_url ?? ''; ?>
<?php echo esc_attr( $title ); ?>

product_price ) ) : ?> product_price ); ?> product_mileage ) ) : ?> product_mileage ); ?>
visitor_country ?? '', $row->visitor_region ?? '' ) ) ) ); $badge = self::contact_type_badge_class( $row->contact_type ?? '' ); ?>
contact_type ?? '' ) ); ?>
__( 'Salesman', 'adct-tracking-template' ), 'value' => $row->agent_name ?: '—', ), array( 'label' => __( 'Clicked', 'adct-tracking-template' ), 'value' => $row->clicked_value ?: '—', ), array( 'label' => __( 'Source', 'adct-tracking-template' ), 'value' => $row->source ?: '—', ), ) ); self::render_meta_box( __( 'Visitor', 'adct-tracking-template' ), array( array( 'label' => __( 'Device', 'adct-tracking-template' ), 'value' => self::format_device_type( $row->device_type ?? '' ), ), array( 'label' => __( 'Browser', 'adct-tracking-template' ), 'value' => $row->browser_name ?: '—', ), array( 'label' => __( 'Location', 'adct-tracking-template' ), 'value' => $location ?: '—', ), ) ); if ( self::has_attribution_data( $row ) ) { self::render_meta_box( __( 'Marketing', 'adct-tracking-template' ), self::build_marketing_items( $row ) ); } ?>
contact_type ?? '' ); ?>
contact_type ?? '' ) ); ?>
__( 'Salesman', 'adct-tracking-template' ), 'value' => $row->agent_name ?: '—', ), array( 'label' => __( 'Clicked', 'adct-tracking-template' ), 'value' => $row->clicked_value ?: '—', ), array( 'label' => __( 'Page clicked', 'adct-tracking-template' ), 'value' => $row->product_url ?: '—', 'url' => $row->product_url ?: '', ), ) ); ?>
visitor_country ?? '', $session->visitor_region ?? '' ) ) ) ); $entry_source = sanitize_key( (string) ( $session->entry_source ?? '' ) ); $source_class = $entry_source ? 'is-source-' . $entry_source : 'is-source-direct'; $click_types = array(); foreach ( $clicks as $click ) { $type = $click->contact_type ?? ''; if ( $type && ! in_array( $type, $click_types, true ) ) { $click_types[] = $type; } } ?>
>
session_started ) ); ?> → session_ended ) ); ?>
utm_campaign ) ) : ?> utm_campaign ); ?> landing_path ) ) : ?> landing_path ); ?>
click_count ) ) ); ?> session_duration ?? 0 ) ); ?> entry_source ) ) : ?> entry_source ) ); ?>
'; self::render_meta_box( __( 'Visitor', 'adct-tracking-template' ), array( array( 'label' => __( 'Device', 'adct-tracking-template' ), 'value' => self::format_device_type( $session->device_type ?? '' ), ), array( 'label' => __( 'Browser', 'adct-tracking-template' ), 'value' => $session->browser_name ?: '—', ), array( 'label' => __( 'Location', 'adct-tracking-template' ), 'value' => $location ?: '—', ), ) ); self::render_meta_box( __( 'Marketing', 'adct-tracking-template' ), $marketing_items ); echo '
'; } ?>

contact_type ?? '' ); ?>
agent_name ?: __( 'No salesman', 'adct-tracking-template' ) ); ?> contact_type ?? '' ) ); ?>
clicks ) ) ); ?> last_clicked_at ) ) : ?> last_clicked_at ) ); ?>
class_exists( 'WooCommerce' ), 'tracker_js' => file_exists( $tracker_path ), 'entry_js' => file_exists( $entry_path ), 'wp_rocket' => defined( 'WP_ROCKET_VERSION' ), ); } public static function render_sidebar( array $snapshot, $export_url, $show_setup ) { $status = self::get_system_status(); $can_manage = ADCT_Settings::user_can_manage(); $allowed_roles = ADCT_Settings::get_allowed_roles(); $available_roles = ADCT_Settings::get_available_roles(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display filter, not a state-changing action $access_saved = isset( $_GET['adct_access'] ) && 'saved' === $_GET['adct_access']; ?> $snapshot, 'show_setup' => $show_setup, ); } public static function render_overview_page() { if ( ! ADCT_Settings::user_can_view() ) { return; } $period = ADCT_Analytics::get_period_from_request(); $overview = ADCT_Analytics::get_overview_data( $period ); $page = self::get_page_context(); $snapshot = $page['snapshot']; $show_setup = $page['show_setup']; $sessions_url = admin_url( 'admin.php?page=adct-contact-click-tracker-sessions' ); $leads_url = admin_url( 'admin.php?page=adct-contact-click-tracker-leads' ); $has_data = ! empty( $overview['totals']['clicks'] ); $has_marketing = ! empty( $overview['source_breakdown'] ) || ! empty( $overview['campaign_breakdown'] ) || ! empty( $overview['landing_breakdown'] ); $top_kpi_label = __( 'Top campaign', 'adct-tracking-template' ); $top_kpi_value = $overview['totals']['top_campaign']; if ( '—' === $top_kpi_value ) { $top_kpi_label = __( 'Top source', 'adct-tracking-template' ); $top_kpi_value = $overview['totals']['top_source']; } wp_localize_script( 'adct-overview', 'adctOverviewConfig', array( 'contactLabels' => $overview['charts']['contact_labels'], 'contactCounts' => $overview['charts']['contact_counts'], 'contactColors' => $overview['charts']['contact_colors'], 'sourceLabels' => $overview['charts']['source_labels'], 'sourceCounts' => $overview['charts']['source_counts'], 'sourceColors' => $overview['charts']['source_colors'], 'campaignLabels' => $overview['charts']['campaign_labels'], 'campaignCounts' => $overview['charts']['campaign_counts'], 'campaignColors' => $overview['charts']['campaign_colors'], 'dayLabels' => $overview['charts']['labels'], 'clickSeries' => $overview['charts']['clicks'], 'sessionSeries' => $overview['charts']['sessions'], ) ); ?>

  • ·

  • clicked_at ?? '' ) ); ?>
    product_title ?: __( 'Site-wide contact', 'adct-tracking-template' ) ); ?> contact_type ?? '' ), ! empty( $lead->entry_source ) ? self::format_entry_source( $lead->entry_source ) : '', ADCT_Leads::is_salesman_attributed_click( $lead->contact_type ?? '', $lead->agent_name ?? '' ) ? $lead->agent_name : '', ) ) ) ) ); ?>
    contact_type ?? '' ) ); ?>

' . esc_html__( 'No data yet.', 'adct-tracking-template' ) . '

'; return; } ?> ' . esc_html__( 'No attributed traffic yet. Once visitors arrive from Google, ads, or referrals and click contact, channels will appear here.', 'adct-tracking-template' ) . '

'; return; } ?>
$total_pages ) { $paged = $total_pages; $offset = ( $paged - 1 ) * $per_page; } $lead_rows = ADCT_Database::get_clicks( $filters, $per_page, $offset ); $session_context = ADCT_Database::get_lead_session_context( $lead_rows ); $channel_counts = ADCT_Leads::get_channel_counts( $tab_filters ); $agents = ADCT_Database::get_distinct_values( 'agent_name' ); $entry_sources = ADCT_Database::get_distinct_values( 'entry_source' ); $campaigns = ADCT_Database::get_distinct_values( 'utm_campaign' ); $page_context = self::get_page_context(); $snapshot = $page_context['snapshot']; $show_setup = $page_context['show_setup']; $base_url = admin_url( 'admin.php?page=adct-contact-click-tracker-leads' ); $query_args = array_merge( $tab_filters, array( 'page' => 'adct-contact-click-tracker-leads', 'lead_channel' => $channel, 'per_page' => $per_page, ) ); $tab_query = array_merge( $tab_filters, array( 'page' => 'adct-contact-click-tracker-leads', 'per_page' => $per_page, ) ); $from_item = $list_total ? ( $offset + 1 ) : 0; $to_item = min( $offset + $per_page, $list_total ); $export_url = wp_nonce_url( add_query_arg( array_merge( // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display filter, not a state-changing action $_GET, array( 'adct_export' => 'csv', 'page' => 'adct-contact-click-tracker-leads', ) ), $base_url ), 'adct_export_csv' ); ?>

' . esc_html__( 'Likely engaged', 'adct-tracking-template' ) . '' ); ?>

contact_type ?? '' ); $status = ADCT_Leads::get_lead_status_label( $row->contact_type ?? '' ); $has_product = ! empty( $row->product_title ) || ! empty( $row->product_id ); $date_parts = ADCT_Leads::format_lead_datetime_parts( $row->clicked_at ?? '' ); $source_text = ! empty( $row->entry_source ) ? self::format_entry_source( $row->entry_source ) : '—'; $source_hint = self::format_landing_path_short( $row->landing_path ?? '' ); ?>
contact_type ?? '' ) ); ?>
product_image_url ) ) : ?> <?php echo esc_attr( $row->product_title ?? '' ); ?>

product_title ?: __( 'Site-wide contact', 'adct-tracking-template' ) ); ?>

product_price ) || ! empty( $row->product_mileage ) ) : ?>

product_price ?? '', $row->product_mileage ?? '', ) ) ) ) ); ?>

product_url ) ) : ?>

utm_campaign ?: '—' ); ?> contact_type ?? '', $row->agent_name ?? '' ) ); ?> id ?? 0 ); $position = $positions[ $row_id ] ?? null; $summary = ( ! empty( $session_key ) && isset( $summaries[ $session_key ] ) ) ? $summaries[ $session_key ] : null; $session_url = ADCT_Leads::build_session_view_url( $session_key ); if ( '' === $session_key || ! $session_url ) { echo '' . esc_html__( 'No session', 'adct-tracking-template' ) . ''; return; } $session_code = ADCT_Leads::format_session_code( $session_key ); $position_txt = $position ? sprintf( /* translators: 1: click number in session, 2: total clicks in session */ __( 'Click %1$d of %2$d', 'adct-tracking-template' ), (int) $position['position'], (int) $position['total'] ) : ''; ?>
session_id ?? '', $row->clicked_at ?? '', $row->product_title, $row->product_url, $row->product_price ?? '', $row->product_mileage ?? '', $row->product_image_url ?? '', $row->agent_name, $row->contact_type, $row->clicked_value ?? '', $row->source ?? '', $row->device_type ?? '', $row->browser_name ?? '', $row->visitor_country ?? '', $row->visitor_region ?? '', $row->entry_source ?? '', $row->landing_url ?? '', $row->landing_path ?? '', $row->landing_referrer ?? '', $row->utm_source ?? '', $row->utm_medium ?? '', $row->utm_campaign ?? '', $row->utm_term ?? '', $row->utm_content ?? '', $row->gclid ?? '', $row->duration_seconds ?? 0, ) ); } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- streaming CSV directly to php://output for browser download, not a real file; WP_Filesystem has no stream-output equivalent fclose( $output ); exit; } public static function render_reports_page() { if ( ! ADCT_Settings::user_can_view() ) { return; } $filters = ADCT_Database::get_filters_from_request(); $pagination = ADCT_Database::get_pagination_args(); $per_page = $pagination['per_page']; $paged = $pagination['paged']; $offset = $pagination['offset']; $list_total = ADCT_Database::count_sessions( $filters ); $total_pages = max( 1, (int) ceil( $list_total / $per_page ) ); if ( $paged > $total_pages ) { $paged = $total_pages; $offset = ( $paged - 1 ) * $per_page; } $session_rows = ADCT_Database::get_sessions( $filters, $per_page, $offset ); $session_keys = array_map( static function ( $session ) { return $session->session_key; }, $session_rows ); $clicks_by_session = ADCT_Database::get_clicks_for_session_keys( $session_keys, $filters ); $total_clicks = ADCT_Database::count_clicks( $filters ); $agents = ADCT_Database::get_distinct_values( 'agent_name' ); $types = ADCT_Database::get_distinct_values( 'contact_type' ); $devices = ADCT_Database::get_distinct_values( 'device_type' ); $countries = ADCT_Database::get_distinct_values( 'visitor_country' ); $entry_sources = ADCT_Database::get_distinct_values( 'entry_source' ); $campaigns = ADCT_Database::get_distinct_values( 'utm_campaign' ); $landing_paths = ADCT_Database::get_distinct_values( 'landing_path' ); $page_context = self::get_page_context(); $snapshot = $page_context['snapshot']; $show_setup = $page_context['show_setup']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display filter, not a state-changing action $focus_session = ! empty( $filters['session_id'] ) ? sanitize_text_field( $filters['session_id'] ) : ''; $base_url = admin_url( 'admin.php?page=adct-contact-click-tracker-sessions' ); $query_args = array_merge( $filters, array( 'page' => 'adct-contact-click-tracker-sessions', 'per_page' => $per_page, ) ); $from_item = $list_total ? ( $offset + 1 ) : 0; $to_item = min( $offset + $per_page, $list_total ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display filter, not a state-changing action $export_url = wp_nonce_url( add_query_arg( array_merge( $_GET, array( 'adct_export' => 'csv' ) ), $base_url ), 'adct_export_csv' ); ?>

session_key ] ?? array(); $is_focused = $focus_session && $focus_session === (string) $session->session_key; self::render_session_card( $session, $session_clicks, $is_focused ); ?>