report = (string) $report_id; $this->priority = (int) $priority; } public function with_views( array $aliases ) { $this->views = array(); foreach ( $aliases as $alias_id => $alias ) { $preset = isset( $alias['preset'] ) && is_array( $alias['preset'] ) ? $alias['preset'] : array(); if ( array() === $preset ) { continue; } $key = sanitize_key( implode( '_', array_map( 'strval', array_values( $preset ) ) ) ); if ( '' === $key || false !== strpos( $key, '__' ) || isset( $this->views[ $key ] ) ) { continue; } $this->views[ $key ] = array( 'label' => isset( $alias['label'] ) ? (string) $alias['label'] : '', 'table' => isset( $alias['table_id'] ) ? (int) $alias['table_id'] : 0, 'preset' => $preset, 'alias' => (string) $alias_id, 'icon' => self::view_icon( $preset ), ); } return $this; } public function views() { if ( null === $this->views ) { $mine = array(); if ( class_exists( 'AIEO_Table_Registry' ) ) { foreach ( AIEO_Table_Registry::superseded_aliases() as $alias_id => $alias ) { if ( (string) $alias['definition'] === $this->report ) { $mine[ (string) $alias_id ] = $alias; } } } $this->with_views( $mine ); $this->own_views(); } return $this->views; } private function own_views() { $def = class_exists( 'AIEO_Report_Registry' ) ? AIEO_Report_Registry::get( $this->report ) : null; if ( ! $def ) { return; } foreach ( (array) $def->get( 'views', array() ) as $key => $view ) { $key = (string) $key; if ( isset( $this->views[ $key ] ) ) { if ( '' !== (string) ( $view['label'] ?? '' ) ) { $this->views[ $key ]['label'] = (string) $view['label']; } continue; } if ( empty( $view['preset'] ) || ! is_array( $view['preset'] ) ) { continue; } $this->views[ $key ] = array( 'label' => (string) ( $view['label'] ?? $key ), 'table' => 0, 'preset' => $view['preset'], 'alias' => '', 'icon' => (string) ( $view['icon'] ?? '' ), ); } } public function alias_ids() { $out = array(); foreach ( $this->views() as $view ) { if ( '' !== $view['alias'] ) { $out[] = $view['alias']; } } if ( class_exists( 'AIEO_Table_Registry' ) ) { foreach ( AIEO_Table_Registry::superseded_vendor_tables() as $tid => $def ) { if ( (string) $def === $this->report ) { $out[] = 'insights_t' . (int) $tid; } } } return array_values( array_unique( $out ) ); } private static function view_icon( array $preset ) { $icons = array( 'cart_position=only' => 'package', 'cart_position=first' => 'door-open', 'cart_position=last' => 'circle-check', 'month_slot=current' => 'calendar-days', 'month_slot=previous' => 'calendar-arrow-down', 'month_slot=historical' => 'calendar-range', 'stock_state=out_of_stock' => 'boxes', 'stock_state=not_set' => 'circle-question-mark', 'cost_known=predicted' => 'banknote', ); foreach ( $preset as $k => $v ) { if ( isset( $icons[ $k . '=' . $v ] ) ) { return $icons[ $k . '=' . $v ]; } } return ''; } public function get_tabs() { if ( array() === $this->views() ) { return array(); } $tabs = array( 'all' => array( 'label' => __( 'All', 'ai-eshop-optimizer' ), 'icon' => 'list' ), ); foreach ( $this->views() as $key => $view ) { $tabs[ $key ] = array( 'label' => $this->view_label( $view ), 'icon' => $view['icon'] ); } return $tabs; } public function get_tab_arg() { return 'rv'; } public function board_key() { return (string) $this->get_id(); } private function view_label( array $view ) { if ( '' !== $view['label'] ) { return __( $view['label'], 'ai-eshop-optimizer' ); } $curated = self::vendor_labels(); return isset( $curated[ $view['table'] ] ) ? (string) $curated[ $view['table'] ] : $view['alias']; } private function requested_view() { if ( array() === $this->views() ) { return ''; } $rv = isset( $_GET['rv'] ) ? sanitize_key( wp_unslash( $_GET['rv'] ) ) : ''; if ( '' !== $rv ) { return isset( $this->views()[ $rv ] ) ? $rv : ''; } $tab = isset( $_GET['dmm_tab'] ) ? sanitize_key( wp_unslash( $_GET['dmm_tab'] ) ) : ''; foreach ( $this->views() as $key => $view ) { if ( '' !== $tab && $tab === $view['alias'] ) { return $key; } } return ''; } public function get_id() { return $this->report; } public function get_priority() { return $this->priority; } public function report_id() { return $this->report; } public function pro_question_id() { return (string) $this->report; } protected function render_pro_locked_screen() { if ( class_exists( 'AIEO_Report_Demo' ) && AIEO_Report_Demo::enabled() ) { $snap = AIEO_Report_Demo::snapshot_for( $this->get_id() ); foreach ( is_array( $snap ) ? array() : $this->alias_ids() as $alias_id ) { $snap = AIEO_Report_Demo::snapshot_for( $alias_id ); if ( is_array( $snap ) ) { break; } } if ( is_array( $snap ) && AIEO_Report_Demo::render_demo_card( $this, $snap ) ) { return; } } parent::render_pro_locked_screen(); if ( class_exists( 'AIEO_Report_Demo' ) && ! AIEO_Report_Demo::enabled() ) { AIEO_Report_Demo::render_optin_strip(); } } public static function vendor_labels() { return array( 18 => __( 'Cost of Stock-Outs', 'ai-eshop-optimizer' ), 20 => __( 'Out of Stock Products', 'ai-eshop-optimizer' ), 27 => __( 'Out of Stock Rate by Period', 'ai-eshop-optimizer' ), 26 => __( 'Out of Stock Variations Ratio', 'ai-eshop-optimizer' ), 14 => __( 'Stock Fast Finder', 'ai-eshop-optimizer' ), 15 => __( 'Stock Not Set — Fast Finder', 'ai-eshop-optimizer' ), 8 => __( 'Bought on Its Own', 'ai-eshop-optimizer' ), 6 => __( 'First Item in the Basket', 'ai-eshop-optimizer' ), 7 => __( 'Last Item Before Checkout', 'ai-eshop-optimizer' ), 43 => __( 'Google Analytics — Product Funnel', 'ai-eshop-optimizer' ), 32 => __( 'Unit Sales — Online vs Branches', 'ai-eshop-optimizer' ), 33 => __( 'ERP Raw Order Data', 'ai-eshop-optimizer' ), 5 => __( 'All Products in Carts', 'ai-eshop-optimizer' ), 3 => __( 'Order Patterns', 'ai-eshop-optimizer' ), 4 => __( 'Customer Order Averages', 'ai-eshop-optimizer' ), 13 => __( 'Post-Order Survey Results', 'ai-eshop-optimizer' ), 41 => __( 'Woo vs ERP Turnover — Current Month', 'ai-eshop-optimizer' ), 40 => __( 'Woo vs ERP Turnover — Previous Month', 'ai-eshop-optimizer' ), 42 => __( 'Woo vs ERP Turnover — Historical', 'ai-eshop-optimizer' ), 35 => __( 'Reorder Summary', 'ai-eshop-optimizer' ), 34 => __( 'Reorders — Keratsini', 'ai-eshop-optimizer' ), 28 => __( 'Needs coverage', 'ai-eshop-optimizer' ), ); } public function get_label() { $def = AIEO_Report_Registry::get( $this->report ); if ( ! $def ) { return $this->report; } $label = (string) $def->get( 'label', '' ); if ( '' === $label ) { return $this->report; } return __( $label, 'ai-eshop-optimizer' ); } public function get_group() { $def = AIEO_Report_Registry::get( $this->report ); $g = $def ? (string) $def->get( 'group', '' ) : ''; return '' !== $g ? $g : 'eshop_insights'; } public function get_section() { $def = AIEO_Report_Registry::get( $this->report ); return $def ? (string) $def->get( 'section', '' ) : ''; } public function get_icon() { $def = AIEO_Report_Registry::get( $this->report ); return $def ? (string) $def->get( 'icon', '' ) : ''; } public function is_pro_locked() { return 'pro' === AIEO_Report_Registry::tier( $this->report ); } public function get_pro_promo() { $def = AIEO_Report_Registry::get( $this->report ); if ( ! $def ) { return array(); } $description = (string) $def->get( 'description', '' ); return array( 'title' => $this->get_label(), 'description' => '' !== $description ? __( $description, 'ai-eshop-optimizer' ) : '', ); } public function render_subtab() { $def = AIEO_Report_Registry::get( $this->report ); if ( ! $def ) { return; } if ( ! $def->user_can( 'view' ) ) { return; } $request = self::request_from_url(); $view = $this->requested_view(); if ( '' !== $view ) { $asked = isset( $request['params'] ) ? (array) $request['params'] : array(); $request['params'] = array_merge( $this->views()[ $view ]['preset'], $asked ); } if ( AIEO_Report_Render::library_on( $def ) ) { $request = AIEO_Report_Render::view_request( $def, $request ); } $envelope = AIEO_Report_Render::run( $this->report, $request ); if ( class_exists( 'AIEO_Dark_Screens' ) ) { AIEO_Dark_Screens::observe( $this->get_id(), (array) $envelope, (array) $request ); } if ( class_exists( 'AIEO_Remembered_Figures' ) && ( ! $this->is_pro_locked() || self::is_aieo_pro_user() ) ) { AIEO_Remembered_Figures::observe( $this->get_id(), (string) $this->report, (array) $envelope, (array) $request ); } if ( array() !== $this->views() ) { AIEO_DMM_UI::tab_strip( $this->get_id(), $this->get_tabs(), '' !== $view ? $view : 'all', array( 'arg' => $this->get_tab_arg(), 'wrapper' => 'div' ) ); } $footnote = (string) $def->get( 'footnote', '' ); $scope = array_intersect_key( isset( $request['params'] ) ? (array) $request['params'] : array(), (array) $def->get( 'params', array() ) ); echo AIEO_Report_Render::summary_html( $def, ( '' !== $view ? $this->views()[ $view ]['preset'] : array() ) + $scope, self::anchor_for( $this->get_id() ) . '-summary' ); $shell = AIEO_Table::card_shell( array( 'anchor' => self::anchor_for( $this->get_id() ), 'title' => __( 'Results', 'ai-eshop-optimizer' ), 'description' => self::card_description( $this->get_id(), $def ), 'footnote' => '' !== $footnote ? __( $footnote, 'ai-eshop-optimizer' ) : '', 'flush' => true, ) ); echo $shell[0]; if ( isset( $envelope['error'] ) ) { echo AIEO_Report_Render::failed_html( __( 'This report could not be run. Its definition may be invalid.', 'ai-eshop-optimizer' ) ) . $shell[1]; return; } echo self::export_bar( $this->report, $request ); echo $envelope['html']; echo $shell[1]; } public static function card_description( $page_id, $def ) { $description = is_object( $def ) ? (string) $def->get( 'description', '' ) : ''; if ( '' === $description ) { return ''; } $description = __( $description, 'ai-eshop-optimizer' ); $head = class_exists( 'AIEO_Menu_Descriptions' ) ? (string) AIEO_Menu_Descriptions::for_subtab( (string) $page_id ) : ''; $plain = static function ( $s ) { return rtrim( trim( wp_strip_all_tags( (string) $s ) ), '.' ); }; if ( $plain( $description ) !== $plain( $head ) ) { return $description; } $own = self::results_sentences(); return isset( $own[ (string) $page_id ] ) ? $own[ (string) $page_id ] : ''; } private static function results_sentences() { return array( 'insights_basket_position' => __( 'Each row is a product with the number of orders where it held that position; filter by product, SKU or order count.', 'ai-eshop-optimizer' ), 'insights_cart_products' => __( 'Each row is one product in one order; filter by order, product, ID or SKU to find the lines you need.', 'ai-eshop-optimizer' ), 'insights_invoice_match' => __( 'Each row is one order beside its ERP document; filter by period, date or status to find the ones that disagree.', 'ai-eshop-optimizer' ), 'insights_order_patterns' => __( 'Each row is one order; filter by date or city and sort by value to see who buys what, and through which channel.', 'ai-eshop-optimizer' ), 'insights_out_of_stock' => __( 'Each row is a product that cannot be sold; sort by lost sales or units to reorder to decide what to restock first.', 'ai-eshop-optimizer' ), 'insights_stock_check' => __( 'Each row is a product with no stock to sell; filter by product, SKU, stock or state, then fix its stock figure.', 'ai-eshop-optimizer' ), ); } private static function export_bar( $report_id, $request ) { return AIEO_Report_Render::export_bar( $report_id, $request ); } private static function request_from_url() { return AIEO_Report_Render::request_from_url( '' ); } public static function anchor_for( $report_id ) { return trim( preg_replace( '/-+/', '-', str_replace( '_', '-', strtolower( (string) $report_id ) ) ), '-' ); } public static function page_anchors() { if ( ! class_exists( 'AIEO_DMM' ) ) { return array(); } $dmm = AIEO_DMM::instance(); if ( ! is_object( $dmm ) || ! method_exists( $dmm, 'get_modules' ) ) { return array(); } $out = array(); foreach ( (array) $dmm->get_modules() as $module ) { if ( ! $module instanceof self ) { continue; } $in_nav = ! method_exists( $module, 'appears_in_nav' ) || $module->appears_in_nav(); $out[ self::anchor_for( $module->get_id() ) ] = (bool) $in_nav; } return $out; } public static function register_all( $dmm ) { if ( ! is_object( $dmm ) || ! method_exists( $dmm, 'register_module' ) ) { return; } if ( class_exists( 'AIEO_Engine_Blocks', false ) && AIEO_Engine_Blocks::none_rendered() ) { return; } $priority = 0; foreach ( AIEO_Report_Registry::all() as $id => $def ) { if ( '' === (string) $def->get( 'group', '' ) ) { continue; } $priority += 10; $menu_priority = (int) $def->get( 'menu_priority', 0 ); $page = new self( $id, $menu_priority > 0 ? $menu_priority : $priority ); $dmm->register_module( $page ); } } } add_action( 'aieo_dmm_register_deferred_modules', array( 'AIEO_Report_Page', 'register_all' ) );