repository = $repository;
}
public static function labels() {
$labels = array(
'login_otp' => __( 'Login code', 'anar-toolkit' ),
'password_reset' => __( 'Password recovery', 'anar-toolkit' ),
'paid_customer' => __( 'Payment confirmation', 'anar-toolkit' ),
'paid_admin' => __( 'Paid order to administrators', 'anar-toolkit' ),
'new_admin' => __( 'New order to administrators', 'anar-toolkit' ),
'failed_admin' => __( 'Payment failure alert', 'anar-toolkit' ),
'shipment_customer' => __( 'Shipment tracking', 'anar-toolkit' ),
'status_processing' => __( 'Order processing', 'anar-toolkit' ),
'status_completed' => __( 'Order completed', 'anar-toolkit' ),
'status_on-hold' => __( 'Order on hold', 'anar-toolkit' ),
'status_cancelled' => __( 'Order cancelled', 'anar-toolkit' ),
'status_refunded' => __( 'Order refunded', 'anar-toolkit' ),
'recovery_1' => __( 'Recovery · first reminder', 'anar-toolkit' ),
'recovery_2' => __( 'Recovery · second reminder', 'anar-toolkit' ),
'recovery_3' => __( 'Recovery · third reminder', 'anar-toolkit' ),
'recovery_request' => __( 'Requested recovery code', 'anar-toolkit' ),
);
foreach ( array_keys( ( new \Anar\Login\Marketing\Settings() )->defaults()['rules'] ) as $event ) {
$labels[ 'mkt_' . $event ] = \Anar\Login\Marketing\Engine::label( $event ); }
return $labels;
}
public static function states() {
return array( 'waiting' => __( 'Waiting for stock', 'anar-toolkit' ), 'queued' => __( 'Queued', 'anar-toolkit' ), 'notified' => __( 'Notified', 'anar-toolkit' ), 'rewarded' => __( 'Reward issued', 'anar-toolkit' ), 'finished' => __( 'Completed', 'anar-toolkit' ), 'started' => __( 'Started', 'anar-toolkit' ), 'paused' => __( 'Paused', 'anar-toolkit' ), 'draft' => __( 'Draft', 'anar-toolkit' ), 'active' => __( 'Open', 'anar-toolkit' ), 'converted' => __( 'Purchased', 'anar-toolkit' ), 'suppressed' => __( 'Stopped', 'anar-toolkit' ), 'expired' => __( 'Expired', 'anar-toolkit' ), 'pending' => __( 'Queued', 'anar-toolkit' ), 'running' => __( 'Sending', 'anar-toolkit' ), 'accepted' => __( 'Accepted by provider', 'anar-toolkit' ), 'failed' => __( 'Failed', 'anar-toolkit' ), 'uncertain' => __( 'Needs investigation', 'anar-toolkit' ), 'cancelled' => __( 'Cancelled', 'anar-toolkit' ) );
}
public static function badge( $state ) {
$states = self::states();
return '' . esc_html( $states[ $state ] ?? $state ) . '';
}
public function data( $days = 30, $currency = '' ) {
global $wpdb;
$days = in_array( (int) $days, array( 7, 30, 90 ), true ) ? (int) $days : 30;
$since = ( new \DateTimeImmutable( 'today', wp_timezone() ) )->modify( '-' . ( $days - 1 ) . ' days' )->getTimestamp();
$carts = $this->repository->table( 'carts' );
$messages = $this->repository->table( 'messages' );
$currencies = $wpdb->get_col( $wpdb->prepare( 'SELECT DISTINCT currency FROM %i ORDER BY currency LIMIT 30', $carts ) );
$default = function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : 'IRR';
$currency = in_array( $currency, $currencies, true ) || $currency === $default ? $currency : $default;
$counts = $wpdb->get_row( $wpdb->prepare( "SELECT COUNT(*) saved, SUM(CASE WHEN status = 'active' AND updated_at < %d THEN 1 ELSE 0 END) abandoned, SUM(CASE WHEN clicked_at >= %d THEN 1 ELSE 0 END) visits FROM %i WHERE created_at >= %d AND currency = %s", time() - 30 * MINUTE_IN_SECONDS, $since, $carts, $since, $currency ) );
$revenue = $wpdb->get_row( $wpdb->prepare( "SELECT COUNT(*) recovered, COALESCE(SUM(amount),0) revenue FROM (SELECT recovered_order, MAX(recovered_total) amount FROM %i WHERE status = 'converted' AND recovered_order > 0 AND converted_at >= %d AND currency = %s GROUP BY recovered_order) recovered_orders", $carts, $since, $currency ) );
$queue = $wpdb->get_results( $wpdb->prepare( 'SELECT status, COUNT(*) count FROM %i WHERE created_at >= %d GROUP BY status', $messages, $since ), OBJECT_K );
$chart = array();
$today = new \DateTimeImmutable( 'today', wp_timezone() );
for ( $index = min( 14, $days ) - 1; $index >= 0; --$index ) {
$start = $today->modify( '-' . $index . ' days' );
$end = $start->modify( '+1 day' );
$row = $wpdb->get_row( $wpdb->prepare( "SELECT COUNT(*) saved, SUM(CASE WHEN status = 'converted' AND recovered_order > 0 THEN 1 ELSE 0 END) recovered FROM %i WHERE created_at >= %d AND created_at < %d AND currency = %s", $carts, $start->getTimestamp(), $end->getTimestamp(), $currency ) );
$chart[] = array( 'date' => wp_date( 'm/d', $start->getTimestamp() ), 'saved' => (int) $row->saved, 'recovered' => (int) $row->recovered );
}
return compact( 'days', 'since', 'currency', 'currencies', 'counts', 'revenue', 'queue', 'chart' );
}
public function render() {
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only, administrator-only report filters.
$days = isset( $_GET['period'] ) ? absint( $_GET['period'] ) : 30;
$currency = isset( $_GET['currency'] ) && is_string( $_GET['currency'] ) ? sanitize_text_field( wp_unslash( $_GET['currency'] ) ) : '';
$page = max( 1, min( 10000, isset( $_GET['report_page'] ) ? absint( $_GET['report_page'] ) : 1 ) );
$filter = isset( $_GET['state'] ) && is_string( $_GET['state'] ) ? sanitize_key( wp_unslash( $_GET['state'] ) ) : '';
// phpcs:enable WordPress.Security.NonceVerification.Recommended
$tab = \Anar\Login\Infrastructure\Features::current_tab();
$days = in_array( $days, array( 7, 30, 90 ), true ) ? $days : 30;
$data = 'overview' === $tab ? $this->data( $days, $currency ) : array( 'days' => $days, 'since' => time() - $days * DAY_IN_SECONDS, 'currency' => $currency ?: ( function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : 'IRR' ) );
$labels = self::labels();
$states = self::states();
$accepted = (int) ( $data['queue']['accepted']->count ?? 0 );
$pending = (int) ( $data['queue']['pending']->count ?? 0 );
$failed = (int) ( $data['queue']['failed']->count ?? 0 ) + (int) ( $data['queue']['uncertain']->count ?? 0 );
?>
ANAR TOOLKIT v; ?>)
| id : ( $labels[ $row->event ] ?? $row->event ) ); ?> coupon_id && class_exists( 'WC_Coupon' ) ) : ?> coupon_id ) )->get_code() ); ?> | phone ?? '' ) : $row->recipient ) ); ?> | status ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- badge() escapes all values. ?> |
total, 0 ) . ' ' . $row->currency ); ?>
order_id && function_exists( 'wc_get_order' ) && wc_get_order( $row->order_id ) ) :
?>
#order_id ); ?>
provider ?: '—' ); ?>error_code ); ?> | updated_at ) ); ?> | object_id > 0 && 'failed' === $row->status && $row->attempts < 3 && 'login_otp' !== $row->event && 'password_reset' !== $row->event ) : ?> — |