' . esc_html( sprintf( __( 'You have used %s%% of this month\'s estimated AI budget.', 'linkcortex' ), number_format_i18n( (int) round( $budget['percent'] ) ) ) ) . '
';
}
self::render_kpis( $kpis );
$status = Availability::cached();
echo '';
if ( $is_admin && Availability::is_available() ) {
echo '
' . esc_html__( 'Scan for link suggestions…', 'linkcortex' ) . ' ';
echo '
' . esc_html__( 'You will see the estimated cost and can cancel before anything is sent to AI. Pages that have not changed since the last scan are skipped for free.', 'linkcortex' ) . '
';
} elseif ( $is_admin ) {
echo '
' . esc_html__( 'AI is not ready yet.', 'linkcortex' ) . ' ' . esc_html( $status ? $status['message'] : __( 'Check the AI connection to start scanning.', 'linkcortex' ) ) . '
';
echo '
' . esc_html__( 'Check AI connection', 'linkcortex' ) . ' ';
echo '' . esc_html__( 'Update page index (free, no AI)', 'linkcortex' ) . '
';
echo '
' . esc_html__( 'Your page index, link map and orphan report work without AI.', 'linkcortex' ) . '
';
} else {
echo '
' . esc_html__( 'An administrator runs scans. You can review suggestions for your own pages.', 'linkcortex' ) . ' ' . esc_html__( 'Review suggestions', 'linkcortex' ) . '
';
}
Keywords_Page::progress_box( $is_admin ? Queue::active_run( array( 'scan', 'regenerate', 'keywords', 'index' ) ) : null );
echo '
';
self::render_quick_wins();
echo '';
}
/**
* KPI numbers.
*
* @return array
*/
public static function kpis() {
global $wpdb;
$t = Activator::tables();
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Own tables, live numbers.
$indexed = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i', $t['index'] ) );
$pending = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE status IN ('pending','accepted')", $t['suggestions'] ) );
$applied = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i WHERE reverted_at IS NULL AND applied_at >= %s', $t['applied'], gmdate( 'Y-m-01 00:00:00' ) ) );
$scans = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE type IN ('scan','regenerate')", $t['runs'] ) );
// phpcs:enable
return array(
'indexed' => $indexed,
'orphans' => LinkGraph::orphan_count(),
'pending' => $pending,
'applied' => $applied,
'scans' => $scans,
'budget' => Budget::status(),
);
}
/**
* KPI cards.
*
* @param array $k KPIs.
* @return void
*/
private static function render_kpis( array $k ) {
$b = $k['budget'];
$spend = $b['cap'] > 0
/* translators: 1: spent, 2: cap. */
? sprintf( __( '%1$s of %2$s', 'linkcortex' ), Cost_Panel::money( $b['spent'] ), Cost_Panel::money( $b['cap'] ) )
: Cost_Panel::money( $b['spent'] );
$cards = array(
array( __( 'Pages indexed', 'linkcortex' ), number_format_i18n( $k['indexed'] ), Menu::url( 'keywords' ), current_user_can( 'edit_others_posts' ) ),
array( __( 'Orphan pages', 'linkcortex' ), number_format_i18n( $k['orphans'] ), Menu::url( 'keywords', array( 'view' => 'orphan' ) ), current_user_can( 'edit_others_posts' ), __( 'No other page links to them.', 'linkcortex' ) ),
array( __( 'Suggestions to review', 'linkcortex' ), number_format_i18n( $k['pending'] ), Menu::url( 'suggestions' ), true ),
array( __( 'Links applied this month', 'linkcortex' ), number_format_i18n( $k['applied'] ), Menu::url( 'suggestions', array( 'status' => 'applied' ) ), true ),
array( __( 'Estimated AI spend this month', 'linkcortex' ), $spend, Menu::url( 'settings', array( 'tab' => 'ai' ) ), current_user_can( 'manage_options' ) ),
);
echo '' . esc_html__( 'The most useful links to add first: strong suggestions that point to pages with few or no links yet.', 'linkcortex' ) . '
';
if ( ! $rows ) {
echo '' . esc_html__( 'No suggestions to review yet. Run a scan to get link ideas.', 'linkcortex' ) . '
';
return;
}
echo '